我们可以从html解析的自适应卡中连接任务模块吗?

问题描述 投票:0回答:1

我正在使用 MS Teams 中的这 2 个包,使用自适应卡的 HTML 渲染版本。

  1. https://www.npmjs.com/package/adaptivecards(版本2.11.1)

  2. https://www.npmjs.com/package/adaptivecards-react(版本1.1.1)

  3. 反应16

以前,我按原样使用自适应卡,并在 Teams 应用程序的帮助下将它们发布到 MS Teams 群聊或频道中。任何按钮单击事件都用于在我的任务模块微服务中捕获。

现在,当我将同一张自适应卡解析并渲染为 HTML 格式时,如何在单击 HTML 按钮时获得相同的结果?

这就是我将自适应卡解析为 HTML 的方式

Card.tsx

import { AdaptiveCardUsingHostConfigContext } from "adaptivecards-react";
import React, { useState, useEffect } from "react";
import * as AC from "adaptivecards";
import { SubmitAction } from "./SubmitAction";
import { ExecuteAction } from "./ExecuteAction";

AC.GlobalRegistry.actions.register(SubmitAction.JsonTypeName, SubmitAction);
AC.GlobalRegistry.actions.register(ExecuteAction.JsonTypeName, ExecuteAction);

const Card = (props: { card: any }) => {
  const { card } = props;
  const [isLoading, setIsLoading] = useState(false);
  const [cardPayload, setCardPayload] = useState(card);

  useEffect(() => {
    // console.log("Card: ", JSON.stringify(cardPayload));
  }, [cardPayload]);

  const onActionSubmit = (e: any) => {
    console.log("SUBMIT");
    console.log("inputs", e.data, e.id);
  };

  const onExecuteAction = (e: any) => {
    const actionType = e.getJsonTypeName();
    const { verb } = e;

    console.log(`executing action for ${verb}`);

    let response;
    if (actionType === "Action.Execute") {
      setIsLoading(true);
      response = cardPayload;
      response.body = [
        ...cardPayload.body,
        {
          text: "This is a fresh dynamic detail!",
          wrap: true,
          type: "TextBlock",
        },
      ];
      // }

      setCardPayload(response);
      setIsLoading(false);
    }
  };

  return (
    <div className="Card">
      <AdaptiveCardUsingHostConfigContext
        payload={cardPayload}
        onActionSubmit={onActionSubmit}
        onExecuteAction={onExecuteAction}
      />
    </div>
  );
};

export default Card;
ExecuteAction.tsx
import React from "react";
import * as AC from "adaptivecards";
import Button from "../components/Button";
import { reactDomRender } from "./shared";

export class ExecuteAction extends AC.ExecuteAction {
  private internalRenderedElement: any;

  static readonly titleProperty = new AC.StringProperty(
    AC.Versions.v1_0,
    "title",
    true
  );

  getTitle(): string | undefined {
    return this.getValue(ExecuteAction.titleProperty);
  }

  get renderedElement(): HTMLElement | undefined {
    return this.internalRenderedElement;
  }

  render() {
    const element = reactDomRender(this.renderElement());
    this.internalRenderedElement = element;
  }

  private renderElement = (): JSX.Element => {
    return <Button label={this.getTitle()} onClick={() => this.execute()} />;
  };
}
SubmitAction.tsx
import React from "react";
import * as AC from "adaptivecards";
import Button from "../components/Button";
import { reactDomRender } from "./shared";

export class SubmitAction extends AC.SubmitAction {
  private internalRenderedElement: any;

  static readonly textProperty = new AC.StringProperty(
    AC.Versions.v1_3,
    "text",
    true
  );

  static readonly titleProperty = new AC.StringProperty(
    AC.Versions.v1_3,
    "title",
    true
  );

  static readonly dataProperty = new AC.PropertyDefinition(
    AC.Versions.v1_3,
    "data"
  );

  getTitle(): string | undefined {
    return this.getValue(SubmitAction.titleProperty);
  }

  getText(): string | undefined {
    return this.getValue(SubmitAction.textProperty);
  }

