diff --git a/docs/demos/sender/actions-config-basic.vue b/docs/demos/sender/actions-config-basic.vue
index 231e368e6..0407d6ce8 100644
--- a/docs/demos/sender/actions-config-basic.vue
+++ b/docs/demos/sender/actions-config-basic.vue
@@ -49,10 +49,3 @@ const handleSubmit = (text: string) => {
color: #606266;
}
-
-
diff --git a/docs/src/components/sender.md b/docs/src/components/sender.md
index e78fb9d9c..cf98caf50 100644
--- a/docs/src/components/sender.md
+++ b/docs/src/components/sender.md
@@ -724,11 +724,16 @@ Sender 组件提供了丰富的 CSS 变量用于自定义样式。
| -------------------------------- | ------------ |
| `--tr-sender-button-size` | 按钮尺寸 |
| `--tr-sender-button-size-submit` | 提交按钮尺寸 |
+| `--tr-sender-tooltip-gap` | 按钮 Tooltip 与触发按钮的间距 |
:::tip 尺寸变体
所有变量都支持通过 `size` 属性自动切换。当 `size="small"` 时,组件会使用对应的 `-small` 变体(如 `--tr-sender-font-size-small`)。
:::
+:::tip Tooltip 间距
+Sender 按钮的 Tooltip 间距可通过 `--tr-sender-tooltip-gap` 全局自定义,默认值为 `8px`。由于 Tooltip 弹层通常挂载在全局层级,建议在 `:root` 或全局主题作用域下设置该变量。
+:::
+
:::tip 使用示例
```css
/* 自定义背景色 */
@@ -742,6 +747,11 @@ Sender 组件提供了丰富的 CSS 变量用于自定义样式。
--tr-sender-button-size: 40px;
--tr-sender-button-size-submit: 44px;
}
+
+/* 全局调整 Sender 按钮 Tooltip 间距 */
+:root {
+ --tr-sender-tooltip-gap: 10px;
+}
```
:::
@@ -789,4 +799,4 @@ Sender 组件提供了丰富的 CSS 变量用于自定义样式。
|-------|----------|
| startSpeech | 使用 `VoiceButton.start()` |
| stopSpeech | 使用 `VoiceButton.stop()` |
-| activateTemplateFirstField | 自动处理,无需调用 |
\ No newline at end of file
+| activateTemplateFirstField | 自动处理,无需调用 |
diff --git a/packages/components/src/sender-actions/action-button/index.vue b/packages/components/src/sender-actions/action-button/index.vue
index 4e1eca529..94673b216 100644
--- a/packages/components/src/sender-actions/action-button/index.vue
+++ b/packages/components/src/sender-actions/action-button/index.vue
@@ -86,3 +86,11 @@ const sizeStyle = computed(() => {
}
}
+
+
diff --git a/packages/components/src/sender-actions/clear-button/index.vue b/packages/components/src/sender-actions/clear-button/index.vue
index a5394a63c..a4c41585b 100644
--- a/packages/components/src/sender-actions/clear-button/index.vue
+++ b/packages/components/src/sender-actions/clear-button/index.vue
@@ -3,7 +3,6 @@ import { computed } from 'vue'
import { useSenderContext } from '../../sender/context'
import { IconClear } from '@opentiny/tiny-robot-svgs'
import ActionButton from '../action-button/index.vue'
-import { normalizeTooltipContent } from '../utils/tooltip'
// 从 Context 读取状态和配置
const { hasContent, clearable, clear, loading, defaultActions } = useSenderContext()
@@ -18,7 +17,7 @@ const isDisabled = computed(() => {
return false
})
-const tooltipRenderFn = computed(() => normalizeTooltipContent(defaultActions.value?.clear?.tooltip))
+const tooltip = computed(() => defaultActions.value?.clear?.tooltip)
const tooltipPlacement = computed(() => defaultActions.value?.clear?.tooltipPlacement ?? 'top')
@@ -46,7 +45,7 @@ const handleClick = () => {
v-if="show"
:icon="IconClear"
:disabled="isDisabled"
- :tooltip="tooltipRenderFn"
+ :tooltip="tooltip"
:tooltip-placement="tooltipPlacement"
@click="handleClick"
/>
diff --git a/packages/components/src/sender-actions/styles/tooltip.less b/packages/components/src/sender-actions/styles/tooltip.less
new file mode 100644
index 000000000..5eadb012a
--- /dev/null
+++ b/packages/components/src/sender-actions/styles/tooltip.less
@@ -0,0 +1,59 @@
+.tr-sender-tooltip-gap-mixin() {
+ &[data-popper-placement^='top'],
+ &[x-placement^='top'],
+ &[data-placement^='top'] {
+ padding-bottom: var(--tr-sender-tooltip-gap, 8px);
+ }
+
+ &[data-popper-placement^='bottom'],
+ &[x-placement^='bottom'],
+ &[data-placement^='bottom'] {
+ padding-top: var(--tr-sender-tooltip-gap, 8px);
+ }
+
+ &[data-popper-placement^='left'],
+ &[x-placement^='left'],
+ &[data-placement^='left'] {
+ padding-right: var(--tr-sender-tooltip-gap, 8px);
+ }
+
+ &[data-popper-placement^='right'],
+ &[x-placement^='right'],
+ &[data-placement^='right'] {
+ padding-left: var(--tr-sender-tooltip-gap, 8px);
+ }
+}
+
+.tr-sender-tooltip-shell-mixin() {
+ padding: 0;
+ background: transparent;
+ border-radius: 0;
+ box-shadow: none;
+}
+
+.tr-sender-tooltip-inner-base-mixin() {
+ display: block;
+ min-width: 10px;
+ max-width: 450px;
+ word-wrap: break-word;
+ white-space: pre-wrap;
+}
+
+.tr-sender-tooltip-light-inner-mixin() {
+ .tr-sender-tooltip-inner {
+ .tr-sender-tooltip-inner-base-mixin();
+ padding: var(--tv-Tooltip-padding-y) var(--tv-Tooltip-padding-x);
+ color: var(--tv-Tooltip-popper-light-text-color);
+ font-size: var(--tv-Tooltip-popper-font-size);
+ line-height: var(--tv-Tooltip-popper-font-line-height);
+ background: var(--tv-Tooltip-popper-light-bg-color);
+ border-radius: var(--tv-Tooltip-popper-border-radius);
+ box-shadow: var(--tv-Tooltip-box-shadow);
+ }
+}
+
+.tr-sender-tooltip-light-popper-mixin() {
+ .tr-sender-tooltip-shell-mixin();
+ .tr-sender-tooltip-gap-mixin();
+ .tr-sender-tooltip-light-inner-mixin();
+}
diff --git a/packages/components/src/sender-actions/submit-button/index.vue b/packages/components/src/sender-actions/submit-button/index.vue
index 22a346df1..4124a8aa8 100644
--- a/packages/components/src/sender-actions/submit-button/index.vue
+++ b/packages/components/src/sender-actions/submit-button/index.vue
@@ -148,15 +148,10 @@ const handleClick = () => {
diff --git a/packages/components/src/sender-actions/utils/tooltip.ts b/packages/components/src/sender-actions/utils/tooltip.ts
index 6325a2993..0e35b346d 100644
--- a/packages/components/src/sender-actions/utils/tooltip.ts
+++ b/packages/components/src/sender-actions/utils/tooltip.ts
@@ -1,5 +1,34 @@
+import { h, isVNode, type VNode } from 'vue'
import type { TooltipContent } from '../types/tooltip'
+const TOOLTIP_INNER_CLASS = 'tr-sender-tooltip-inner'
+
+function hasTooltipInnerClass(value: unknown): boolean {
+ if (!value) return false
+
+ if (typeof value === 'string') {
+ return value.split(/\s+/).includes(TOOLTIP_INNER_CLASS)
+ }
+
+ if (Array.isArray(value)) {
+ return value.some(hasTooltipInnerClass)
+ }
+
+ if (typeof value === 'object') {
+ return Boolean((value as Record)[TOOLTIP_INNER_CLASS])
+ }
+
+ return false
+}
+
+function renderTooltipInner(content: string | VNode) {
+ if (isVNode(content) && hasTooltipInnerClass(content.props?.class)) {
+ return content
+ }
+
+ return h('div', { class: TOOLTIP_INNER_CLASS }, content)
+}
+
/**
* 将 TooltipContent 转换为 TinyTooltip 的 render-content 函数
*/
@@ -7,8 +36,8 @@ export function normalizeTooltipContent(tooltip: TooltipContent | undefined) {
if (!tooltip) return undefined
if (typeof tooltip === 'string') {
- return () => tooltip
+ return () => renderTooltipInner(tooltip)
}
- return tooltip
+ return () => renderTooltipInner(tooltip())
}