From 7552bd27e61285e5a9c65f76d7b6f317fd895597 Mon Sep 17 00:00:00 2001 From: ShineKOT <1917095344@qq.com> Date: Thu, 20 Mar 2025 14:44:36 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=20=E6=9B=B4=E6=96=B0AI=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E6=A0=8F=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=AD=90=E9=A1=B9?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat-container/chat-container.scss | 29 +-- .../chat-container/chat-container.tsx | 12 +- .../chat-minimize.scss} | 2 +- .../chat-minimize.tsx} | 4 +- .../chat-toolbar-item/chat-toolbar-item.scss | 164 ++++++++++++++ .../chat-toolbar-item/chat-toolbar-item.tsx | 208 ++++++++++++++++++ src/components/chat-toolbar/chat-toolbar.scss | 85 +------ src/components/chat-toolbar/chat-toolbar.tsx | 117 +++------- .../i-chat-toolbar-item.ts | 10 +- src/utils/index.ts | 2 +- 10 files changed, 438 insertions(+), 195 deletions(-) rename src/components/{chart-minimize/chart-minimize.scss => chat-minimize/chat-minimize.scss} (98%) rename src/components/{chart-minimize/chart-minimize.tsx => chat-minimize/chat-minimize.tsx} (98%) create mode 100644 src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.scss create mode 100644 src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.tsx diff --git a/src/components/chat-container/chat-container.scss b/src/components/chat-container/chat-container.scss index b9c7076..9f50ee5 100644 --- a/src/components/chat-container/chat-container.scss +++ b/src/components/chat-container/chat-container.scss @@ -52,10 +52,17 @@ $ai-chat: ( } @include e(toolbar) { - display: flex; - flex-wrap: wrap; + margin: 0; + left: 20px; + gap: 8px 4px; + bottom: 118px; + min-height: 48px; + position: absolute; justify-content: center; - margin: 8px; + width: calc(100% - 40px); + @include when(has-materials) { + bottom: 180px; + } } *::-webkit-scrollbar { @@ -127,22 +134,6 @@ $ai-chat: ( width: calc(100% - 200px); height: 100%; - .#{bem(chat-toolbar, default)} { - position: absolute; - bottom: 118px; - left: 20px; - gap: 8px 16px; - width: calc(100% - 40px); - min-height: 48px; - margin: 0; - - &.is-has-materials { - bottom: 180px; - } - .#{bem(chat-toolbar, item)} { - margin: 0; - } - } .#{bem(chat-messages)} { padding-bottom: 48px; } diff --git a/src/components/chat-container/chat-container.tsx b/src/components/chat-container/chat-container.tsx index 58620bd..b70b58e 100644 --- a/src/components/chat-container/chat-container.tsx +++ b/src/components/chat-container/chat-container.tsx @@ -15,7 +15,7 @@ import { AIChatConst } from '../../constants'; import { IChatToolbarItem, IChatContainerOptions } from '../../interface'; import { ChatTopics } from '../chat-topics/chat-topics'; import { ChatToolbar } from '../chat-toolbar/chat-toolbar'; -import { ChatMinimize } from '../chart-minimize/chart-minimize'; +import { ChatMinimize } from '../chat-minimize/chat-minimize'; import './chat-container.scss'; export interface ChatContainerProps { @@ -582,7 +582,10 @@ export class ChatContainer extends Component< 0, + )}`} controller={this.props.aiChat} items={this.props.footerToolbarItems} /> @@ -605,7 +608,10 @@ export class ChatContainer extends Component< 0, + )}`} controller={this.props.aiChat} items={this.props.footerToolbarItems} /> diff --git a/src/components/chart-minimize/chart-minimize.scss b/src/components/chat-minimize/chat-minimize.scss similarity index 98% rename from src/components/chart-minimize/chart-minimize.scss rename to src/components/chat-minimize/chat-minimize.scss index 163e160..bd37fd4 100644 --- a/src/components/chart-minimize/chart-minimize.scss +++ b/src/components/chat-minimize/chat-minimize.scss @@ -1,4 +1,4 @@ -@include b(chart-minimize) { +@include b(chat-minimize) { width: 56px; height: 56px; display: flex; diff --git a/src/components/chart-minimize/chart-minimize.tsx b/src/components/chat-minimize/chat-minimize.tsx similarity index 98% rename from src/components/chart-minimize/chart-minimize.tsx rename to src/components/chat-minimize/chat-minimize.tsx index 01c61d1..37d9f55 100644 --- a/src/components/chart-minimize/chart-minimize.tsx +++ b/src/components/chat-minimize/chat-minimize.tsx @@ -4,7 +4,7 @@ import { Namespace, isWithinBounds, limitDraggable } from '../../utils'; import { AiChatController } from '../../controller'; import { AIChatConst } from '../../constants'; import { AISvg } from '../../icons'; -import './chart-minimize.scss'; +import './chat-minimize.scss'; export interface ChatMinimizeProps { /** @@ -36,7 +36,7 @@ export interface ChatMinimizeProps { onClick: () => void; } -const ns = new Namespace('chart-minimize'); +const ns = new Namespace('chat-minimize'); export const ChatMinimize = (props: ChatMinimizeProps) => { const ref = useRef(null); diff --git a/src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.scss b/src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.scss new file mode 100644 index 0000000..197d127 --- /dev/null +++ b/src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.scss @@ -0,0 +1,164 @@ +@include b(chat-toolbar-item) { + display: flex; + cursor: pointer; + position: relative; + align-items: center; + white-space: nowrap; + + @include e(content) { + display: flex; + align-items: center; + white-space: nowrap; + + @include m(icon) { + width: 18px; + height: 18px; + font-size: 18px; + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + } + + @include m(label) { + display: flex; + align-items: center; + } + } + + @include e(more) { + display: flex; + align-items: center; + + @include m(icon) { + font-size: 18px; + } + } +} + +@include b(chat-toolbar-item-default) { + font-size: 14px; + width: fit-content; + + .#{bem(chat-toolbar-item, content)} { + gap: 6px; + line-height: 28px; + padding: 2px 14px; + border-radius: 12px; + color: #{getCssVar('ai-chat', 'color-2')}; + background-color: #{getCssVar('ai-chat', 'background-color-2')}; + } + + @include when(disabled) { + cursor: not-allowed; + .#{bem(chat-toolbar-item, content)}, + .#{bem(chat-toolbar-item, more)} { + background-color: #{getCssVar('ai-chat', 'disabled-color')}; + } + } + + &:not(.is-disabled) { + .#{bem(chat-toolbar-item, content)}, + .#{bem(chat-toolbar-item, more)} { + &:hover { + background-color: #{getCssVar('ai-chat', 'hover-background-color-2')}; + } + } + } + + @include when(more) { + .#{bem(chat-toolbar-item, content)} { + padding: 2px 8px 2px 14px; + border-radius: 12px 0 0 12px; + } + } + + .#{bem(chat-toolbar-item, more)} { + height: 32px; + padding: 2px 14px 2px 8px; + border-radius: 0 12px 12px 0; + border-left: 1px solid #{getCssVar('ai-chat', 'border-color')}; + background-color: #{getCssVar('ai-chat', 'background-color-2')}; + } +} + +@include b(chat-toolbar-item-circle) { + height: 32px; + padding: 6px; + max-width: 32px; + overflow: hidden; + font-size: 12px; + white-space: nowrap; + border-radius: 15px; + transition: max-width 0.8s ease; + border: 1px solid #{getCssVar('ai-chat', 'border-color')}; + + @include when(disabled) { + cursor: not-allowed; + color: #{getCssVar('ai-chat', 'disabled-color')}; + } + + &:hover:not(.is-disabled) { + max-width: 200px; + color: #{getCssVar('ai-chat', 'hover-color')}; + background-color: #{getCssVar('ai-chat', 'hover-background-color')}; + } + + .#{bem(chat-toolbar-item, content, label)} { + font-size: 14px; + margin-left: 8px; + width: calc(100% - 32px); + } +} + +@include b(chat-toolbar-item-dropdown) { + right: 0; + top: 36px; + gap: 4px; + padding: 4px; + display: flex; + max-width: 200px; + border-radius: 6px; + position: absolute; + flex-direction: column; + background-color: #{getCssVar('ai-chat', 'background-color-2')}; + + @include e(item) { + gap: 6px; + display: flex; + padding: 0 8px; + cursor: pointer; + line-height: 32px; + border-radius: 6px; + align-items: center; + + @include when(disabled) { + cursor: not-allowed; + background-color: #{getCssVar('ai-chat', 'disabled-color')}; + } + + &:hover:not(.is-disabled) { + background-color: #{getCssVar('ai-chat', 'hover-background-color-2')}; + } + + @include m(icon) { + width: 18px; + height: 18px; + font-size: 18px; + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + } + + @include m(label) { + min-width: 0; + flex-grow: 1; + display: flex; + overflow: hidden; + white-space: nowrap; + align-items: center; + text-overflow: ellipsis; + } + } +} diff --git a/src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.tsx b/src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.tsx new file mode 100644 index 0000000..240f88a --- /dev/null +++ b/src/components/chat-toolbar/chat-toolbar-item/chat-toolbar-item.tsx @@ -0,0 +1,208 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useEffect, useRef, useState } from 'preact/hooks'; +import { IChatToolbarItem } from '../../../interface'; +import { Namespace, isSvg } from '../../../utils'; +import './chat-toolbar-item.scss'; + +export interface ChatToolberItemProps { + /** + * 工具栏项模型 + * + * @type {IChatToolbarItem} + * @memberof ChatToolberItemProps + */ + model: IChatToolbarItem; + /** + * 业务数据 + * + * @type {*} + * @memberof ChatToolberItemProps + */ + data: any; + /** + * 是否禁用 + * + * @type {boolean} + * @memberof ChatToolberItemProps + */ + disabled?: boolean; + /** + * 类名 + * + * @type {string} + * @memberof ChatToolberItemProps + */ + className?: string; + /** + * 按钮类型 + * + * @type {('default' | 'circle')} + * @memberof ChatToolberItemProps + */ + buttonType?: 'default' | 'circle'; + /** + * 点击事件 + * + * @memberof ChatToolberItemProps + */ + onClick: (e: MouseEvent, item: IChatToolbarItem) => void; +} + +const ns = new Namespace('chat-toolbar-item'); + +export const ChatToolberItem = (props: ChatToolberItemProps) => { + const { + model, + data, + className, + disabled = false, + buttonType = 'default', + onClick, + } = props; + + const [isVisible, setIsVisible] = useState(false); + + const dropdownRef = useRef(null); + + /** + * 是否隐藏 + * + * @param {IChatToolbarItem} model + * @return {*} {boolean} + */ + const isHidden = (item: IChatToolbarItem): boolean => { + if (typeof item.hidden === 'function') return item.hidden(data); + return item.hidden === true; + }; + + // 隐藏不绘制 + if (isHidden(model)) return <>; + + /** + * 是否禁用 + * + * @param {IChatToolbarItem} item + * @return {*} {boolean} + */ + const isDisabled = (item: IChatToolbarItem): boolean => { + if (disabled) return true; + if (typeof item.disabled === 'function') return item.disabled(data); + return item.disabled === true; + }; + + /** + * 绘制图标 + * + * @param {IChatToolbarItem} item + * @return {*} + */ + const renderIcon = (item: IChatToolbarItem) => { + if (typeof item.icon === 'function') return item.icon(); + if (item.icon?.showIcon && item.icon?.cssClass) + return ; + if (item.icon?.showIcon && item.icon?.imagePath) { + if (isSvg(item.icon.imagePath)) + return ( +
+ ); + return ; + } + }; + + /** + * 打开更多 + * + * @param {MouseEvent} _event + */ + const openMore = (_event: MouseEvent) => { + if (isDisabled(model)) return; + setIsVisible(true); + }; + + /** + * 处理项点击 + * + * @param {MouseEvent} e + * @param {IChatToolbarItem} item + */ + const handleItemClick = (e: MouseEvent, item: IChatToolbarItem) => { + if (isDisabled(item)) return; + setIsVisible(false); + onClick(e, item); + }; + + useEffect(() => { + // 监听外部点击 + const handleClickOutside = (event: MouseEvent) => { + if ( + dropdownRef.current && + dropdownRef.current.contains(event.target as Node) + ) + return; + setIsVisible(false); + }; + + if (isVisible) { + document.addEventListener('mousedown', handleClickOutside); + } + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isVisible]); + + return ( +
+
handleItemClick(e, model)} + > +
{renderIcon(model)}
+
{model.label}
+
+ {model.children?.length && ( +
+