  getData(): string | undefined {
    return this.getValue(SubmitAction.dataProperty);
  }

  getInputs(): any {
    return this.getValue(SubmitAction.associatedInputsProperty);
  }

  getInputValues(): any {
    return this.parent?.getAllInputs().map((input) => {
      return { id: input.id, value: input.value };
    });
  }

  get renderedElement(): HTMLElement | undefined {
    return this.internalRenderedElement;
  }

  render() {
    const element = reactDomRender(this.renderElement());
    this.internalRenderedElement = element;
  }

  execute() {
    console.log("-->", this.getData());
    const inputs = this.validateInputs();
    console.log(inputs);
    if (inputs.length === 0) {
      console.log("inputs valid");
      const inputValues = this.getInputValues();
      console.log("input values", inputValues);
    } else {
      for (const input of inputs) {
        console.log("input id", input.id);
        console.log("isValid", input.isValid());
        this.render();
      }
    }
  }

  onExecuteAction(action: any) {
    console.log("onExecuteAction");
    action();
  }

  private renderElement = (): JSX.Element => {
    return <Button label={this.title} onClick={() => this.execute()} />;
  };
}

shared.tsx

import React from "react";
import * as ReactDOM from "react-dom";

export const reactDomRender = (
  reactElement: React.ReactElement
): HTMLElement | undefined => {
  const div = document.createElement("div");
  ReactDOM.render(reactElement, div);
  return div;
};
RenderCard.tsx
/* eslint-disable react/prop-types */
import React from "react";
import Card from "./components/Card";
import { Loader } from "@fluentui/react-northstar";

const RenderCard = (props) => {
  const { card } = props;

  if (card) {
    return (
      <div className="App">
        <div>
          <Card card={card} />
        </div>
      </div>
    );
  } else {
    return <Loader label="Please wait..." size="largest" />;
  }
};

export default RenderCard;
hostConfig.tsx
const hostConfig = () => {
  return {
    spacing: {
      small: 3,
      default: 8,
      medium: 20,
      large: 30,
      extraLarge: 40,
      padding: 10,
    },
    separator: {
      lineThickness: 1,
      lineColor: "#EEEEEE",
    },
    supportsInteractivity: true,
    fontTypes: {
      default: {
        fontFamily:
          "'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
        fontSizes: {
          small: 12,
          default: 14,
          medium: 17,
          large: 21,
          extraLarge: 26,
        },
        fontWeights: {
          lighter: 200,
          default: 400,
          bolder: 600,
        },
      },
      monospace: {
        fontFamily: "'Courier New', Courier, monospace",
        fontSizes: {
          small: 12,
          default: 14,
          medium: 17,
          large: 21,
          extraLarge: 26,
        },
        fontWeights: {
          lighter: 200,
          default: 400,
          bolder: 600,
        },
      },
    },
    containerStyles: {
      default: {
        backgroundColor: "#FFFFFF",
        foregroundColors: {
          default: {
            default: "#000000",
            subtle: "#767676",
          },
          accent: {
            default: "#0063B1",
            subtle: "#0063B1",
          },
          attention: {
            default: "#FF0000",
            subtle: "#DDFF0000",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#c3ab23",
            subtle: "#DDc3ab23",
          },
        },
      },
      emphasis: {
        backgroundColor: "#F0F0F0",
        foregroundColors: {
          default: {
            default: "#000000",
            subtle: "#767676",
          },
          accent: {
            default: "#2E89FC",
            subtle: "#882E89FC",
          },
          attention: {
            default: "#FF0000",
            subtle: "#DDFF0000",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#c3ab23",
            subtle: "#DDc3ab23",
          },
        },
      },
      accent: {
        foregroundColors: {
          default: {
            default: "#333333",
            subtle: "#EE333333",
          },
          dark: {
            default: "#000000",
            subtle: "#66000000",
          },
          light: {
            default: "#FFFFFF",
            subtle: "#33000000",
          },
          accent: {
            default: "pink",
            subtle: "#882E89FC",
          },
          attention: {
            default: "#cc3300",
            subtle: "#DDcc3300",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#e69500",
            subtle: "#DDe69500",
          },
        },
      },
      good: {
        backgroundColor: "#CCFFCC",
        foregroundColors: {
          default: {
            default: "#333333",
            subtle: "#EE333333",
          },
          dark: {
            default: "#000000",
            subtle: "#66000000",
          },
          light: {
            default: "#FFFFFF",
            subtle: "#33000000",
          },
          accent: {
            default: "#2E89FC",
            subtle: "#882E89FC",
          },
          attention: {
            default: "#cc3300",
            subtle: "#DDcc3300",
          },
          good: {
            default: "#54a254",
            subtle: "#DD54a254",
          },
          warning: {
            default: "#e69500",
            subtle: "#DDe69500",
          },
        },
      },
    },
    imageSizes: {
      small: 40,
      medium: 80,
      large: 160,
    },
    actions: {
      maxActions: 6,
      spacing: "default",
      buttonSpacing: 10,
      showCard: {
        actionMode: "inline",
        inlineTopMargin: 8,
      },
      actionsOrientation: "vertical",
      actionAlignment: "stretch",
      iconSize: 50,
    },
    adaptiveCard: {
      allowCustomStyle: true,
    },
    imageSet: {
      imageSize: "medium",
      maxImageHeight: 100,
    },
    factSet: {
      title: {
        color: "default",
        size: "default",
        isSubtle: false,
        weight: "bolder",
        wrap: true,
        maxWidth: 150,
      },
      value: {
        color: "default",
        size: "default",
        isSubtle: false,
        weight: "default",
        wrap: true,
      },
      spacing: 8,
    },
  };
};

