From fca7be57ce06197d8ae5db749dce89a7441a04e7 Mon Sep 17 00:00:00 2001 From: ShineKOT <1917095344@qq.com> Date: Wed, 19 Nov 2025 21:30:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E8=A1=8C=E5=86=85?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locale/en/index.ts | 1 + src/locale/zh-CN/index.ts | 1 + .../inline-ai-textarea.hook.ts | 75 +++++++++--------- .../inline-ai-textarea.scss | 18 +++-- .../inline-ai-textarea/inline-ai-textarea.tsx | 77 ++++++++++++------- 5 files changed, 103 insertions(+), 69 deletions(-) diff --git a/src/locale/en/index.ts b/src/locale/en/index.ts index 18b1acf60..1c85a4d5a 100644 --- a/src/locale/en/index.ts +++ b/src/locale/en/index.ts @@ -898,6 +898,7 @@ export default { replaceText: 'Replace Text', copyText: 'Copy Text', info: 'The content is generated by AI, please carefully discern.', + stopEdit: 'Terminate editing', }, }, // runTime diff --git a/src/locale/zh-CN/index.ts b/src/locale/zh-CN/index.ts index e8524ab5b..e0d52c43d 100644 --- a/src/locale/zh-CN/index.ts +++ b/src/locale/zh-CN/index.ts @@ -842,6 +842,7 @@ export default { replaceText: '替换文本', copyText: '复制文本', info: '内容由 AI 生成,请仔细甄别。', + stopEdit: '终止编辑', }, }, // runTime diff --git a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.hook.ts b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.hook.ts index 2ed0adb60..dac63e3ba 100644 --- a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.hook.ts +++ b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.hook.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ref, Ref, computed, onMounted, onUnmounted, CSSProperties } from 'vue'; +import { ref, Ref, onMounted, onUnmounted, CSSProperties } from 'vue'; import { IAppDEACMode, IAppDataEntity } from '@ibiz/model-core'; -import { calcResPath } from '@ibiz-template/runtime'; +import { calcResPath, IInLineAiChatOptions } from '@ibiz-template/runtime'; import { CancelIcon, CopyTextIcon, @@ -34,32 +34,6 @@ export const computedInLineAIParams = ( }; }; -/** - * @description 计算动态样式 - * @returns { containerDynaStyle: IData; contentDynaStyle: IData } containerDynaStyle: 容器动态样式 contentDynaStyle: 内容动态样式 - */ -export const computedInLineAIDynaStyle = ( - props: IData, -): { containerDynaStyle: IData; contentDynaStyle: IData } => { - const containerDynaStyle = computed(() => { - return { - width: `${props.options.width}px`, - left: `${props.options.left}px`, - top: `${props.options.top}px`, - }; - }); - const contentDynaStyle = computed(() => { - return { - height: `${props.options.height || 80}px`, - 'max-height': `${props.options.maxHeight}px`, - }; - }); - return { - containerDynaStyle, - contentDynaStyle, - }; -}; - /** * @description 监听点击事件 */ @@ -212,15 +186,20 @@ export interface IActionItem { } /** - * 使用行为 + * 使用基础能力 * @returns */ -export const useActions = ( +export const useBase = ( + props: { + options: IInLineAiChatOptions; + }, container: Ref, target: Ref, ): { actions: IActionItem[]; actionStyle: Ref; + containerStyle: Ref; + contentStyle: Ref; } => { const actions: IActionItem[] = [ { @@ -258,12 +237,32 @@ export const useActions = ( }, ]; + /** + * 行为组样式 + */ const actionStyle = ref({}); /** - * @description 计算行为窗样式 + * 容器样式 + */ + const containerStyle = ref({ + width: `${props.options.width}px`, + left: `${props.options.left}px`, + top: `${props.options.top}px`, + }); + + /** + * 内容样式 + */ + const contentStyle = ref({ + height: `${props.options.height || 80}px`, + 'max-height': `${props.options.maxHeight}px`, + }); + + /** + * @description 计算样式 */ - const calcActionStyle = (isInit: boolean = false) => { + const calcStyle = (isInit: boolean = false) => { if (!container.value || !target.value) return; const containerRect = container.value.getBoundingClientRect(); const targetHeight = target.value.offsetHeight; @@ -283,16 +282,18 @@ export const useActions = ( } }; - const onResize = () => calcActionStyle(); + const updatePosition = () => calcStyle(); onMounted(() => { - calcActionStyle(true); - window.addEventListener('resize', onResize); + calcStyle(true); + // window.addEventListener('scroll', updatePosition, { passive: true }); + window.addEventListener('resize', updatePosition, { passive: true }); }); onUnmounted(() => { - window.removeEventListener('resize', onResize); + // window.removeEventListener('scroll', updatePosition); + window.removeEventListener('resize', updatePosition); }); - return { actions, actionStyle }; + return { actions, actionStyle, containerStyle, contentStyle }; }; diff --git a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss index 64fc5a39c..a0701453c 100644 --- a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss +++ b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.scss @@ -44,14 +44,20 @@ $inline-ai-context-menu: ( color: getCssVar(color, disabled, text); } @include m(textarea) { + position: relative; flex-grow: 1; - padding: 0; - resize: none; - border: none; - outline: none; - &:disabled { - background-color: getCssVar(color, bg-1); + textarea { + width: 100%; + height: 100%; + padding: 0; + resize: none; + border: none; + outline: none; + + &:disabled { + background-color: getCssVar(color, bg-1); + } } } @include m(suffix) { diff --git a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.tsx b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.tsx index 4d2c6db08..0c1bf1422 100644 --- a/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.tsx +++ b/src/util/inline-ai-util/inline-ai-textarea/inline-ai-textarea.tsx @@ -1,14 +1,13 @@ /* eslint-disable no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { defineComponent, PropType, ref } from 'vue'; +import { defineComponent, computed, onMounted, PropType, ref } from 'vue'; import { IAppDEACMode } from '@ibiz/model-core'; import { useNamespace } from '@ibiz-template/vue3-util'; import { IInLineAiChatOptions } from '@ibiz-template/runtime'; import { useAI, - useActions, + useBase, computedInLineAIParams, - computedInLineAIDynaStyle, useInLineAIContainerClick, } from './inline-ai-textarea.hook'; import { AIIcon, SendIcon, StopIcon } from './icon'; @@ -68,9 +67,11 @@ export const InlineAITextArea = defineComponent({ const { srfaiappendcurdata, srfaiautoappend, srfmode } = computedInLineAIParams(props); - // 动态样式 - const { containerDynaStyle, contentDynaStyle } = - computedInLineAIDynaStyle(props); + const { actions, actionStyle, containerStyle, contentStyle } = useBase( + props, + containerRef, + actionsRef, + ); // 处理点击事件 useInLineAIContainerClick(props); @@ -79,9 +80,6 @@ export const InlineAITextArea = defineComponent({ srfaiappendcurdata, srfmode, }); - - const { actions, actionStyle } = useActions(containerRef, actionsRef); - /** * 多行文本框内容 */ @@ -98,6 +96,15 @@ export const InlineAITextArea = defineComponent({ * 是否在加载状态 */ const isLoading = ref(false); + + /** + * 编辑器是否禁用状态 + */ + const disabled = computed(() => { + // 助手回答或者加载状态中 + return contentType.value === 'ASSISTANT' || isLoading.value; + }); + /** * @description 发送问题 * @returns {*} {Promise} @@ -149,6 +156,7 @@ export const InlineAITextArea = defineComponent({ break; case 'replaceText': props.replaceSelectionText(content); + props.unMountAIChat(); break; case 'copyText': ibiz.util.text.copy(content); @@ -162,17 +170,22 @@ export const InlineAITextArea = defineComponent({ } }; + onMounted(() => { + if (srfaiautoappend) sendQuestion(textareaContent.value); + }); + return { ns, actions, isLoading, actionsRef, + disabled, actionStyle, contentType, containerRef, + contentStyle, + containerStyle, textareaContent, - contentDynaStyle, - containerDynaStyle, onKeydown, sendQuestion, stopQuestion, @@ -181,24 +194,35 @@ export const InlineAITextArea = defineComponent({ }, render() { return ( -
-
- {this.contentType === 'USER' && ( +
+
+ {!this.disabled && (
{AIIcon}
)} -