From cd26aafaf221d689b1bcc46ec5da10884ffb8387 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 12:54:30 +0000
Subject: [PATCH 1/7] Initial plan
From 037c9254eb16e6bd02912dad99f72bc061130148 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 13:00:12 +0000
Subject: [PATCH 2/7] Fix popover positioning when trigger element has fixed
position
Co-authored-by: Wesley-0808 <69622989+Wesley-0808@users.noreply.github.com>
---
packages/components/popover/popover.ts | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/packages/components/popover/popover.ts b/packages/components/popover/popover.ts
index 3d5f38f0b..db962f502 100644
--- a/packages/components/popover/popover.ts
+++ b/packages/components/popover/popover.ts
@@ -215,10 +215,11 @@ export default class Popover extends SuperComponent {
const query = this.createSelectorQuery();
query.select(`#${name}-wrapper`).boundingClientRect();
query.select(`#${name}-content`).boundingClientRect();
+ query.select(`#${name}-wrapper`).fields({ computedStyle: ['position'] });
query.selectViewport().scrollOffset();
query.exec((res) => {
- const [triggerRect, contentRect, viewportOffset] = res;
+ const [triggerRect, contentRect, triggerStyle, viewportOffset] = res;
if (!triggerRect || !contentRect) return;
// 最终放置位置
@@ -227,8 +228,10 @@ export default class Popover extends SuperComponent {
this.setData({ _placement: finalPlacement });
const { scrollTop = 0, scrollLeft = 0 } = viewportOffset;
- const top = basePos.top + scrollTop;
- const left = basePos.left + scrollLeft;
+ // 如果触发元素是 fixed 定位,不需要加上滚动偏移量
+ const isFixed = triggerStyle?.position === 'fixed';
+ const top = isFixed ? basePos.top : basePos.top + scrollTop;
+ const left = isFixed ? basePos.left : basePos.left + scrollLeft;
const style = `top:${Math.max(top, 0)}px;left:${Math.max(left, 0)}px;`;
const arrowStyle = this.calcArrowStyle(_placement, triggerRect, contentRect);
From 50ae271f05d89f1919d027b49eac4f4649afd576 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 13:01:36 +0000
Subject: [PATCH 3/7] Add comment to document query result order
Co-authored-by: Wesley-0808 <69622989+Wesley-0808@users.noreply.github.com>
---
packages/components/popover/popover.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/components/popover/popover.ts b/packages/components/popover/popover.ts
index db962f502..1ec0d3dc7 100644
--- a/packages/components/popover/popover.ts
+++ b/packages/components/popover/popover.ts
@@ -219,6 +219,7 @@ export default class Popover extends SuperComponent {
query.selectViewport().scrollOffset();
query.exec((res) => {
+ // res 数组顺序: [triggerRect, contentRect, triggerStyle, viewportOffset]
const [triggerRect, contentRect, triggerStyle, viewportOffset] = res;
if (!triggerRect || !contentRect) return;
From 16ab7b34b168c0418b34837f7fb31502290d423d Mon Sep 17 00:00:00 2001
From: Wesley <985189328@qq.com>
Date: Thu, 11 Dec 2025 15:26:16 +0800
Subject: [PATCH 4/7] feat: add fixed
---
packages/components/popover/README.en-US.md | 4 ++--
packages/components/popover/README.md | 26 +++++++++++++++++----
packages/components/popover/popover.less | 4 ++++
packages/components/popover/popover.wxml | 2 +-
packages/components/popover/props.ts | 5 ++++
packages/components/popover/type.ts | 8 +++++++
6 files changed, 41 insertions(+), 8 deletions(-)
diff --git a/packages/components/popover/README.en-US.md b/packages/components/popover/README.en-US.md
index 6fbbc2ca3..6dd7c6592 100644
--- a/packages/components/popover/README.en-US.md
+++ b/packages/components/popover/README.en-US.md
@@ -2,7 +2,6 @@
## API
-
### Popover Props
name | type | default | description | required
@@ -11,6 +10,7 @@ style | Object | - | CSS(Cascading Style Sheets) | N
custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N
close-on-click-outside | Boolean | true | \- | N
content | String | - | \- | N
+fixed | Boolean | false | \- | N
placement | String | top | options: top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
show-arrow | Boolean | true | \- | N
theme | String | dark | options: dark/light/brand/success/warning/error | N
@@ -41,4 +41,4 @@ t-class-content | \-
The component provides the following CSS variables, which can be used to customize styles.
Name | Default Value | Description
-- | -- | --
---td-popover-padding | 24rpx | -
+--td-popover-padding | 24rpx | -
diff --git a/packages/components/popover/README.md b/packages/components/popover/README.md
index 37aaae18e..6722de89f 100644
--- a/packages/components/popover/README.md
+++ b/packages/components/popover/README.md
@@ -25,12 +25,27 @@ isComponent: true
### 组件类型
-带箭头的弹出气泡
+#### 带箭头的弹出气泡
{{ base }}
-## API
+### 组件样式
+
+{{ theme }}
+
+{{ placement }}
+
+## FAQ
+如果使用场景为 `fixed`,除了需要显示指定 `fixed` 属性为 `true`,还需在触发元素的顶层添加`t-popover-wrapper--fixed` 类,用于定位触发元素。
+
+```html
+
+
+
+```
+
+## API
### Popover Props
@@ -40,6 +55,7 @@ style | Object | - | 样式 | N
custom-style | Object | - | 样式,一般用于开启虚拟化组件节点场景 | N
close-on-click-outside | Boolean | true | 是否在点击外部元素后关闭菜单 | N
content | String | - | 确认框内容 | N
+fixed | Boolean | false | 如果 popover 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true | N
placement | String | top | 浮层出现位置。可选项:top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
show-arrow | Boolean | true | 是否显示浮层箭头 | N
theme | String | dark | 弹出气泡主题。可选项:dark/light/brand/success/warning/error | N
@@ -55,8 +71,8 @@ visible-change | `(visible: boolean)` | 确认框显示或隐藏时触发
名称 | 描述
-- | --
-\- | 自定义 `` 显示内容
-content \| 自定义 `content` 显示内容
+\- | 默认插槽,作用同 `content` 插槽
+content | 自定义 `content` 显示内容
### Popover External Classes
@@ -70,4 +86,4 @@ t-class-content | 内容样式类
组件提供了下列 CSS 变量,可用于自定义样式。
名称 | 默认值 | 描述
-- | -- | --
---td-popover-padding | 24rpx | -
\ No newline at end of file
+--td-popover-padding | 24rpx | -
diff --git a/packages/components/popover/popover.less b/packages/components/popover/popover.less
index 3a8fb3370..40e474764 100644
--- a/packages/components/popover/popover.less
+++ b/packages/components/popover/popover.less
@@ -32,6 +32,10 @@
overflow: visible;
transition: 0.2s ease-in-out all;
+ &--fixed {
+ position: fixed;
+ }
+
&__content {
position: relative;
padding: @popover-padding;
diff --git a/packages/components/popover/popover.wxml b/packages/components/popover/popover.wxml
index 9651c2c13..cac5a49c7 100644
--- a/packages/components/popover/popover.wxml
+++ b/packages/components/popover/popover.wxml
@@ -16,7 +16,7 @@
wx:if="{{realVisible}}"
id="{{classPrefix}}-content"
style="{{style}} {{contentStyle}} {{customStyle}}"
- class="{{class}} {{classPrefix}} {{transitionClass}} {{prefix}}-class"
+ class="{{class}} {{classPrefix}} {{transitionClass}} {{prefix}}-class {{fixed ? classPrefix + '--fixed' : ''}}"
data-placement="{{_placement}}"
>
Date: Thu, 11 Dec 2025 15:27:17 +0800
Subject: [PATCH 5/7] fix: trigger fixed
---
packages/components/popover/popover.ts | 120 ++++++++++++++-----------
1 file changed, 67 insertions(+), 53 deletions(-)
diff --git a/packages/components/popover/popover.ts b/packages/components/popover/popover.ts
index 1ec0d3dc7..951ae2cf5 100644
--- a/packages/components/popover/popover.ts
+++ b/packages/components/popover/popover.ts
@@ -161,50 +161,59 @@ export default class Popover extends SuperComponent {
return start + triggerSize / 2 - contentSize / 2;
},
- calcPlacement(placement: string, triggerRect: any, contentRect: any) {
- const { isHorizontal, isVertical } = this.getToward(placement);
- // 获取内容大小
- const { width: contentWidth, height: contentHeight } = contentRect;
- // 获取所在位置
- const { left: triggerLeft, top: triggerTop, right: triggerRight, bottom: triggerBottom } = triggerRect;
- // 是否能正常放置
- let canPlace = true;
- const { windowWidth, windowHeight } = getWindowInfo();
- let finalPlacement = placement;
-
- if (isHorizontal) {
- if (placement.startsWith('top')) {
- canPlace = triggerTop - contentHeight >= 0;
- } else if (placement.startsWith('bottom')) {
- canPlace = triggerBottom + contentHeight <= windowHeight;
- }
- } else if (isVertical) {
- if (placement.startsWith('left')) {
- canPlace = triggerLeft - contentWidth >= 0;
- } else if (placement.startsWith('right')) {
- canPlace = triggerRight + contentWidth <= windowWidth;
- }
- }
-
- if (!canPlace) {
- // 反向
- if (isHorizontal) {
- finalPlacement = placement.startsWith('top')
- ? placement.replace('top', 'bottom')
- : placement.replace('bottom', 'top');
- } else if (isVertical) {
- finalPlacement = placement.startsWith('left')
- ? placement.replace('left', 'right')
- : placement.replace('right', 'left');
- }
- }
-
- const basePos = this.calcContentPosition(finalPlacement, triggerRect, contentRect);
-
- return {
- placement: finalPlacement,
- ...basePos,
- };
+ calcPlacement(isFixed: boolean, placement: string, triggerRect: any, contentRect: any) {
+ return new Promise<{ placement: string; top: number; left: number }>((resolve) => {
+ // 选取当前组件节点所在的组件实例,以支持 fixed 定位的元素计算位置
+ const owner = this.selectOwnerComponent().createSelectorQuery();
+ owner.select(`.${name}-wrapper--fixed`).boundingClientRect();
+ owner.exec((b) => {
+ const [triggerChildRect] = b;
+ if (triggerChildRect && isFixed) {
+ triggerRect = triggerChildRect;
+ }
+
+ const { isHorizontal, isVertical } = this.getToward(placement);
+ // 获取内容大小
+ const { width: contentWidth, height: contentHeight } = contentRect;
+ // 获取所在位置
+ const { left: triggerLeft, top: triggerTop, right: triggerRight, bottom: triggerBottom } = triggerRect;
+ // 是否能正常放置
+ let canPlace = true;
+ const { windowWidth, windowHeight } = getWindowInfo();
+ let finalPlacement = placement;
+
+ if (isHorizontal) {
+ if (placement.startsWith('top')) {
+ canPlace = triggerTop - contentHeight >= 0;
+ } else if (placement.startsWith('bottom')) {
+ canPlace = triggerBottom + contentHeight <= windowHeight;
+ }
+ } else if (isVertical) {
+ if (placement.startsWith('left')) {
+ canPlace = triggerLeft - contentWidth >= 0;
+ } else if (placement.startsWith('right')) {
+ canPlace = triggerRight + contentWidth <= windowWidth;
+ }
+ }
+
+ if (!canPlace) {
+ // 反向
+ if (isHorizontal) {
+ finalPlacement = placement.startsWith('top')
+ ? placement.replace('top', 'bottom')
+ : placement.replace('bottom', 'top');
+ } else if (isVertical) {
+ finalPlacement = placement.startsWith('left')
+ ? placement.replace('left', 'right')
+ : placement.replace('right', 'left');
+ }
+ }
+
+ const basePos = this.calcContentPosition(finalPlacement, triggerRect, contentRect);
+
+ resolve({ placement: finalPlacement, ...basePos });
+ });
+ });
},
async computePosition() {
@@ -215,22 +224,27 @@ export default class Popover extends SuperComponent {
const query = this.createSelectorQuery();
query.select(`#${name}-wrapper`).boundingClientRect();
query.select(`#${name}-content`).boundingClientRect();
- query.select(`#${name}-wrapper`).fields({ computedStyle: ['position'] });
query.selectViewport().scrollOffset();
- query.exec((res) => {
- // res 数组顺序: [triggerRect, contentRect, triggerStyle, viewportOffset]
- const [triggerRect, contentRect, triggerStyle, viewportOffset] = res;
+ query.exec(async (res) => {
+ const [triggerRect, contentRect, viewportOffset] = res;
if (!triggerRect || !contentRect) return;
+ // 如果 fixed 定位,不需要加上滚动偏移量
+ const isFixed = this.properties.fixed;
// 最终放置位置
- const { placement: finalPlacement, ...basePos } = this.calcPlacement(_placement, triggerRect, contentRect);
- // TODO 优化:滚动时可能导致箭头闪烁
+ const { placement: finalPlacement, ...basePos } = await this.calcPlacement(
+ isFixed,
+ _placement,
+ triggerRect,
+ contentRect,
+ );
+
+ // TODO 优化:滚动时切换placement可能导致箭头闪烁
this.setData({ _placement: finalPlacement });
- const { scrollTop = 0, scrollLeft = 0 } = viewportOffset;
- // 如果触发元素是 fixed 定位,不需要加上滚动偏移量
- const isFixed = triggerStyle?.position === 'fixed';
+ const { scrollTop = 0, scrollLeft = 0 } = viewportOffset || {};
+
const top = isFixed ? basePos.top : basePos.top + scrollTop;
const left = isFixed ? basePos.left : basePos.left + scrollLeft;
From 389bd745eb50425b3dc9dfabdcda35395ce62915 Mon Sep 17 00:00:00 2001
From: Wesley <985189328@qq.com>
Date: Thu, 11 Dec 2025 15:40:55 +0800
Subject: [PATCH 6/7] chore: docs
---
packages/components/popover/README.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/packages/components/popover/README.md b/packages/components/popover/README.md
index 6722de89f..5e554b4b8 100644
--- a/packages/components/popover/README.md
+++ b/packages/components/popover/README.md
@@ -31,8 +31,10 @@ isComponent: true
### 组件样式
+#### 气泡主题
{{ theme }}
+#### 气泡位置
{{ placement }}
## FAQ
@@ -40,9 +42,9 @@ isComponent: true
如果使用场景为 `fixed`,除了需要显示指定 `fixed` 属性为 `true`,还需在触发元素的顶层添加`t-popover-wrapper--fixed` 类,用于定位触发元素。
```html
-
-
-
+
+
+
```
## API
From e6beedb7ceeebea86f5d68fd357cb0e67037d3ca Mon Sep 17 00:00:00 2001
From: anlyyao
Date: Thu, 25 Dec 2025 16:01:11 +0800
Subject: [PATCH 7/7] docs(Popover): update fixed attribute description
---
packages/components/popover/README.en-US.md | 2 +-
packages/components/popover/README.md | 13 ++-----------
packages/components/popover/props.ts | 2 +-
packages/components/popover/type.ts | 2 +-
4 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/packages/components/popover/README.en-US.md b/packages/components/popover/README.en-US.md
index 6dd7c6592..afa51941c 100644
--- a/packages/components/popover/README.en-US.md
+++ b/packages/components/popover/README.en-US.md
@@ -10,7 +10,7 @@ style | Object | - | CSS(Cascading Style Sheets) | N
custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N
close-on-click-outside | Boolean | true | \- | N
content | String | - | \- | N
-fixed | Boolean | false | \- | N
+fixed | Boolean | false | `1.12.1` | N
placement | String | top | options: top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
show-arrow | Boolean | true | \- | N
theme | String | dark | options: dark/light/brand/success/warning/error | N
diff --git a/packages/components/popover/README.md b/packages/components/popover/README.md
index 5e554b4b8..a70ff1034 100644
--- a/packages/components/popover/README.md
+++ b/packages/components/popover/README.md
@@ -37,15 +37,6 @@ isComponent: true
#### 气泡位置
{{ placement }}
-## FAQ
-
-如果使用场景为 `fixed`,除了需要显示指定 `fixed` 属性为 `true`,还需在触发元素的顶层添加`t-popover-wrapper--fixed` 类,用于定位触发元素。
-
-```html
-
-
-
-```
## API
@@ -57,7 +48,7 @@ style | Object | - | 样式 | N
custom-style | Object | - | 样式,一般用于开启虚拟化组件节点场景 | N
close-on-click-outside | Boolean | true | 是否在点击外部元素后关闭菜单 | N
content | String | - | 确认框内容 | N
-fixed | Boolean | false | 如果 popover 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true | N
+fixed | Boolean | false | `1.12.1`。如果触发元素为 `fixed` 场景,需要显示指定 `fixed` 属性为 `true`,同时需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素 | N
placement | String | top | 浮层出现位置。可选项:top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
show-arrow | Boolean | true | 是否显示浮层箭头 | N
theme | String | dark | 弹出气泡主题。可选项:dark/light/brand/success/warning/error | N
@@ -73,7 +64,7 @@ visible-change | `(visible: boolean)` | 确认框显示或隐藏时触发
名称 | 描述
-- | --
-\- | 默认插槽,作用同 `content` 插槽
+\- | 默认插槽,用于自定义触发元素
content | 自定义 `content` 显示内容
### Popover External Classes
diff --git a/packages/components/popover/props.ts b/packages/components/popover/props.ts
index dc3ca3db4..43360c960 100644
--- a/packages/components/popover/props.ts
+++ b/packages/components/popover/props.ts
@@ -15,7 +15,7 @@ const props: TdPopoverProps = {
content: {
type: String,
},
- /** 如果 popover 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true */
+ /** 如果触发元素为 `fixed` 场景,需要显示指定 `fixed` 属性为 `true`,同时需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素 */
fixed: {
type: Boolean,
value: false,
diff --git a/packages/components/popover/type.ts b/packages/components/popover/type.ts
index 1c7c723f1..f70400f14 100644
--- a/packages/components/popover/type.ts
+++ b/packages/components/popover/type.ts
@@ -21,7 +21,7 @@ export interface TdPopoverProps {
value?: string;
};
/**
- * 如果 popover 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true
+ * 如果触发元素为 `fixed` 场景,需要显示指定 `fixed` 属性为 `true`,同时需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素
* @default false
*/
fixed?: {