能否调整Action.ShowCard弹出的自适应卡片在SPFx ACE中的显示位置?
在SPFx ACE中实现Action.ShowCard在按钮下方展开的解决方案
原生Adaptive Cards的Action.ShowCard组件默认会在父卡片底部展开子卡片,无法直接修改其定位逻辑。要在Viva Connections仪表板的SPFx ACE中实现子卡片在触发按钮下方展开,需要通过自定义按钮事件+动态渲染定位的方式替代原生组件,具体步骤如下:
1. 替换原生Action.ShowCard为Action.Execute
在自适应卡片JSON中,将原有的Action.ShowCard替换为Action.Execute,通过verb标识动作类型,同时传入子卡片的JSON payload:
{ "type": "AdaptiveCard", "version": "1.5", "body": [ // 父卡片原有内容 { "type": "ActionSet", "actions": [ { "type": "Action.Execute", "title": "展示子卡片", "verb": "showChildCard", "data": { "childCard": { "type": "AdaptiveCard", "version": "1.5", "body": [ { "type": "TextBlock", "text": "按钮下方的子卡片内容" } ] } } } ] } ] }
2. 在SPFx ACE中监听按钮点击并定位渲染子卡片
在ACE的渲染逻辑中,监听Action.Execute按钮的点击事件,获取按钮的DOM位置,动态创建子卡片容器并定位到按钮下方:
import { AdaptiveCard } from "@microsoft/adaptivecards-tools"; export default class YourAce extends BaseClientSideWebPart<IYourAceProps> { public render(): void { // 渲染父卡片 const parentCardHtml = AdaptiveCard.declare(require('./your-card-template.json')).render(this.state); this.domElement.innerHTML = `<div id="ace-container" style="position: relative;">${parentCardHtml}</div>`; // 绑定按钮点击事件 this.bindShowChildCardAction(); } private bindShowChildCardAction(): void { const aceContainer = this.domElement.querySelector('#ace-container') as HTMLElement; const executeButtons = aceContainer.querySelectorAll('.adaptive-card-action-button'); executeButtons.forEach(button => { button.addEventListener('click', (e) => { const targetBtn = e.target as HTMLElement; const verb = targetBtn.getAttribute('data-verb'); if (verb === 'showChildCard') { // 获取子卡片payload const data = JSON.parse(targetBtn.getAttribute('data-data') || '{}'); // 渲染子卡片到按钮下方 this.renderChildCard(targetBtn, data.childCard); } }); }); } private renderChildCard(triggerBtn: HTMLElement, childCardPayload: any): void { // 移除已存在的子卡片 const existingCard = this.domElement.querySelector('#child-card-container'); if (existingCard) existingCard.remove(); // 创建子卡片容器 const childCardContainer = document.createElement('div'); childCardContainer.id = 'child-card-container'; // 基于按钮位置计算容器定位 const btnRect = triggerBtn.getBoundingClientRect(); const containerRect = this.domElement.querySelector('#ace-container')!.getBoundingClientRect(); childCardContainer.style.position = 'absolute'; childCardContainer.style.top = `${btnRect.bottom - containerRect.top}px`; childCardContainer.style.left = `${btnRect.left - containerRect.left}px`; childCardContainer.style.zIndex = '10'; childCardContainer.style.backgroundColor = '#ffffff'; childCardContainer.style.border = '1px solid #e0e0e0'; childCardContainer.style.borderRadius = '8px'; childCardContainer.style.padding = '16px'; childCardContainer.style.boxShadow = '0 4px 12px rgba(0,0,0,0.08)'; // 渲染子卡片内容 const childCardHtml = AdaptiveCard.declare(childCardPayload).render({}); childCardContainer.innerHTML = childCardHtml; // 添加到ACE容器 this.domElement.querySelector('#ace-container')!.appendChild(childCardContainer); // 点击其他区域关闭子卡片 document.addEventListener('click', (e) => { if (!childCardContainer.contains(e.target as Node)) { childCardContainer.remove(); } }, { once: true }); } }
关键注意事项
- 确保ACE容器设置了
position: relative,这样子卡片的绝对定位会相对于容器而非整个页面 - 可以根据Viva Connections的设计规范调整子卡片的样式(阴影、边框、间距等)
- 点击其他区域关闭子卡片的逻辑避免了多个子卡片同时存在的问题
内容的提问来源于stack exchange,提问作者Andrew Gaskell
相关产品推荐
相关产品推荐

