cd-aichat 1.0.0
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 +188 -0
- package/dist/ailogo.png +0 -0
- package/dist/cd-aichat.css +1 -0
- package/dist/cd-aichat.es.js +32223 -0
- package/dist/cd-aichat.umd.js +279 -0
- package/dist/index.css +1 -0
- package/package.json +61 -0
- package/src/ailogo.png +0 -0
- package/src/components/AiChat.test.js +538 -0
- package/src/components/AiChat.vue +2206 -0
- package/src/components/AiChatWidget.test.js +312 -0
- package/src/components/AiChatWidget.vue +963 -0
- package/src/components/BurnAfterReadDialog.test.js +121 -0
- package/src/components/BurnAfterReadDialog.vue +511 -0
- package/src/components/BurnAfterReadMessage.test.js +188 -0
- package/src/components/BurnAfterReadMessage.vue +193 -0
- package/src/components/BurnIndicator.test.js +101 -0
- package/src/components/BurnIndicator.vue +164 -0
- package/src/components/ChatWindow.vue +0 -0
- package/src/components/MentionList.vue +163 -0
- package/src/components/ResourceList.vue +194 -0
- package/src/components/RichTextEditor.vue +437 -0
- package/src/components/ScheduledSendDialog.vue +476 -0
- package/src/components/ScreenshotOverlay.vue +78 -0
- package/src/components/SendButtonGroup.test.js +174 -0
- package/src/components/SendButtonGroup.vue +166 -0
- package/src/components/UserDrawer.vue +0 -0
- package/src/components/screenshot/ScreenshotsBackground/getBoundsByPoints.ts +41 -0
- package/src/components/screenshot/ScreenshotsBackground/index.scss +27 -0
- package/src/components/screenshot/ScreenshotsBackground/index.tsx +145 -0
- package/src/components/screenshot/ScreenshotsButton/index.scss +29 -0
- package/src/components/screenshot/ScreenshotsButton/index.tsx +58 -0
- package/src/components/screenshot/ScreenshotsCanvas/getBoundsByPoints.ts +55 -0
- package/src/components/screenshot/ScreenshotsCanvas/getPoints.ts +60 -0
- package/src/components/screenshot/ScreenshotsCanvas/index.scss +84 -0
- package/src/components/screenshot/ScreenshotsCanvas/index.tsx +277 -0
- package/src/components/screenshot/ScreenshotsCanvas/isPointInDraw.ts +35 -0
- package/src/components/screenshot/ScreenshotsColor/index.scss +45 -0
- package/src/components/screenshot/ScreenshotsColor/index.tsx +39 -0
- package/src/components/screenshot/ScreenshotsContext.ts +56 -0
- package/src/components/screenshot/ScreenshotsMagnifier/index.scss +61 -0
- package/src/components/screenshot/ScreenshotsMagnifier/index.tsx +126 -0
- package/src/components/screenshot/ScreenshotsOperations/index.scss +25 -0
- package/src/components/screenshot/ScreenshotsOperations/index.tsx +118 -0
- package/src/components/screenshot/ScreenshotsOption/index.scss +50 -0
- package/src/components/screenshot/ScreenshotsOption/index.tsx +150 -0
- package/src/components/screenshot/ScreenshotsSize/index.scss +28 -0
- package/src/components/screenshot/ScreenshotsSize/index.tsx +41 -0
- package/src/components/screenshot/ScreenshotsSizeColor/index.scss +8 -0
- package/src/components/screenshot/ScreenshotsSizeColor/index.tsx +25 -0
- package/src/components/screenshot/ScreenshotsTextarea/calculateNodeSize.ts +117 -0
- package/src/components/screenshot/ScreenshotsTextarea/index.scss +19 -0
- package/src/components/screenshot/ScreenshotsTextarea/index.tsx +96 -0
- package/src/components/screenshot/composeImage.ts +57 -0
- package/src/components/screenshot/exports.ts +4 -0
- package/src/components/screenshot/hooks/useBounds.ts +35 -0
- package/src/components/screenshot/hooks/useCall.ts +17 -0
- package/src/components/screenshot/hooks/useCanvasContextRef.ts +8 -0
- package/src/components/screenshot/hooks/useCanvasMousedown.ts +13 -0
- package/src/components/screenshot/hooks/useCanvasMousemove.ts +13 -0
- package/src/components/screenshot/hooks/useCanvasMouseup.ts +13 -0
- package/src/components/screenshot/hooks/useCursor.ts +34 -0
- package/src/components/screenshot/hooks/useDispatcher.ts +8 -0
- package/src/components/screenshot/hooks/useDrawSelect.ts +16 -0
- package/src/components/screenshot/hooks/useEmiter.ts +61 -0
- package/src/components/screenshot/hooks/useHistory.ts +160 -0
- package/src/components/screenshot/hooks/useLang.ts +8 -0
- package/src/components/screenshot/hooks/useOperation.ts +37 -0
- package/src/components/screenshot/hooks/useReset.ts +26 -0
- package/src/components/screenshot/hooks/useStore.ts +8 -0
- package/src/components/screenshot/icons/iconfont.scss +88 -0
- package/src/components/screenshot/icons/iconfont.ttf +0 -0
- package/src/components/screenshot/icons/iconfont.woff +0 -0
- package/src/components/screenshot/icons/iconfont.woff2 +0 -0
- package/src/components/screenshot/index.tsx +169 -0
- package/src/components/screenshot/operations/Arrow/draw.ts +56 -0
- package/src/components/screenshot/operations/Arrow/index.tsx +193 -0
- package/src/components/screenshot/operations/Brush/draw.ts +45 -0
- package/src/components/screenshot/operations/Brush/index.tsx +169 -0
- package/src/components/screenshot/operations/Cancel/index.tsx +18 -0
- package/src/components/screenshot/operations/Ellipse/draw.ts +96 -0
- package/src/components/screenshot/operations/Ellipse/index.tsx +245 -0
- package/src/components/screenshot/operations/Mosaic/index.tsx +223 -0
- package/src/components/screenshot/operations/Ok/index.tsx +37 -0
- package/src/components/screenshot/operations/Pin/index.tsx +37 -0
- package/src/components/screenshot/operations/Rectangle/draw.ts +80 -0
- package/src/components/screenshot/operations/Rectangle/index.tsx +245 -0
- package/src/components/screenshot/operations/Redo/index.tsx +22 -0
- package/src/components/screenshot/operations/Save/index.tsx +37 -0
- package/src/components/screenshot/operations/Scan/index.tsx +46 -0
- package/src/components/screenshot/operations/Search/index.tsx +39 -0
- package/src/components/screenshot/operations/Text/index.tsx +307 -0
- package/src/components/screenshot/operations/Undo/index.tsx +22 -0
- package/src/components/screenshot/operations/index.ts +34 -0
- package/src/components/screenshot/operations/utils.ts +34 -0
- package/src/components/screenshot/screenshots.scss +13 -0
- package/src/components/screenshot/types.ts +53 -0
- package/src/components/screenshot/useGetLoadedImage.ts +29 -0
- package/src/components/screenshot/var.scss +107 -0
- package/src/components/screenshot/zh_CN.ts +37 -0
- package/src/emoji/100.gif +0 -0
- package/src/emoji/101.gif +0 -0
- package/src/emoji/102.gif +0 -0
- package/src/emoji/103.gif +0 -0
- package/src/emoji/104.gif +0 -0
- package/src/emoji/105.gif +0 -0
- package/src/emoji/106.gif +0 -0
- package/src/emoji/107.gif +0 -0
- package/src/emoji/108.gif +0 -0
- package/src/emoji/109.gif +0 -0
- package/src/emoji/110.gif +0 -0
- package/src/emoji/111.gif +0 -0
- package/src/emoji/112.gif +0 -0
- package/src/emoji/113.gif +0 -0
- package/src/emoji/114.gif +0 -0
- package/src/emoji/115.gif +0 -0
- package/src/emoji/116.gif +0 -0
- package/src/emoji/117.gif +0 -0
- package/src/emoji/118.gif +0 -0
- package/src/emoji/119.gif +0 -0
- package/src/emoji/120.gif +0 -0
- package/src/emoji/121.gif +0 -0
- package/src/emoji/122.gif +0 -0
- package/src/emoji/123.gif +0 -0
- package/src/emoji/124.gif +0 -0
- package/src/emoji/125.gif +0 -0
- package/src/emoji/126.gif +0 -0
- package/src/emoji/127.gif +0 -0
- package/src/emoji/128.gif +0 -0
- package/src/emoji/129.gif +0 -0
- package/src/emoji/130.gif +0 -0
- package/src/emoji/131.gif +0 -0
- package/src/emoji/132.gif +0 -0
- package/src/emoji/133.gif +0 -0
- package/src/emoji/134.gif +0 -0
- package/src/emoji/135.gif +0 -0
- package/src/emoji/136.gif +0 -0
- package/src/emoji/137.gif +0 -0
- package/src/emoji/138.gif +0 -0
- package/src/emoji/139.gif +0 -0
- package/src/emoji/140.gif +0 -0
- package/src/emoji/141.gif +0 -0
- package/src/emoji/142.gif +0 -0
- package/src/emoji/143.gif +0 -0
- package/src/emoji/144.gif +0 -0
- package/src/emoji/145.gif +0 -0
- package/src/emoji/146.gif +0 -0
- package/src/emoji/147.gif +0 -0
- package/src/emoji/148.gif +0 -0
- package/src/emoji/149.gif +0 -0
- package/src/emoji/150.gif +0 -0
- package/src/emoji/151.gif +0 -0
- package/src/emoji/152.gif +0 -0
- package/src/emoji/153.gif +0 -0
- package/src/emoji/154.gif +0 -0
- package/src/emoji/155.gif +0 -0
- package/src/emoji/156.gif +0 -0
- package/src/emoji/157.gif +0 -0
- package/src/emoji/158.gif +0 -0
- package/src/emoji/159.gif +0 -0
- package/src/emoji/160.gif +0 -0
- package/src/emoji/161.gif +0 -0
- package/src/emoji/162.gif +0 -0
- package/src/emoji/163.gif +0 -0
- package/src/emoji/164.gif +0 -0
- package/src/emoji/165.gif +0 -0
- package/src/emoji/166.gif +0 -0
- package/src/emoji/167.gif +0 -0
- package/src/emoji/168.gif +0 -0
- package/src/emoji/169.gif +0 -0
- package/src/emoji/170.gif +0 -0
- package/src/emoji/171.gif +0 -0
- package/src/emoji/172.gif +0 -0
- package/src/emoji/173.gif +0 -0
- package/src/emoji/174.gif +0 -0
- package/src/emoji/175.gif +0 -0
- package/src/emoji/176.gif +0 -0
- package/src/emoji/177.gif +0 -0
- package/src/emoji/178.gif +0 -0
- package/src/emoji/179.gif +0 -0
- package/src/emoji/180.gif +0 -0
- package/src/emoji/181.gif +0 -0
- package/src/emoji/182.gif +0 -0
- package/src/emoji/183.gif +0 -0
- package/src/emoji/184.gif +0 -0
- package/src/emoji/185.gif +0 -0
- package/src/emoji/186.gif +0 -0
- package/src/emoji/187.gif +0 -0
- package/src/emoji/188.gif +0 -0
- package/src/emoji/189.gif +0 -0
- package/src/emoji/190.gif +0 -0
- package/src/emoji/191.gif +0 -0
- package/src/emoji/192.gif +0 -0
- package/src/emoji/193.gif +0 -0
- package/src/emoji/194.gif +0 -0
- package/src/emoji/195.gif +0 -0
- package/src/emoji/196.gif +0 -0
- package/src/emoji/197.gif +0 -0
- package/src/emoji/198.gif +0 -0
- package/src/emoji/199.gif +0 -0
- package/src/emoji/200.png +0 -0
- package/src/emoji/201.png +0 -0
- package/src/emoji/202.png +0 -0
- package/src/emoji/203.png +0 -0
- package/src/emoji/204.png +0 -0
- package/src/emoji/205.png +0 -0
- package/src/emoji/206.png +0 -0
- package/src/emoji/207.png +0 -0
- package/src/emoji/208.png +0 -0
- package/src/emoji/209.png +0 -0
- package/src/emoji/210.png +0 -0
- package/src/emoji/211.png +0 -0
- package/src/emoji/212.png +0 -0
- package/src/emoji/213.png +0 -0
- package/src/emoji/214.png +0 -0
- package/src/emoji/215.png +0 -0
- package/src/emoji/216.png +0 -0
- package/src/emoji/217.png +0 -0
- package/src/emoji/218.png +0 -0
- package/src/emoji/219.png +0 -0
- package/src/emoji2/101--Streamline-The-Team.png +0 -0
- package/src/emoji2/128--Streamline-The-Team.png +0 -0
- package/src/emoji2/134--Streamline-The-Team.png +0 -0
- package/src/emoji2/173--Streamline-The-Team.png +0 -0
- package/src/emoji2/Airplane--Streamline-Emoji.svg +24 -0
- package/src/emoji2/Alien--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Amazed-Face--Streamline-Emoji.svg +16 -0
- package/src/emoji2/Amusing-Face--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Anguished-Face--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Anxious-Face--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Astonished-Face--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Backhand-Index-Pointing-Down-1--Streamline-Emoji.svg +12 -0
- package/src/emoji2/Backhand-Index-Pointing-Left-1--Streamline-Emoji.svg +13 -0
- package/src/emoji2/Backhand-Index-Pointing-Right-1--Streamline-Emoji.svg +13 -0
- package/src/emoji2/Backhand-Index-Pointing-Up-1--Streamline-Emoji.svg +14 -0
- package/src/emoji2/Bar-Chart--Streamline-Emoji.svg +22 -0
- package/src/emoji2/Beaming-Face-With-Smiling-Eyes--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Boy-1--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Boy-2--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Boy-3--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Broken-Heart--Streamline-Emoji.svg +12 -0
- package/src/emoji2/Clapping-Hands-1--Streamline-Emoji.svg +23 -0
- package/src/emoji2/Clinking-Glasses-2--Streamline-Emoji.svg +43 -0
- package/src/emoji2/Confounded-Face--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Confused-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Construction-Worker--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Couple-With-Heart-Woman-Man-1--Streamline-Emoji.svg +40 -0
- package/src/emoji2/Couple-With-Heart-Woman-Man-2--Streamline-Emoji.svg +40 -0
- package/src/emoji2/Cowboy-Hat-Face--Streamline-Emoji.svg +22 -0
- package/src/emoji2/Crazy-Face--Streamline-Emoji.svg +25 -0
- package/src/emoji2/Crossed-Fingers-1--Streamline-Emoji.svg +25 -0
- package/src/emoji2/Crown--Streamline-Emoji.svg +35 -0
- package/src/emoji2/Crying-Face--Streamline-Emoji.svg +26 -0
- package/src/emoji2/Delivery-Truck--Streamline-Emoji.svg +31 -0
- package/src/emoji2/Determined-Face--Streamline-Emoji.svg +25 -0
- package/src/emoji2/Disappointed-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Dizzy-Face--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Downcast-Face-With-Sweat--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Drooling-Face-1--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Drooling-Face-2--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Ear--Streamline-Emoji.svg +14 -0
- package/src/emoji2/Exclamation-Mark--Streamline-Emoji.svg +12 -0
- package/src/emoji2/Exploding-Head--Streamline-Emoji.svg +24 -0
- package/src/emoji2/Expressionless-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Face-Blowing-A-Kiss--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Face-Savoring-Food--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Face-Screaming-In-Fear--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Face-Vomiting--Streamline-Emoji.svg +28 -0
- package/src/emoji2/Face-With-Head-Bandage--Streamline-Emoji.svg +25 -0
- package/src/emoji2/Face-With-Medical-Mask--Streamline-Emoji.svg +23 -0
- package/src/emoji2/Face-With-Monocle--Streamline-Emoji.svg +26 -0
- package/src/emoji2/Face-With-Raised-Eyebrow--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Face-With-Rolling-Eyes--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Face-With-Steam-From-Nose--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Face-With-Symbols-On-Mouth--Streamline-Emoji.svg +22 -0
- package/src/emoji2/Face-With-Tears-Of-Joy--Streamline-Emoji.svg +34 -0
- package/src/emoji2/Face-With-Thermometer--Streamline-Emoji.svg +31 -0
- package/src/emoji2/Face-With-Tongue--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Face-Without-Mouth--Streamline-Emoji.svg +14 -0
- package/src/emoji2/Fearful-Face--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Flexed-Biceps-1--Streamline-Emoji.svg +13 -0
- package/src/emoji2/Flushed-Face--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Folded-Hands-1--Streamline-Emoji.svg +29 -0
- package/src/emoji2/Frowning-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Fuel-Pump--Streamline-Emoji.svg +30 -0
- package/src/emoji2/Girl-1--Streamline-Emoji.svg +23 -0
- package/src/emoji2/Glasses-1--Streamline-Emoji.svg +27 -0
- package/src/emoji2/Grimacing-Face--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Grinning-Cat-Face--Streamline-Emoji.svg +32 -0
- package/src/emoji2/Grinning-Face--Streamline-Emoji.svg +16 -0
- package/src/emoji2/Grinning-Face-With-Sweat--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Grinning-Squinting-Face--Streamline-Emoji.svg +16 -0
- package/src/emoji2/Hand-With-Fingers-Splayed-1--Streamline-Emoji.svg +14 -0
- package/src/emoji2/Heart-Suit--Streamline-Emoji.svg +9 -0
- package/src/emoji2/Hushed-Face-1--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Hushed-Face-2--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Index-Pointing-Up-1--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Kissing-Face-With-Closed-Eyes--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Loudly-Crying-Face--Streamline-Emoji.svg +16 -0
- package/src/emoji2/Lying-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Man-1--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Man-Facepalming-1--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Man-Gesturing-No-1--Streamline-Emoji.svg +34 -0
- package/src/emoji2/Man-Gesturing-Ok-1--Streamline-Emoji.svg +25 -0
- package/src/emoji2/Man-Health-Worker-1--Streamline-Emoji.svg +41 -0
- package/src/emoji2/Man-Raising-Hand-1--Streamline-Emoji.svg +26 -0
- package/src/emoji2/Man-Shrugging-1--Streamline-Emoji.svg +31 -0
- package/src/emoji2/Money-Mouth-Face-2--Streamline-Emoji.svg +30 -0
- package/src/emoji2/Mouth--Streamline-Emoji.svg +12 -0
- package/src/emoji2/Nauseated-Face-2--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Neutral-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Ok-Hand-1--Streamline-Emoji.svg +14 -0
- package/src/emoji2/Old-Man-1--Streamline-Emoji.svg +27 -0
- package/src/emoji2/Old-Woman-1--Streamline-Emoji.svg +23 -0
- package/src/emoji2/Oncoming-Fist-1--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Person-Wearing-Turban-2--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Pile-Of-Poo--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Police-Car-Light--Streamline-Emoji.svg +26 -0
- package/src/emoji2/Rocket--Streamline-Emoji.svg +32 -0
- package/src/emoji2/Sailboat--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Shaved-Ice--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Shortcake-2--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Shushing-Face--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Sign-Of-The-Horns-1--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Sleeping-Face--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Slightly-Smiling-Face--Streamline-Emoji.svg +15 -0
- package/src/emoji2/Smiling-Face-With-Halo--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Smiling-Face-With-Heart-Eyes--Streamline-Emoji.svg +21 -0
- package/src/emoji2/Smirking-Face--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Sun-With-Face--Streamline-Emoji.svg +24 -0
- package/src/emoji2/Thumbs-Down-1--Streamline-Emoji.svg +20 -0
- package/src/emoji2/Thumbs-Up-1--Streamline-Emoji.svg +19 -0
- package/src/emoji2/Winking-Face--Streamline-Emoji.svg +18 -0
- package/src/emoji2/Woman-Gesturing-No-1--Streamline-Emoji.svg +34 -0
- package/src/emoji2/Woman-Gesturing-Ok-2--Streamline-Emoji.svg +25 -0
- package/src/emoji2/Woman-Raising-Hand-1--Streamline-Emoji.svg +26 -0
- package/src/emoji2/Womans-Sandal--Streamline-Emoji.svg +13 -0
- package/src/emoji2/Worried-Face--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Writing-Hand-1--Streamline-Emoji.svg +17 -0
- package/src/emoji2/Zipper-Mouth-Face--Streamline-Emoji.svg +21 -0
- package/src/index.js +19 -0
- package/src/services/burn-after-read-service.js +313 -0
- package/src/services/burn-after-read-service.test.js +325 -0
- package/src/services/dify-api.js +338 -0
- package/src/services/dify-api.test.js +376 -0
- package/src/services/scheduled-send-service.js +311 -0
- package/src/services/scheduled-send-service.test.js +317 -0
- package/src/styles/index.css +2368 -0
- package/src/utils/emoji.js +125 -0
- package/src/utils/emojiData.js +267 -0
- package/src/utils/eventEmitter.js +114 -0
- package/src/utils/state.js +224 -0
- package/src/utils/state.test.js +198 -0
- package/src/utils/storage.js +122 -0
- package/src/utils/storage.test.js +162 -0
- package/src/utils/validation.js +249 -0
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.im-container{display:flex;flex-direction:column;height:100%;background-color:#f5f5f5;font-family:PingFang SC,Microsoft YaHei,sans-serif;position:relative}.header{display:flex;align-items:center;padding:12px 16px;background-color:#fff;border-bottom:1px solid #e5e5e5;gap:12px}.header .ri-arrow-left-line{font-size:24px;cursor:pointer;color:#333;padding:4px}.header .ri-arrow-left-line:hover{color:#1890ff}.user-info{flex:1;display:flex;align-items:center;gap:12px;min-width:0}.user-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.user-avatar img{width:100%;height:100%;object-fit:cover}.user-details{flex:1;min-width:0}.user-name{font-size:16px;font-weight:600;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-account{font-size:12px;color:#999;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-status{font-size:12px;color:#999}.header-actions{display:flex;align-items:center;gap:12px}.header-more-icon,.header-close-icon{font-size:20px;cursor:pointer;color:#666;padding:4px;transition:color .2s}.header-more-icon:hover,.header-close-icon:hover{color:#1890ff}.main{flex:1;overflow:hidden;display:flex;flex-direction:column}.chatBox{flex:1;display:flex;flex-direction:column;overflow:hidden}.chatList{flex:none;overflow-y:auto;padding:16px;scroll-behavior:smooth;transition:height .1s ease;position:relative}.chatList::-webkit-scrollbar{width:6px}.chatList::-webkit-scrollbar-thumb{background-color:#0003;border-radius:3px}.chatList::-webkit-scrollbar-track{background-color:transparent}.chatList-item{margin-bottom:16px}.chatList-item--compact{margin-top:-8px}.chatList-time{text-align:center;font-size:12px;color:#999;margin-bottom:16px}.chatList-wrapper{display:flex;gap:12px;align-items:flex-start}.chatList-item--mine .chatList-wrapper{justify-content:flex-end}.chatList-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.chatList-avatar img{width:100%;height:100%;object-fit:cover}.chatList-avatar--placeholder{width:40px;height:40px;flex-shrink:0}.chatList-content{flex:0 1 auto;min-width:0;max-width:60%;display:flex;flex-direction:column}.chatList-item--mine .chatList-content{align-items:flex-end}.chatList-sender-name{font-size:12px;color:#666;margin-bottom:4px;padding-left:12px}.chatList-item--mine .chatList-sender-name{padding-left:0;padding-right:12px}.chatList-text{position:relative;padding:10px 14px;border-radius:8px;background-color:#fff;word-wrap:break-word;word-break:break-word;box-shadow:0 1px 2px #0000000d}.chatList-text--mine{background-color:#95ec69;color:#333}.chatList-text--ai{background-color:#fff;border:1px solid #e5e5e5}.chatList-text--group{background-color:#fff}.chatList-text--no-bg{background-color:transparent;box-shadow:none;padding:0;border-radius:0}.chatList-arrow{position:absolute;top:12px;width:0;height:0;border:6px solid transparent}.chatList-text .chatList-arrow{left:-12px;border-right-color:#fff}.chatList-text--mine .chatList-arrow{left:auto;right:-12px;border-left-color:#95ec69;border-right-color:transparent}.chatList-text--ai .chatList-arrow{border-right-color:#fff}.chatList__msg--text{margin:0;font-size:14px;line-height:1.6;white-space:pre-wrap}.chatList__msg--img{max-width:200px;max-height:200px;border-radius:4px;cursor:pointer;display:block}.chatList__msg--audio{width:200px;height:40px}.chatList__msg--voice{display:flex;align-items:center;gap:8px;padding:8px 12px;background-color:#f0f0f0;border-radius:6px;cursor:pointer;transition:all .2s;-webkit-user-select:none;user-select:none}.chatList__msg--voice:hover{background-color:#e8e8e8}.voice-play-icon{font-size:20px;color:#0067cc;flex-shrink:0}.voice-duration{font-size:14px;color:#333;min-width:30px}.chatList__msg--video{max-width:300px;max-height:200px;border-radius:4px}.streaming-indicator{display:inline-block;animation:blink 1s infinite;margin-left:4px;color:#1890ff}@keyframes blink{0%,50%{opacity:1}51%,to{opacity:0}}.chatList-loading{text-align:center;padding:20px}.loading-dots{display:inline-flex;gap:6px}.loading-dots span{width:8px;height:8px;border-radius:50%;background-color:#1890ff;animation:bounce 1.4s infinite ease-in-out both}.loading-dots span:nth-child(1){animation-delay:-.32s}.loading-dots span:nth-child(2){animation-delay:-.16s}@keyframes bounce{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.resize-handle{height:8px;background-color:#f5f5f5;cursor:ns-resize;display:flex;align-items:center;justify-content:center;transition:background-color .2s;position:relative;-webkit-user-select:none;user-select:none}.resize-handle:hover{background-color:#e5e5e5}.resize-handle.resizing{background-color:#1890ff}.resize-line{width:40px;height:3px;background-color:#d9d9d9;border-radius:2px;transition:background-color .2s}.resize-handle:hover .resize-line,.resize-handle.resizing .resize-line{background-color:#1890ff}.toolBox{display:flex;justify-content:space-between;align-items:center;padding:8px 16px;background-color:#fff;border-top:1px solid #e5e5e5}.toolBox-left,.toolBox-right{display:flex;gap:16px}.tool-icon{font-size:20px;color:#666;cursor:pointer;transition:color .2s}.tool-icon:hover{color:#1890ff}.tool-icon.recording-active{color:#ff4d4f;animation:pulse 1s infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.writeBox{flex:1;display:flex;flex-direction:column;background-color:#fff;border-top:1px solid #e5e5e5}.recording-dialog{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:100;background-color:#000000e6;border-radius:12px;padding:32px 48px;box-shadow:0 8px 24px #0000004d;animation:fadeIn .3s ease}.recording-dialog-content{display:flex;flex-direction:column;align-items:center;gap:16px}.recording-icon-large{width:80px;height:80px;border-radius:50%;background-color:#ff4d4f;display:flex;align-items:center;justify-content:center;animation:pulse 1.5s infinite}.recording-icon-large i{font-size:40px;color:#fff}.recording-text{font-size:16px;color:#fffc}.recording-stop-btn{display:flex;align-items:center;gap:8px;padding:12px 24px;background-color:#fff;color:#ff4d4f;border:none;border-radius:24px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;margin-top:8px}.recording-stop-btn:hover{background-color:#ff4d4f;color:#fff;transform:scale(1.05)}.recording-stop-btn i{font-size:18px}.recording-dialog-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background:#000000b3;display:flex;align-items:center;justify-content:center;z-index:100;pointer-events:auto;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.recording-dialog-content{display:flex;flex-direction:column;align-items:center;gap:16px;background-color:#000000e6;border-radius:12px;padding:32px 48px;box-shadow:0 8px 24px #0000004d;animation:slideUp .3s ease}.recording-icon{width:80px;height:80px;border-radius:50%;background-color:#ff4d4f;display:flex;align-items:center;justify-content:center;animation:pulse 1.5s infinite;transition:all .2s}.recording-icon i{font-size:40px;color:#fff}.recording-icon.cancel-mode{background-color:#ff7875;transform:scale(1.1)}.recording-icon.voice-to-text-mode{background-color:#1890ff;transform:scale(1.1)}.recording-time{font-size:32px;font-weight:600;color:#fff;font-variant-numeric:tabular-nums}.recording-hints{display:flex;gap:40px;margin-top:12px}.hint-item{display:flex;flex-direction:column;align-items:center;gap:8px;color:#ffffff80;transition:all .2s}.hint-item i{font-size:24px}.hint-item span{font-size:12px;font-weight:500;color:#fff9}.hint-item.active{color:#fff;transform:scale(1.1)}.hint-item.active i{animation:bounce .6s ease-in-out infinite;color:#1890ff;text-shadow:0 0 10px rgba(24,144,255,.8)}.input-container{flex:1;display:flex;flex-direction:column;min-height:0}.message-input{flex:1;width:100%;min-height:60px;padding:8px 12px;border:1px solid #d9d9d9;border-radius:4px;font-size:14px;line-height:1.6;resize:none;font-family:inherit;transition:border-color .2s}.message-input:focus{outline:none;border-color:#1890ff;box-shadow:0 0 0 2px #1890ff1a}.send-btn-wrapper{display:flex;gap:8px}.send-btn{display:flex;align-items:center;gap:6px;padding:8px 20px;background-color:#1890ff;color:#fff;border:none;border-radius:4px;font-size:14px;cursor:pointer;transition:background-color .2s}.send-btn:hover:not(:disabled){background-color:#40a9ff}.send-btn:disabled{background-color:#d9d9d9;cursor:not-allowed}.emoji-picker{position:absolute;bottom:200px;left:16px;background-color:#fff;border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 4px 12px #00000026;padding:12px;z-index:1000;width:380px}.emoji-list{display:grid;grid-template-columns:repeat(12,1fr);gap:4px;max-height:none;overflow:visible}.emoji-item{font-size:20px;cursor:pointer;text-align:center;padding:0;border-radius:4px;transition:all .2s;line-height:1;-webkit-user-select:none;user-select:none;width:28px;height:28px;display:flex;align-items:center;justify-content:center}.context-menu{position:fixed;background-color:#fff;border:1px solid #e5e5e5;border-radius:6px;box-shadow:0 4px 12px #00000026;padding:4px 0;z-index:1000;min-width:120px}.context-menu-item{display:flex;align-items:center;gap:8px;padding:8px 16px;cursor:pointer;transition:background-color .2s}.context-menu-item:hover{background-color:#f5f5f5}.context-menu-item i{font-size:16px;color:#666}.context-menu-item span{font-size:14px;color:#333}.im-container--mobile{font-size:14px}.im-container--mobile .header{padding:10px 12px}.im-container--mobile .user-name{font-size:15px}.im-container--mobile .chatList{padding:12px}.im-container--mobile .chatList-avatar,.im-container--mobile .chatList-avatar--placeholder{width:36px;height:36px}.im-container--mobile .chatList-text{font-size:15px}.im-container--mobile .toolBox{padding:6px 12px}.im-container--mobile .tool-icon{font-size:22px}.im-container--mobile .input-container{padding:10px 12px}.im-container--mobile .message-input{font-size:15px}.im-container--mobile .send-btn{padding:6px 16px;font-size:15px}@media(max-width:768px){.chatList-content{max-width:75%}.emoji-picker{left:50%;transform:translate(-50%);max-width:90%}.emoji-list{grid-template-columns:repeat(10,1fr)}.emoji-item{font-size:18px;width:26px;height:26px}}.emoji-picker{position:absolute;bottom:200px;left:16px;background-color:#fff;border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 4px 12px #00000026;padding:0;z-index:1000;width:420px}.emoji-tabs{display:flex;border-bottom:1px solid #e5e5e5;padding:8px 12px;gap:4px;background-color:#fafafa;border-radius:8px 8px 0 0}.emoji-tab{width:36px;height:36px;display:flex;align-items:center;justify-content:center;font-size:20px;cursor:pointer;border-radius:6px;transition:all .2s;-webkit-user-select:none;user-select:none}.emoji-tab:hover{background-color:#f0f0f0}.emoji-tab.active{background-color:#e6f7ff;border:1px solid #91d5ff}.emoji-list{display:grid;grid-template-columns:repeat(12,1fr);gap:4px;padding:12px}.emoji-item{cursor:pointer;text-align:center;padding:0;border-radius:4px;transition:all .2s;line-height:1;-webkit-user-select:none;user-select:none;width:28px;height:28px;display:flex;align-items:center;justify-content:center}.emoji-item img{width:24px;height:24px;object-fit:contain}.emoji-item:hover{background-color:#f5f5f5;transform:scale(1.15)}.emoji-img{width:20px;height:20px;vertical-align:middle;margin:0 2px}.favorite-dialog-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.favorite-dialog{background-color:#fff;border-radius:12px;width:90%;max-width:400px;box-shadow:0 8px 24px #00000026;animation:slideUp .3s ease}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.favorite-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #e5e5e5}.favorite-dialog-title{font-size:16px;font-weight:600;color:#333}.favorite-dialog-close{font-size:20px;color:#999;cursor:pointer;transition:color .2s}.favorite-dialog-close:hover{color:#333}.favorite-dialog-content{padding:20px;max-height:400px;overflow-y:auto}.favorite-default-content{display:flex;flex-direction:column;gap:16px}.favorite-message-preview{background-color:#f5f5f5;border-radius:8px;padding:12px}.favorite-message-type{display:flex;align-items:center;gap:6px;margin-bottom:8px;color:#666;font-size:13px}.favorite-message-type i{font-size:16px}.favorite-message-text{color:#333;font-size:14px;line-height:1.6;word-wrap:break-word}.favorite-message-image{max-width:100%}.favorite-message-image img{max-width:100%;max-height:200px;border-radius:4px;object-fit:contain}.favorite-note-input{width:100%;padding:10px;border:1px solid #e5e5e5;border-radius:6px;font-size:14px;line-height:1.6;resize:vertical;font-family:inherit;transition:border-color .2s}.favorite-note-input:focus{outline:none;border-color:#1890ff}.favorite-dialog-footer{display:flex;gap:12px;padding:16px 20px;border-top:1px solid #e5e5e5}.favorite-btn{flex:1;padding:10px 20px;border:none;border-radius:6px;font-size:14px;cursor:pointer;transition:all .2s}.favorite-btn-cancel{background-color:#f5f5f5;color:#666}.favorite-btn-cancel:hover{background-color:#e5e5e5}.favorite-btn-confirm{background-color:#1890ff;color:#fff}.favorite-btn-confirm:hover{background-color:#40a9ff}.favorite-btn-confirm:active{background-color:#096dd9}.favorites-list-dialog-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:3000;animation:fadeIn .2s ease}.favorites-list-dialog{background-color:#fff;border-radius:12px;width:90%;max-width:600px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 8px 24px #00000026;animation:slideUp .3s ease}.favorites-list-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #e5e5e5}.favorites-list-title{font-size:16px;font-weight:600;color:#333}.favorites-list-close{font-size:20px;color:#999;cursor:pointer;transition:color .2s}.favorites-list-close:hover{color:#333}.favorites-list-content{flex:1;overflow-y:auto;padding:12px 0}.favorites-list-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;color:#999}.favorites-list-empty i{font-size:64px;margin-bottom:16px}.favorites-list-empty p{font-size:14px}.favorites-list-items{display:flex;flex-direction:column}.favorites-list-item{display:flex;align-items:center;padding:12px 20px;cursor:pointer;transition:background-color .2s;border-bottom:1px solid #f0f0f0}.favorites-list-item:hover{background-color:#f5f7fa}.favorites-list-item:last-child{border-bottom:none}.favorite-item-icon{font-size:24px;color:#1890ff;margin-right:12px;flex-shrink:0}.favorite-item-content{flex:1;min-width:0}.favorite-item-title{font-size:15px;font-weight:500;color:#333;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.favorite-item-preview{font-size:13px;color:#999;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:4px}.favorite-item-time{font-size:12px;color:#bbb}.favorite-item-arrow{font-size:20px;color:#ccc;margin-left:12px;flex-shrink:0}.mention-list-container{position:relative;z-index:99999;pointer-events:auto!important}.mention-item{display:flex;align-items:center;padding:8px 12px;cursor:pointer;transition:background-color .2s;position:relative;z-index:99999;pointer-events:auto!important}.mention-item:hover,.mention-item-selected{background-color:#f5f5f5}.mention-avatar{width:36px;height:36px;border-radius:4px;margin-right:10px;flex-shrink:0;object-fit:cover}.mention-avatar-placeholder{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:500}.mention-info{flex:1;min-width:0;overflow:hidden}.mention-name{font-size:14px;color:#333;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:2px}.mention-account{font-size:12px;color:#999;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chatList__msg--file{display:flex;align-items:center;gap:12px;padding:12px;background-color:#f5f5f5;border-radius:6px;min-width:200px;cursor:pointer;transition:background-color .2s}.chatList__msg--file:hover{background-color:#e5e5e5}.file-icon{font-size:32px;color:#1890ff;flex-shrink:0}.file-info{flex:1;min-width:0}.file-name{font-size:14px;color:#333;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:4px}.file-size{font-size:12px;color:#999}.chatList-item--mine .chatList-content{display:flex;flex-direction:column;align-items:flex-end}.chatList-item--mine .chatList-text{text-align:left}.input-wrapper{position:relative;flex:1;transition:border-color .2s}.input-wrapper:focus-within{border-color:#0067cc;background:#fff}.message-input{width:100%;max-height:360px;padding:12px 100px 12px 12px;border:none;outline:none;background:transparent;font-size:14px;line-height:1.6;resize:vertical;font-family:inherit}.message-input::placeholder{color:#999}.input-actions{position:absolute;right:8px;bottom:30px;display:flex;gap:4px;align-items:center}.send-options-btn{width:32px;height:32px;border:none;background:transparent;color:#666;border-radius:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:18px;transition:all .2s}.send-options-btn:hover:not(:disabled){background:#e8e8e8;color:#333}.send-options-btn:disabled{opacity:.4;cursor:not-allowed}.send-btn{height:32px;padding:0 16px;border:none;background:#0067cc;color:#fff;border-radius:4px;cursor:pointer;display:flex;align-items:center;gap:4px;font-size:14px;font-weight:500;transition:all .2s}.send-btn:hover:not(:disabled){background:#0052a3;transform:translateY(-1px);box-shadow:0 2px 8px #0067cc4d}.send-btn:active:not(:disabled){transform:translateY(0)}.send-btn:disabled{opacity:.5;cursor:not-allowed}.send-btn i{font-size:16px}.send-options-menu{position:absolute;right:12px;bottom:100%;margin-bottom:8px;background:#fff;border-radius:8px;box-shadow:0 4px 20px #00000026;min-width:160px;padding:8px 0;z-index:1000;animation:slideUp .2s ease}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.send-option-item{display:flex;align-items:center;gap:12px;padding:10px 16px;cursor:pointer;transition:background .2s;color:#333;font-size:14px}.send-option-item:hover{background:#f5f5f5}.send-option-item i{font-size:18px;color:#666;width:20px;text-align:center}.send-option-item:hover i{color:#0067cc}.send-option-item:has(.ri-fire-line):hover{background:#fff3f3}.send-option-item:has(.ri-fire-line):hover i{color:#ff4757}.send-option-item:has(.ri-alarm-warning-line):hover{background:#fff8e6}.send-option-item:has(.ri-alarm-warning-line):hover i{color:#ff9800}@media(max-width:768px){.message-input{padding:10px 90px 10px 10px;min-height:60px}.send-btn{padding:0 12px;font-size:13px}.send-options-menu{right:8px}}.scheduled-messages{margin-top:12px;background:#fff9e6;border:1px solid #ffe58f;border-radius:8px;padding:12px;max-height:200px;overflow-y:auto}.scheduled-messages-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;color:#d48806;font-size:14px;font-weight:500}.scheduled-messages-header i{font-size:16px}.scheduled-message-item{display:flex;align-items:flex-start;gap:12px;padding:10px;background:#fff;border-radius:6px;margin-bottom:8px;border:1px solid #ffe58f;transition:all .2s}.scheduled-message-item:last-child{margin-bottom:0}.scheduled-message-item:hover{border-color:#ffc53d;box-shadow:0 2px 8px #faad1426}.scheduled-message-text{color:#333;font-size:14px;line-height:1.5;margin-bottom:6px;word-break:break-word}.scheduled-message-time{display:flex;align-items:center;gap:4px;color:#d48806;font-size:12px}.scheduled-message-time i{font-size:14px}.scheduled-message-actions{display:flex;gap:4px;flex-shrink:0}.scheduled-action-btn{width:28px;height:28px;border:none;background:transparent;border-radius:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;transition:all .2s;color:#666}.scheduled-action-btn:hover{background:#f5f5f5}.edit-btn:hover{color:#0067cc;background:#e6f7ff}.cancel-btn:hover{color:#ff4d4f;background:#fff1f0}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.scheduled-message-item{animation:slideIn .3s ease}.template-dialog-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.template-dialog{background:#fff;border-radius:12px;width:90%;max-width:600px;max-height:80vh;overflow:hidden;box-shadow:0 8px 32px #0003;animation:slideUp .3s ease;display:flex;flex-direction:column}.template-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:#0067cc;color:#fff;font-weight:500;flex-shrink:0}.template-header-actions{display:flex;gap:12px}.template-header-actions i{font-size:20px;cursor:pointer;padding:4px;border-radius:4px;transition:background .2s}.template-header-actions i:hover{background:#fff3}.template-dialog-body{padding:20px;overflow-y:auto;flex:1}.template-filters{margin-bottom:20px}.template-search{width:100%;padding:10px 12px;border:2px solid #e0e0e0;border-radius:8px;font-size:14px;outline:none;transition:border-color .2s;margin-bottom:12px}.template-search:focus{border-color:#0067cc}.template-categories{display:flex;gap:8px;flex-wrap:wrap}.category-btn{padding:8px 16px;border:2px solid #e0e0e0;background:#fff;color:#666;border-radius:20px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s;white-space:nowrap}.category-btn:hover{border-color:#0067cc;color:#0067cc}.category-btn.active{border-color:#0067cc;background:#0067cc;color:#fff}.template-list{display:flex;flex-direction:column;gap:12px}.template-item{display:flex;gap:12px;padding:16px;border:2px solid #e0e0e0;border-radius:8px;transition:all .2s}.template-item:hover{border-color:#0067cc;box-shadow:0 2px 8px #0067cc1a}.template-content{flex:1;cursor:pointer}.template-name{font-weight:600;color:#333;margin-bottom:8px;font-size:15px}.template-preview{color:#666;font-size:14px;line-height:1.5;margin-bottom:8px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:pre-wrap}.template-meta{display:flex;gap:12px;font-size:12px;color:#999}.template-category{color:#0067cc}.template-actions{display:flex;flex-direction:column;gap:8px;justify-content:center}.template-actions i{font-size:18px;padding:8px;border-radius:4px;cursor:pointer;transition:all .2s;color:#666}.template-actions i:hover{background:#f5f5f5;color:#0067cc}.template-actions .ri-delete-bin-line:hover{color:#f44}.template-empty{text-align:center;padding:60px 20px;color:#999}.template-empty i{font-size:64px;color:#ddd;margin-bottom:16px}.template-empty p{font-size:16px;margin-bottom:20px}.add-template-btn{padding:10px 24px;background:#0067cc;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.add-template-btn:hover{background:#0052a3;transform:translateY(-1px);box-shadow:0 4px 12px #0067cc4d}.template-editor-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:2100;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.template-editor{background:#fff;border-radius:12px;width:90%;max-width:500px;max-height:80vh;overflow:hidden;box-shadow:0 8px 32px #0003;animation:slideUp .3s ease;display:flex;flex-direction:column}.template-editor-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:#0067cc;color:#fff;font-weight:500;flex-shrink:0}.template-editor-header i{font-size:20px;cursor:pointer;padding:4px;border-radius:4px;transition:background .2s}.template-editor-header i:hover{background:#fff3}.template-editor-body{padding:20px;overflow-y:auto;flex:1}.form-group{margin-bottom:20px}.form-group label{display:block;font-weight:500;color:#333;margin-bottom:8px;font-size:14px}.form-input{width:100%;padding:10px 12px;border:2px solid #e0e0e0;border-radius:8px;font-size:14px;outline:none;transition:border-color .2s}.form-input:focus{border-color:#0067cc}.category-selector{display:flex;gap:8px;flex-wrap:wrap}.category-option{padding:8px 16px;border:2px solid #e0e0e0;background:#fff;color:#666;border-radius:20px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s}.category-option:hover{border-color:#0067cc;color:#0067cc}.category-option.active{border-color:#0067cc;background:#0067cc;color:#fff}.form-textarea{width:100%;padding:12px;border:2px solid #e0e0e0;border-radius:8px;font-size:14px;line-height:1.6;resize:vertical;outline:none;transition:border-color .2s;font-family:inherit;min-height:120px}.form-textarea:focus{border-color:#0067cc}.variable-hint{display:flex;align-items:center;gap:8px;padding:12px;background:#f0f7ff;border-radius:8px;color:#0067cc;font-size:13px}.variable-hint i{font-size:16px}.template-editor-footer{display:flex;gap:12px;padding:16px 20px;background:#f8f9fa;border-top:1px solid #e9ecef;flex-shrink:0}.btn-cancel,.btn-save{flex:1;padding:12px 20px;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.btn-cancel{background:#f5f5f5;color:#666}.btn-cancel:hover{background:#e8e8e8;color:#333}.btn-save{background:#0067cc;color:#fff}.btn-save:hover{background:#0052a3;transform:translateY(-1px);box-shadow:0 4px 12px #0067cc4d}@media(max-width:768px){.template-dialog,.template-editor{width:95%;margin:0 10px}.template-dialog-body,.template-editor-body{padding:16px}.template-categories{overflow-x:auto;flex-wrap:nowrap;padding-bottom:8px}.template-item{flex-direction:column}.template-actions{flex-direction:row;justify-content:flex-end}}@media(prefers-color-scheme:dark){.template-dialog,.template-editor{background:#1a1a1a;color:#fff}.template-dialog-body,.template-editor-body{background:#1a1a1a}.template-search,.form-input,.form-textarea{background:#2a2a2a;border-color:#404040;color:#fff}.template-item{border-color:#404040}.template-name{color:#fff}.template-preview{color:#ccc}.category-btn,.category-option{background:#2a2a2a;border-color:#404040;color:#ccc}.template-editor-footer{background:#2a2a2a;border-top-color:#404040}.btn-cancel{background:#404040;color:#ccc}.variable-hint{background:#1a3a5a}}.input-wrapper{display:flex;gap:8px}.message-input{flex:1;padding:12px;font-size:14px;line-height:1.5;resize:vertical;outline:none;transition:border-color .2s;font-family:inherit}.message-input:focus{border-color:#0067cc}.input-actions{display:flex;gap:4px;align-items:flex-end}.send-options-btn,.send-btn{padding:10px 12px;border:none;background:#0067cc;color:#fff;border-radius:6px;cursor:pointer;font-size:16px;transition:all .2s;display:flex;align-items:center;justify-content:center;min-width:40px;height:40px}.send-options-btn:hover:not(:disabled),.send-btn:hover:not(:disabled){background:#0052a3;transform:translateY(-1px);box-shadow:0 4px 12px #0067cc4d}.send-options-btn:disabled,.send-btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.send-options-btn{position:relative}.send-options-btn:after{content:"";position:absolute;right:0;top:50%;transform:translateY(-50%);width:1px;height:20px;background:#ffffff4d}.send-options-menu{position:absolute;bottom:100%;right:0;background:#fff;border:1px solid #e0e0e0;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:100;min-width:160px;margin-bottom:8px;animation:slideUp .2s ease}.send-option-item{display:flex;align-items:center;gap:12px;padding:12px 16px;cursor:pointer;transition:all .2s;color:#333;font-size:14px}.send-option-item:hover{background:#f5f5f5;color:#0067cc}.send-option-item:first-child{border-radius:8px 8px 0 0}.send-option-item:last-child{border-radius:0 0 8px 8px}.send-option-item i{font-size:16px;width:20px;text-align:center}.scheduled-messages{margin-top:12px;padding:12px;background:#f8f9fa;border-radius:8px;border:1px solid #e9ecef}.scheduled-messages-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;font-weight:500;color:#333;font-size:14px}.scheduled-messages-header i{font-size:16px;color:#0067cc}.scheduled-message-item{display:flex;align-items:center;justify-content:space-between;padding:12px;background:#fff;border-radius:6px;margin-bottom:8px;border:1px solid #e9ecef;transition:all .2s}.scheduled-message-item:hover{box-shadow:0 2px 8px #0000001a}.scheduled-message-content{flex:1;min-width:0}.scheduled-message-text{color:#333;font-size:14px;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.scheduled-message-time{display:flex;align-items:center;gap:4px;color:#999;font-size:12px}.scheduled-message-time i{font-size:12px}.scheduled-message-actions{display:flex;gap:8px;margin-left:12px}.scheduled-action-btn{width:28px;height:28px;border:none;background:#f5f5f5;border-radius:4px;cursor:pointer;font-size:14px;transition:all .2s;display:flex;align-items:center;justify-content:center;color:#666}.scheduled-action-btn:hover{background:#e8e8e8;color:#333}.scheduled-action-btn.edit-btn:hover{background:#e6f7ff;color:#0067cc}.scheduled-action-btn.cancel-btn:hover{background:#fff1f0;color:#ff4d4f}@media(max-width:768px){.input-wrapper{flex-direction:column}.message-input{width:100%}.input-actions{width:100%;justify-content:flex-end}.send-options-btn,.send-btn{flex:1}.send-options-menu{position:fixed;bottom:auto;top:50%;right:50%;transform:translate(50%);min-width:200px;margin-bottom:0}.scheduled-messages{margin-top:8px;padding:8px}.scheduled-message-item{flex-direction:column;align-items:flex-start}.scheduled-message-actions{margin-left:0;margin-top:8px;width:100%}.scheduled-action-btn{flex:1}}.voice-to-text-dialog-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.voice-to-text-dialog{background:#fff;border-radius:12px;width:90%;max-width:500px;max-height:80vh;overflow:hidden;box-shadow:0 8px 32px #0003;animation:slideUp .3s ease;display:flex;flex-direction:column}.voice-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:#0067cc;color:#fff;font-weight:500;flex-shrink:0}.voice-dialog-header i{font-size:20px;cursor:pointer;padding:4px;border-radius:4px;transition:background .2s}.voice-dialog-header i:hover{background:#fff3}.voice-dialog-body{padding:20px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.voice-recording-status{display:flex;flex-direction:column;align-items:center;gap:16px;padding:20px}.voice-icon-large{width:80px;height:80px;border-radius:50%;background-color:#ff4d4f;display:flex;align-items:center;justify-content:center;animation:pulse 1.5s infinite}.voice-icon-large i{font-size:40px;color:#fff}.voice-waveform{display:flex;align-items:center;justify-content:center;gap:4px;height:40px}.voice-waveform span{width:4px;background:#0067cc;border-radius:2px;animation:waveform .6s ease-in-out infinite}.voice-waveform span:nth-child(1){height:20px;animation-delay:0s}.voice-waveform span:nth-child(2){height:30px;animation-delay:.1s}.voice-waveform span:nth-child(3){height:40px;animation-delay:.2s}.voice-waveform span:nth-child(4){height:30px;animation-delay:.3s}.voice-waveform span:nth-child(5){height:20px;animation-delay:.4s}@keyframes waveform{0%,to{transform:scaleY(.5)}50%{transform:scaleY(1)}}.voice-time{font-size:32px;font-weight:600;color:#333;font-variant-numeric:tabular-nums}.voice-hint{font-size:14px;color:#666}.voice-result{display:flex;flex-direction:column;gap:12px}.result-label{font-size:14px;font-weight:500;color:#333}.result-textarea{width:100%;padding:12px;border:2px solid #e0e0e0;border-radius:8px;font-size:14px;line-height:1.6;resize:vertical;outline:none;transition:border-color .2s;font-family:inherit}.result-textarea:focus{border-color:#0067cc}.voice-mode-selector{display:flex;gap:8px;justify-content:center}.mode-btn{display:flex;align-items:center;gap:6px;padding:10px 16px;border:2px solid #e0e0e0;background:#fff;color:#666;border-radius:20px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s;white-space:nowrap}.mode-btn:hover{border-color:#0067cc;color:#0067cc}.mode-btn.active{border-color:#0067cc;background:#0067cc;color:#fff}.mode-btn i{font-size:16px}.voice-dialog-footer{display:flex;gap:12px;padding:16px 20px;background:#f8f9fa;border-top:1px solid #e9ecef;flex-shrink:0}.voice-action-btn{flex:1;padding:12px 20px;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.voice-action-btn.cancel{background:#f5f5f5;color:#666}.voice-action-btn.cancel:hover{background:#e8e8e8;color:#333}.voice-action-btn.primary{background:#0067cc;color:#fff}.voice-action-btn.primary:hover:not(:disabled){background:#0052a3;transform:translateY(-1px);box-shadow:0 4px 12px #0067cc4d}.voice-action-btn:disabled{opacity:.5;cursor:not-allowed}@media(max-width:768px){.voice-to-text-dialog{width:95%;margin:0 10px}.voice-dialog-body{padding:16px}.voice-icon-large{width:60px;height:60px}.voice-icon-large i{font-size:30px}.voice-time{font-size:24px}.voice-mode-selector{flex-wrap:wrap}.mode-btn{flex:1;min-width:100px}}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cd-aichat",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Vue3组件,用于与Dify API进行交互,实现AI聊天功能",
|
|
5
|
+
"main": "dist/cd-aichat.umd.js",
|
|
6
|
+
"module": "dist/cd-aichat.es.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"vue",
|
|
13
|
+
"vue3",
|
|
14
|
+
"component",
|
|
15
|
+
"ai",
|
|
16
|
+
"chat",
|
|
17
|
+
"dify"
|
|
18
|
+
],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"vue": "^3.3.8"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@diycom/screenshots": "file:../screenshots",
|
|
26
|
+
"@diycom/cd-screencut": "file:../cd-srceencut",
|
|
27
|
+
"@tiptap/extension-image": "^3.14.0",
|
|
28
|
+
"@tiptap/extension-link": "^3.14.0",
|
|
29
|
+
"@tiptap/extension-mention": "^3.14.0",
|
|
30
|
+
"@tiptap/extension-placeholder": "^3.14.0",
|
|
31
|
+
"@tiptap/starter-kit": "^3.14.0",
|
|
32
|
+
"@tiptap/suggestion": "^3.14.0",
|
|
33
|
+
"@tiptap/vue-3": "^3.14.0",
|
|
34
|
+
"axios": "^1.4.0",
|
|
35
|
+
"html2canvas": "^1.4.1",
|
|
36
|
+
"paddleocr": "^1.0.6",
|
|
37
|
+
"react": "^19.2.3",
|
|
38
|
+
"react-dom": "^19.2.3",
|
|
39
|
+
"remixicon": "^4.8.0",
|
|
40
|
+
"tdesign-vue-next": "^1.17.7",
|
|
41
|
+
"tesseract.js": "^7.0.0",
|
|
42
|
+
"tippy.js": "^6.3.7"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@vitejs/plugin-vue": "^5.2.4",
|
|
46
|
+
"@vitest/ui": "^2.1.9",
|
|
47
|
+
"@vue/test-utils": "^2.4.6",
|
|
48
|
+
"fast-check": "^4.5.3",
|
|
49
|
+
"happy-dom": "^20.0.11",
|
|
50
|
+
"vite": "^6.0.5",
|
|
51
|
+
"vitest": "^2.1.9"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"dev": "vite",
|
|
55
|
+
"build": "vite build",
|
|
56
|
+
"preview": "vite preview",
|
|
57
|
+
"test": "vitest",
|
|
58
|
+
"test:ui": "vitest --ui",
|
|
59
|
+
"test:run": "vitest run"
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/ailogo.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import AiChat from './AiChat.vue';
|
|
4
|
+
|
|
5
|
+
// Mock DifyApiService
|
|
6
|
+
vi.mock('../services/dify-api', () => {
|
|
7
|
+
return {
|
|
8
|
+
default: vi.fn().mockImplementation((config) => ({
|
|
9
|
+
baseURL: config.url || config,
|
|
10
|
+
agentId: config.agentId || arguments[1],
|
|
11
|
+
platform: config.platform || '',
|
|
12
|
+
scene: config.scene || '',
|
|
13
|
+
sendMessage: vi.fn().mockResolvedValue({
|
|
14
|
+
answer: 'AI response',
|
|
15
|
+
conversation_id: 'conv-001',
|
|
16
|
+
message_id: 'msg-001'
|
|
17
|
+
}),
|
|
18
|
+
updateConfig: vi.fn(),
|
|
19
|
+
resetConversation: vi.fn(),
|
|
20
|
+
isConversationExpired: vi.fn().mockReturnValue(false)
|
|
21
|
+
}))
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('AiChat Props', () => {
|
|
26
|
+
describe('向后兼容性', () => {
|
|
27
|
+
it('应该支持旧的 aiConfig 格式(只有 url 和 agentId)', () => {
|
|
28
|
+
const wrapper = mount(AiChat, {
|
|
29
|
+
props: {
|
|
30
|
+
currentUserId: 'user-001',
|
|
31
|
+
chatType: 'ai',
|
|
32
|
+
aiConfig: {
|
|
33
|
+
url: 'https://api.dify.ai/v1',
|
|
34
|
+
agentId: 'agent-001'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(wrapper.exists()).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('应该在缺少新字段时使用默认值', () => {
|
|
43
|
+
const wrapper = mount(AiChat, {
|
|
44
|
+
props: {
|
|
45
|
+
currentUserId: 'user-001',
|
|
46
|
+
chatType: 'ai',
|
|
47
|
+
aiConfig: {
|
|
48
|
+
url: 'https://api.dify.ai/v1',
|
|
49
|
+
agentId: 'agent-001'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const aiConfig = wrapper.props('aiConfig');
|
|
55
|
+
expect(aiConfig.platform).toBeUndefined();
|
|
56
|
+
expect(aiConfig.scene).toBeUndefined();
|
|
57
|
+
expect(aiConfig.knowledgeBase).toBeUndefined();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('新的 aiConfig 字段', () => {
|
|
62
|
+
it('应该接受 platform 字段', () => {
|
|
63
|
+
const wrapper = mount(AiChat, {
|
|
64
|
+
props: {
|
|
65
|
+
currentUserId: 'user-001',
|
|
66
|
+
chatType: 'ai',
|
|
67
|
+
aiConfig: {
|
|
68
|
+
url: 'https://api.dify.ai/v1',
|
|
69
|
+
agentId: 'agent-001',
|
|
70
|
+
platform: 'report'
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(wrapper.props('aiConfig').platform).toBe('report');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('应该接受 scene 字段', () => {
|
|
79
|
+
const wrapper = mount(AiChat, {
|
|
80
|
+
props: {
|
|
81
|
+
currentUserId: 'user-001',
|
|
82
|
+
chatType: 'ai',
|
|
83
|
+
aiConfig: {
|
|
84
|
+
url: 'https://api.dify.ai/v1',
|
|
85
|
+
agentId: 'agent-001',
|
|
86
|
+
scene: 'data-analysis'
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(wrapper.props('aiConfig').scene).toBe('data-analysis');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('应该接受 knowledgeBase 字段', () => {
|
|
95
|
+
const kb = {
|
|
96
|
+
type: 'json',
|
|
97
|
+
data: { test: 'data' }
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const wrapper = mount(AiChat, {
|
|
101
|
+
props: {
|
|
102
|
+
currentUserId: 'user-001',
|
|
103
|
+
chatType: 'ai',
|
|
104
|
+
aiConfig: {
|
|
105
|
+
url: 'https://api.dify.ai/v1',
|
|
106
|
+
agentId: 'agent-001',
|
|
107
|
+
knowledgeBase: kb
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(wrapper.props('aiConfig').knowledgeBase).toEqual(kb);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('应该接受 inputVariables 字段', () => {
|
|
116
|
+
const vars = {
|
|
117
|
+
userRole: 'analyst',
|
|
118
|
+
currentPage: 'dashboard'
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const wrapper = mount(AiChat, {
|
|
122
|
+
props: {
|
|
123
|
+
currentUserId: 'user-001',
|
|
124
|
+
chatType: 'ai',
|
|
125
|
+
aiConfig: {
|
|
126
|
+
url: 'https://api.dify.ai/v1',
|
|
127
|
+
agentId: 'agent-001',
|
|
128
|
+
inputVariables: vars
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(wrapper.props('aiConfig').inputVariables).toEqual(vars);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('应该接受 conversation 配置', () => {
|
|
137
|
+
const conversation = {
|
|
138
|
+
id: 'conv-001',
|
|
139
|
+
autoCreate: false,
|
|
140
|
+
timeout: 3600000
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const wrapper = mount(AiChat, {
|
|
144
|
+
props: {
|
|
145
|
+
currentUserId: 'user-001',
|
|
146
|
+
chatType: 'ai',
|
|
147
|
+
aiConfig: {
|
|
148
|
+
url: 'https://api.dify.ai/v1',
|
|
149
|
+
agentId: 'agent-001',
|
|
150
|
+
conversation
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(wrapper.props('aiConfig').conversation).toEqual(conversation);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('应该接受 response 配置', () => {
|
|
159
|
+
const response = {
|
|
160
|
+
mode: 'streaming',
|
|
161
|
+
timeout: 60000,
|
|
162
|
+
maxRetries: 5
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const wrapper = mount(AiChat, {
|
|
166
|
+
props: {
|
|
167
|
+
currentUserId: 'user-001',
|
|
168
|
+
chatType: 'ai',
|
|
169
|
+
aiConfig: {
|
|
170
|
+
url: 'https://api.dify.ai/v1',
|
|
171
|
+
agentId: 'agent-001',
|
|
172
|
+
response
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(wrapper.props('aiConfig').response).toEqual(response);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('enableAiConfigHotReload prop', () => {
|
|
182
|
+
it('应该有默认值 true', () => {
|
|
183
|
+
const wrapper = mount(AiChat, {
|
|
184
|
+
props: {
|
|
185
|
+
currentUserId: 'user-001',
|
|
186
|
+
chatType: 'ai',
|
|
187
|
+
aiConfig: {
|
|
188
|
+
url: 'https://api.dify.ai/v1',
|
|
189
|
+
agentId: 'agent-001'
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(wrapper.props('enableAiConfigHotReload')).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('应该接受 false 值', () => {
|
|
198
|
+
const wrapper = mount(AiChat, {
|
|
199
|
+
props: {
|
|
200
|
+
currentUserId: 'user-001',
|
|
201
|
+
chatType: 'ai',
|
|
202
|
+
aiConfig: {
|
|
203
|
+
url: 'https://api.dify.ai/v1',
|
|
204
|
+
agentId: 'agent-001'
|
|
205
|
+
},
|
|
206
|
+
enableAiConfigHotReload: false
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(wrapper.props('enableAiConfigHotReload')).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('完整配置', () => {
|
|
215
|
+
it('应该接受所有新字段的完整配置', () => {
|
|
216
|
+
const fullConfig = {
|
|
217
|
+
url: 'https://api.dify.ai/v1',
|
|
218
|
+
agentId: 'agent-001',
|
|
219
|
+
apiKey: 'key-001',
|
|
220
|
+
platform: 'report',
|
|
221
|
+
scene: 'data-analysis',
|
|
222
|
+
knowledgeBase: {
|
|
223
|
+
type: 'json',
|
|
224
|
+
data: { reports: ['sales', 'finance'] }
|
|
225
|
+
},
|
|
226
|
+
inputVariables: {
|
|
227
|
+
userRole: 'analyst',
|
|
228
|
+
currentPage: 'dashboard'
|
|
229
|
+
},
|
|
230
|
+
conversation: {
|
|
231
|
+
id: 'conv-001',
|
|
232
|
+
autoCreate: true,
|
|
233
|
+
timeout: 1800000
|
|
234
|
+
},
|
|
235
|
+
response: {
|
|
236
|
+
mode: 'blocking',
|
|
237
|
+
timeout: 30000,
|
|
238
|
+
maxRetries: 3
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const wrapper = mount(AiChat, {
|
|
243
|
+
props: {
|
|
244
|
+
currentUserId: 'user-001',
|
|
245
|
+
chatType: 'ai',
|
|
246
|
+
aiConfig: fullConfig,
|
|
247
|
+
enableAiConfigHotReload: true
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(wrapper.props('aiConfig')).toEqual(fullConfig);
|
|
252
|
+
expect(wrapper.props('enableAiConfigHotReload')).toBe(true);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
describe('AiChat 配置热更新', () => {
|
|
259
|
+
it('应该在 aiConfig 更新时触发 aiConfigUpdate 事件', async () => {
|
|
260
|
+
const wrapper = mount(AiChat, {
|
|
261
|
+
props: {
|
|
262
|
+
currentUserId: 'user-001',
|
|
263
|
+
chatType: 'ai',
|
|
264
|
+
aiConfig: {
|
|
265
|
+
url: 'https://api.dify.ai/v1',
|
|
266
|
+
agentId: 'agent-001',
|
|
267
|
+
platform: 'report'
|
|
268
|
+
},
|
|
269
|
+
enableAiConfigHotReload: true
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// 更新配置
|
|
274
|
+
await wrapper.setProps({
|
|
275
|
+
aiConfig: {
|
|
276
|
+
url: 'https://api.dify.ai/v1',
|
|
277
|
+
agentId: 'agent-001',
|
|
278
|
+
platform: 'lowcode'
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// 等待下一个 tick
|
|
283
|
+
await wrapper.vm.$nextTick();
|
|
284
|
+
|
|
285
|
+
// 检查事件是否被触发
|
|
286
|
+
expect(wrapper.emitted('aiConfigUpdate')).toBeTruthy();
|
|
287
|
+
expect(wrapper.emitted('aiConfigUpdate')[0][0].success).toBe(true);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('应该在 enableAiConfigHotReload 为 false 时不更新配置', async () => {
|
|
291
|
+
const wrapper = mount(AiChat, {
|
|
292
|
+
props: {
|
|
293
|
+
currentUserId: 'user-001',
|
|
294
|
+
chatType: 'ai',
|
|
295
|
+
aiConfig: {
|
|
296
|
+
url: 'https://api.dify.ai/v1',
|
|
297
|
+
agentId: 'agent-001',
|
|
298
|
+
platform: 'report'
|
|
299
|
+
},
|
|
300
|
+
enableAiConfigHotReload: false
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// 更新配置
|
|
305
|
+
await wrapper.setProps({
|
|
306
|
+
aiConfig: {
|
|
307
|
+
url: 'https://api.dify.ai/v1',
|
|
308
|
+
agentId: 'agent-001',
|
|
309
|
+
platform: 'lowcode'
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
await wrapper.vm.$nextTick();
|
|
314
|
+
|
|
315
|
+
// 不应该触发事件
|
|
316
|
+
expect(wrapper.emitted('aiConfigUpdate')).toBeFalsy();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('应该在非 AI 模式下不更新配置', async () => {
|
|
320
|
+
const wrapper = mount(AiChat, {
|
|
321
|
+
props: {
|
|
322
|
+
currentUserId: 'user-001',
|
|
323
|
+
chatType: 'person',
|
|
324
|
+
aiConfig: {
|
|
325
|
+
url: 'https://api.dify.ai/v1',
|
|
326
|
+
agentId: 'agent-001'
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// 更新配置
|
|
332
|
+
await wrapper.setProps({
|
|
333
|
+
aiConfig: {
|
|
334
|
+
url: 'https://api.dify.ai/v1',
|
|
335
|
+
agentId: 'agent-002'
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
await wrapper.vm.$nextTick();
|
|
340
|
+
|
|
341
|
+
// 不应该触发事件
|
|
342
|
+
expect(wrapper.emitted('aiConfigUpdate')).toBeFalsy();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe('AiChat 会话管理', () => {
|
|
347
|
+
it('应该在切换聊天对象时重置会话', async () => {
|
|
348
|
+
const wrapper = mount(AiChat, {
|
|
349
|
+
props: {
|
|
350
|
+
currentUserId: 'user-001',
|
|
351
|
+
chatType: 'ai',
|
|
352
|
+
chatUser: {
|
|
353
|
+
id: 'ai-001',
|
|
354
|
+
name: 'AI 助手 1'
|
|
355
|
+
},
|
|
356
|
+
aiConfig: {
|
|
357
|
+
url: 'https://api.dify.ai/v1',
|
|
358
|
+
agentId: 'agent-001'
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// 切换聊天对象
|
|
364
|
+
await wrapper.setProps({
|
|
365
|
+
chatUser: {
|
|
366
|
+
id: 'ai-002',
|
|
367
|
+
name: 'AI 助手 2'
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
await wrapper.vm.$nextTick();
|
|
372
|
+
|
|
373
|
+
// 应该触发会话更新事件
|
|
374
|
+
expect(wrapper.emitted('conversationUpdate')).toBeTruthy();
|
|
375
|
+
expect(wrapper.emitted('conversationUpdate')[0][0].action).toBe('reset');
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
describe('AiChat 消息发送', () => {
|
|
381
|
+
it('应该在发送消息时触发 send 事件', async () => {
|
|
382
|
+
const wrapper = mount(AiChat, {
|
|
383
|
+
props: {
|
|
384
|
+
currentUserId: 'user-001',
|
|
385
|
+
chatType: 'person'
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// 设置消息内容
|
|
390
|
+
const textarea = wrapper.find('textarea');
|
|
391
|
+
await textarea.setValue('Hello');
|
|
392
|
+
|
|
393
|
+
// 点击发送按钮
|
|
394
|
+
const sendBtn = wrapper.find('.send-btn');
|
|
395
|
+
await sendBtn.trigger('click');
|
|
396
|
+
|
|
397
|
+
// 检查事件
|
|
398
|
+
expect(wrapper.emitted('send')).toBeTruthy();
|
|
399
|
+
expect(wrapper.emitted('send')[0][0].content).toBe('Hello');
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('应该在 AI 模式下触发 aiResponseStart 事件', async () => {
|
|
403
|
+
const wrapper = mount(AiChat, {
|
|
404
|
+
props: {
|
|
405
|
+
currentUserId: 'user-001',
|
|
406
|
+
chatType: 'ai',
|
|
407
|
+
aiConfig: {
|
|
408
|
+
url: 'https://api.dify.ai/v1',
|
|
409
|
+
agentId: 'agent-001'
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// 设置消息内容
|
|
415
|
+
const textarea = wrapper.find('textarea');
|
|
416
|
+
await textarea.setValue('Hello AI');
|
|
417
|
+
|
|
418
|
+
// 点击发送按钮
|
|
419
|
+
const sendBtn = wrapper.find('.send-btn');
|
|
420
|
+
await sendBtn.trigger('click');
|
|
421
|
+
|
|
422
|
+
// 等待异步操作
|
|
423
|
+
await wrapper.vm.$nextTick();
|
|
424
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
425
|
+
|
|
426
|
+
// 检查事件
|
|
427
|
+
expect(wrapper.emitted('aiResponseStart')).toBeTruthy();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('应该在 AI 响应成功时触发 aiResponseComplete 事件', async () => {
|
|
431
|
+
const wrapper = mount(AiChat, {
|
|
432
|
+
props: {
|
|
433
|
+
currentUserId: 'user-001',
|
|
434
|
+
chatType: 'ai',
|
|
435
|
+
aiConfig: {
|
|
436
|
+
url: 'https://api.dify.ai/v1',
|
|
437
|
+
agentId: 'agent-001'
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// 设置消息内容
|
|
443
|
+
const textarea = wrapper.find('textarea');
|
|
444
|
+
await textarea.setValue('Hello AI');
|
|
445
|
+
|
|
446
|
+
// 点击发送按钮
|
|
447
|
+
const sendBtn = wrapper.find('.send-btn');
|
|
448
|
+
await sendBtn.trigger('click');
|
|
449
|
+
|
|
450
|
+
// 等待异步操作
|
|
451
|
+
await wrapper.vm.$nextTick();
|
|
452
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
453
|
+
|
|
454
|
+
// 检查事件
|
|
455
|
+
expect(wrapper.emitted('aiResponseComplete')).toBeTruthy();
|
|
456
|
+
const event = wrapper.emitted('aiResponseComplete')[0][0];
|
|
457
|
+
expect(event.response).toBe('AI response');
|
|
458
|
+
expect(event.conversationId).toBe('conv-001');
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('应该在 AI 响应失败时触发 aiResponseError 事件', async () => {
|
|
462
|
+
// 创建一个会失败的 mock
|
|
463
|
+
const failingMock = vi.fn().mockImplementation((config) => ({
|
|
464
|
+
baseURL: config.url || config,
|
|
465
|
+
agentId: config.agentId || arguments[1],
|
|
466
|
+
sendMessage: vi.fn().mockRejectedValue(new Error('API Error')),
|
|
467
|
+
isConversationExpired: vi.fn().mockReturnValue(false),
|
|
468
|
+
resetConversation: vi.fn()
|
|
469
|
+
}));
|
|
470
|
+
|
|
471
|
+
// 临时替换 mock
|
|
472
|
+
const DifyApiService = await import('../services/dify-api');
|
|
473
|
+
const originalMock = DifyApiService.default;
|
|
474
|
+
DifyApiService.default = failingMock;
|
|
475
|
+
|
|
476
|
+
const wrapper = mount(AiChat, {
|
|
477
|
+
props: {
|
|
478
|
+
currentUserId: 'user-001',
|
|
479
|
+
chatType: 'ai',
|
|
480
|
+
aiConfig: {
|
|
481
|
+
url: 'https://api.dify.ai/v1',
|
|
482
|
+
agentId: 'agent-001'
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// 设置消息内容
|
|
488
|
+
const textarea = wrapper.find('textarea');
|
|
489
|
+
await textarea.setValue('Hello AI');
|
|
490
|
+
|
|
491
|
+
// 点击发送按钮
|
|
492
|
+
const sendBtn = wrapper.find('.send-btn');
|
|
493
|
+
await sendBtn.trigger('click');
|
|
494
|
+
|
|
495
|
+
// 等待异步操作
|
|
496
|
+
await wrapper.vm.$nextTick();
|
|
497
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
498
|
+
|
|
499
|
+
// 检查事件
|
|
500
|
+
expect(wrapper.emitted('aiResponseError')).toBeTruthy();
|
|
501
|
+
|
|
502
|
+
// 恢复原始 mock
|
|
503
|
+
DifyApiService.default = originalMock;
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it('应该在消息中包含 AI 元数据', async () => {
|
|
507
|
+
const wrapper = mount(AiChat, {
|
|
508
|
+
props: {
|
|
509
|
+
currentUserId: 'user-001',
|
|
510
|
+
chatType: 'ai',
|
|
511
|
+
aiConfig: {
|
|
512
|
+
url: 'https://api.dify.ai/v1',
|
|
513
|
+
agentId: 'agent-001',
|
|
514
|
+
platform: 'report',
|
|
515
|
+
scene: 'data-analysis',
|
|
516
|
+
inputVariables: {
|
|
517
|
+
userRole: 'analyst'
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
// 设置消息内容
|
|
524
|
+
const textarea = wrapper.find('textarea');
|
|
525
|
+
await textarea.setValue('Hello AI');
|
|
526
|
+
|
|
527
|
+
// 点击发送按钮
|
|
528
|
+
const sendBtn = wrapper.find('.send-btn');
|
|
529
|
+
await sendBtn.trigger('click');
|
|
530
|
+
|
|
531
|
+
// 等待异步操作
|
|
532
|
+
await wrapper.vm.$nextTick();
|
|
533
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
534
|
+
|
|
535
|
+
// 检查 aiResponseComplete 事件中的元数据
|
|
536
|
+
expect(wrapper.emitted('aiResponseComplete')).toBeTruthy();
|
|
537
|
+
});
|
|
538
|
+
});
|