You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 09:41:00