export default hostConfig;

这是我使用

RenderCard.tsx
组件的代码要点

import { ProvidesHostConfigContext } from "adaptivecards-react";
import hostConfig from "./hostConfig";
import RenderCard from "./rendercard";

<ProvidesHostConfigContext hostConfig={hostConfig}>
    <RenderCard card={card} />
</ProvidesHostConfigContext>

任何帮助或推动正确的方向都会非常有帮助。

谢谢你

node.js microsoft-teams adaptive-cards msteams-react-base-component task-module
1个回答
0
投票

您可以在应用程序的 JavaScript 或 Typescript 代码中处理按钮单击事件,并使用 Teams JavaScript 客户端库调用任务模块。

TeamsJS V1 代码示例:

let taskInfo = {
    title: null,
    height: null,
    width: null,
    url: null,
    card: null,
    fallbackUrl: null,
    completionBotId: null,
};

taskInfo.url = "https://contoso.com/teamsapp/customform";
taskInfo.title = "Custom Form";
taskInfo.height = 510;
taskInfo.width = 430;
submitHandler = (err, result) => {
    console.log(`Submit handler - err: ${err}`);
    console.log(`Submit handler - result\rName: ${result.name}\rEmail: ${result.email}\rFavorite book: ${result.favoriteBook}`);
};
microsoftTeams.tasks.startTask(taskInfo, submitHandler);

TeamsJS V2 示例代码:

let taskInfo = {
    title: null,
    height: null,
    width: null,
    url: null,
    card: null,
    fallbackUrl: null,
    completionBotId: null,
};

taskInfo.url = "https://contoso.com/teamsapp/customform";
taskInfo.title = "Custom Form";
taskInfo.height = 510;
taskInfo.width = 430;
dialogResponse = (dialogResponse) => {
        console.log(`Submit handler - err: ${dialogResponse.err}`);
        alert("Result = " + JSON.stringify(dialogResponse.result) + "\nError = " + JSON.stringify(dialogResponse.err));
    };

 microsoftTeams.dialog.open(taskInfo, dialogResponse);

参考文档:https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/task-modules/task-modules-tabs?tabs=teamsjs1%2Cteamsjs3#example-of -调用任务模块

请参考以下示例: https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-task-module

© www.soinside.com 2019 - 2024. All rights reserved.