首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >BotFramework-WebChat -自适应卡

BotFramework-WebChat -自适应卡
EN

Stack Overflow用户
提问于 2020-03-26 23:04:12
回答 1查看 2.9K关注 0票数 2

是否有方法将Onchange添加到webchat (版本V4)中呈现的自适应卡输入字段中。例如,在签出屏幕中更改数量值(类型为number的Adaptive输入字段)应更新总值(Adaptive文本字段)。

为了保持simple....In,下面的图像,一旦我改变了输入框中的数字,它应该在下面的文本框中更新。所有事情都应该发生在webchat V4(React)客户端。

下面是我尝试过的选项,这里没有任何代码可供提交:

option1:尝试使用中间件将事件添加到卡片中的quantity输入字段中,但无法找到唯一标识输入字段以添加事件的选项(可以根据卡中没有的项查看多个输入字段)

option2:根据来自机器人的卡片,在前部创建一张新卡,并将事件添加到新卡中。是否有可能中断发送到机器人的信息并从前端发送一张卡片?

option3:向卡添加一个更新按钮,以便在后端计算总数,并将更新卡提交给用户。

以下是有效载荷:

代码语言:javascript
复制
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0",
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "text": "Output",
            "weight": "Bolder",
            "horizontalAlignment": "Center",
            "size": "Large",
            "id": "output",
            "color": "Good"
        },
        {
            "type": "Container",
            "items": [
                {
                    "$data": "{items}",
                    "type": "Container",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": " ",
                            "id": "line",
                            "spacing": "None"
                        },
                        {
                            "type": "Image",
                            "altText": "",
                            "id": "myimage",
                            "url": "{imgUrl}",
                            "spacing": "None",
                            "size": "Stretch",
                            "width": "1000px",
                            "height": "100px"
                        },
                        {
                            "type": "ColumnSet",
                            "id": "imgset",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": 50,
                                    "id": "desc",
                                    "items": [
                                        {
                                            "type": "TextBlock",
                                            "text": "{description}",
                                            "weight": "Bolder",
                                            "spacing": "None",
                                            "id": "desc",
                                            "wrap": true,
                                            "maxLines": 4
                                        }
                                    ],
                                    "spacing": "None"
                                }
                            ],
                            "spacing": "None"
                        },
                        {
                            "type": "ColumnSet",
                            "spacing": "None",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": 50,
                                    "id": "qty",
                                    "items": [
                                        {
                                            "type": "Input.Number",
                                            "placeholder": "Quantity",
                                            "id": "myquantity",
                                            "min": 0,
                                            "max": 100,
                                            "value": "{quantity}",
                                            "spacing": "None"
                                        }
                                    ],
                                    "horizontalAlignment": "Left",
                                    "verticalContentAlignment": "Center",
                                    "spacing": "None"
                                },
                                {
                                    "type": "Column",
                                    "id": "pricec",
                                    "items": [
                                        {
                                            "type": "TextBlock",
                                            "text": "{price}",
                                            "id": "pricet",
                                            "horizontalAlignment": "Right",
                                            "spacing": "None"
                                        }
                                    ],
                                    "verticalContentAlignment": "Center",
                                    "horizontalAlignment": "Right",
                                    "width": 50,
                                    "spacing": "None"
                                }
                            ],
                            "id": "qtypset"
                        },
                        {
                            "type": "ColumnSet",
                            "spacing": "None",
                            "columns": [
                                {
                                    "type": "Column",
                                    "width": 1,
                                    "items": [
                                        {
                                            "type": "TextBlock",
                                            "text": "Sub Total",
                                            "size": "Medium",
                                            "id": "subtotal00",
                                            "weight": "Bolder",
                                            "spacing": "None"
                                        }
                                    ],
                                    "id": "subtotal1",
                                    "spacing": "None"
                                },
                                {
                                    "type": "Column",
                                    "width": 1,
                                    "items": [
                                        {
                                            "type": "TextBlock",
                                            "horizontalAlignment": "Right",
                                            "text": "{subtotal}",
                                            "size": "Medium",
                                            "weight": "Bolder",
                                            "id": "subtotalt0",
                                            "color": "Accent",
                                            "spacing": "None"
                                        }
                                    ],
                                    "id": "subtotal200",
                                    "spacing": "None"
                                }
                            ],
                            "id": "colsetsubtot00"
                        }
                    ],
                    "id": "itemcontainer",
                    "style": "emphasis",
                    "spacing": "None"
                }
            ],
            "id": "rootcontainer",
            "style": "accent"
        },
        {
            "type": "ColumnSet",
            "id": "totalset",
            "columns": [
                {
                    "type": "Column",
                    "width": 50,
                    "id": "totalcolumn",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "Total",
                            "size": "Medium",
                            "isSubtle": true,
                            "weight": "Bolder",
                            "id": "total",
                            "color": "Dark"
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": 50,
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "{total}",
                            "size": "Medium",
                            "id": "totaltext",
                            "horizontalAlignment": "Right",
                            "weight": "Bolder",
                            "color": "Accent"
                        }
                    ],
                    "id": "totalcol2"
                }
            ]
        }
    ],
    "id": "final"
}

我使用下面的示例作为起点https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/04.api/e.piping-to-redux

webchat.js:

代码语言:javascript
复制
import React from 'react';

import ReactWebChat, { createDirectLine, createStore } from 'botframework-webchat';
import directLineDisconnect from 'botframework-webchat-core/lib/actions/disconnect';
import dispatchIncomingActivityMiddleware from './dispatchIncomingActivityMiddleware';
import uuid from 'uuid';

export default class extends React.Component {
  constructor(props) {
    super(props);

    this.store = createStore({}, dispatchIncomingActivityMiddleware(props.appDispatch, this));
    this.activityMiddleware = this.setActivityMiddleware();
    this.attachmentMiddleware = this.setAttachmentMiddleware();

    this.state = {};

  }

  componentDidMount() {
    this.fetchToken();
    this.setSendBox();
  }

  componentWillUnmount(){

  }

  async fetchToken() {
    const myHeaders = new Headers();
    const userDetails = uuid.v4();
    myHeaders.append('Authorization', 'Bearer ' + 'mytoken'); 
    myHeaders.append('Content-type', 'application/json');
    const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', { 
                        body: JSON.stringify({ user: { id: userDetails, name: userDetails }}),
                        method: 'POST', headers: myHeaders });
    const { token } = await res.json();
    console.log("My Token: " + token);
    this.setState(() => ({
      directLine: createDirectLine({ token })
    }));
  }

  setActivityMiddleware(){
    return () => next => card => {
      return children => (
        <div
          className={card.activity.attachments && (card.activity.attachments[0].content.id === "output") ? card.activity.attachments && card.activity.attachments[0].content.id : ''}
        >
          {next(card)(children)}
        </div>
      );
    };

  }


  setAttachmentMiddleware(){
    return () => next => ({ card, activity, attachment: baseAttachment }) => {
      let attachment = baseAttachment;
      if (baseAttachment.content.body){
      switch (baseAttachment.content.body[0].id) {
        case 'review':                   
         for (let i = 0; i < attachment.content.body[1].items.length; i++) {
         attachment.content.body[1].items[i].items[3].columns[0].items[0].value = baseAttachment.content.body[1].items[i].items[3].columns[0].items[0].value.toString();
                                                                           } //for loop
         break;

         default:
           break;
        }
    }
    return next({ card, activity, attachment });
    };

  }

  setSendBox() {

    this.store.dispatch({
      type: 'WEB_CHAT/SET_SEND_BOX',
      payload: { text: 'sample:redux-middleware' }
    });
/*

    this.store.dispatch({
      type: 'WEB_CHAT/SEND_EVENT',
      payload: { name: 'membersAdded',
                 value: { language: window.navigator.language }
               }  
    }); */
  }


  render() {
    return this.state.directLine ? (
      <ReactWebChat
        activityMiddleware={this.activityMiddleware}
        attachmentMiddleware={this.attachmentMiddleware}
        directLine={this.state.directLine}
        store={this.store}
        styleOptions={{
          backgroundColor: 'Transparent',
          hideUploadButton: true
        }}
      />
    ) : (
      <div>Connecting to bot&hellip;</div>
    );
  }

}

dispatchIncomingActivityMiddleware.js:

代码语言:javascript
复制
export default function(dispatch, thisvariable) {
    return () => next => action => {
      if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
        const { activity } = action.payload;


        if (activity.from.role === 'bot'){
        var inputBox=document.getElementsByClassName("css-eycyw2");
        if (inputBox.length > 0){
          inputBox[inputBox.length - 1].style.display='block';
        }
                                          }

      }


      if ((action.type === 'WEB_CHAT/SEND_POST_BACK') || (action.type === 'WEB_CHAT/SEND_MESSAGE')) { 
        var inputBox=document.getElementsByClassName("css-eycyw2");
        if (inputBox.length > 0){
          inputBox[inputBox.length - 1].style.display='none';
          dispatch(setInputVisibility(true));
        }
      }

      return next(action);
    };
  }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-05 19:12:05

首先要理解的是,Web使用了作为npm包提供的自适应卡JavaScript SDK。Web主要使用SDK的开箱即用的呈现功能,但它改变的一件重要事情是操作的处理方式。如果不提供自定义处理程序,提交操作将不会发送给机器人。

adaptiveCard.onExecuteAction = handleExecuteAction;

这就是应用程序应该如何使用Adaptive。虽然大部分功能都是在SDK端处理的,但是应用程序需要做一些事情才能使Adaptive为特定的应用程序工作。虽然您可以看到Web为特定Adaptive实例的onExecuteAction "event“属性分配函数,但也有一个静态的onExecuteAction对应项可以这样访问:

代码语言:javascript
复制
AdaptiveCard.onExecuteAction = handleExecuteAction;

使用静态事件将为所有Adaptive应用一个处理程序,而不仅仅是一个,但是它将被应用于特定实例的任何处理程序覆盖。我告诉您这一点的原因是因为有更多的静态事件,特别是有一些对您的情况有用的:

静态onAnchorClicked:( CardElement,锚: HTMLAnchorElement) => boolean =空;静态onExecuteAction:(action: Action) => void =空;静态onElementVisibilityChanged:(元素: CardElement) => void =空;静态onImageLoaded:(图像:图像) => void =空;静态onInlineCardExpanded:(action: ShowCardAction,isExpanded: boolean) => void =空;静态onInputValueChanged:(输入:输入) => void =空;静态onInputValueChanged:(元素:=>,json:有错误吗?:) void =空;静态onParseAction:(元素: Action,json: any,Array) => void = null;静态onParseError:(错误: HostConfig.IValidationError) => void = null;静态onProcessMarkdown:(文本:字符串,结果: IMarkdownProcessingResult) => =>= null;

您可以想出一个使用onInputValueChanged事件的解决方案,每当卡片中的任何输入被更改时,该事件都会触发。您的处理程序可以在卡片中搜索它需要用作运算数的其他元素,还需要在卡片中搜索显示结果的元素。与其每次输入字符时做所有这些工作,我更喜欢在卡片开始时搜索一次它在计算中使用的元素的解决方案。除了侦听Adaptive类或Adaptive实例上的事件之外,另一种替代方法是侦听特定元素上的事件,比如输入。因此,我的示例将使用静态onParseElement事件来获取它需要的元素,然后对它找到的特定输入实例使用onValueChanged事件。

在为处理程序编写代码之前,我们需要想出一种方法,以便代码知道要为操作数使用哪些元素以及计算的结果。例如,您可以让代码组合卡片(或容器)中的每个输入,并将结果放在最后找到的文本块中。对于我的例子,我已经提出了代码可以使用的命名模式。有两个关键字,“总计”和“价格”,代码在每个元素ID中查找它们。我想说明这个模式完全是任意的,如果你想要的话,你可以做一些不同的事情。这是我的示例卡:

代码语言:javascript
复制
{
  "type": "AdaptiveCard",
  "version": "1.0",
  "body": [
    {
      "type": "TextBlock",
      "text": "$10.00",
      "id": "foo_a_price"
    },
    {
      "type": "Input.Text",
      "id": "foo_a"
    },
    {
      "type": "TextBlock",
      "text": "$2.00",
      "id": "foo_b_price"
    },
    {
      "type": "Input.Text",
      "id": "foo_b"
    },
    {
      "type": "TextBlock",
      "text": "total",
      "id": "total_foo"
    }
  ],
  "actions": [
    {
      "type": "Action.Submit",
      "title": "Submit"
    }
  ]
}

您可以从这一点中猜测,这个想法是让一个文本块具有一个以"total_“开头的ID,并在其后面有一些标识符。您想要加起来的数量以相同的标识符开始,您希望与每个数量相乘的价格与数量具有相同的ID,但后缀为"_price“。我建议使用数字输入而不是文本输入,但是这个示例显示文本仍然有效。下面是我的示例应用程序的代码,它读取模式:

代码语言:javascript
复制
import * as adaptiveCardsPackage from 'adaptivecards';

adaptiveCardsPackage.AdaptiveCard.onParseElement = element => {
  const PREFIX_TOTAL = 'total_';
  const SUFFIX_PRICE = '_price';

  if (element.id && element.id.startsWith(PREFIX_TOTAL)) {
    const itemPrefix = element.id.slice(PREFIX_TOTAL.length);
    const card = element.getRootElement();
    const inputs = card.getAllInputs().filter(input => input.id.startsWith(itemPrefix));
    const products = {};

    for (const input of inputs) {
      const priceElement = card.getElementById(input.id + SUFFIX_PRICE);
      const price = Number(priceElement.text.replace(/[^0-9.-]+/g, '')) || 0;

      // `sender` will be the same as `input`.
      // You could capture the input const instead of using the argument,
      // but I'm demonstrating that you don't need to.
      input.onValueChanged = sender => {
        const quantity = Number(sender.value) || 0;

        products[sender.id] = price * quantity;

        const sum = Object.values(products).reduce((a, b) => a + b);

        element.setText("$" + sum.toFixed(2));
        element.renderedElement.replaceWith(element.render());
      };
    }
  }
};

我有理由相信,对AdaptiveCard类的更改将自动应用于Web导入的包中的AdaptiveCard类,因为它是同一个包中的同一个类。但是,Web Chat现在允许您将自己的Adaptive包作为属性提供,因此您可以确保Web与您的特殊事件处理程序一起使用该包:

代码语言:javascript
复制
<ReactWebChat
  directLine={createDirectLine({secretOrToken})}
  adaptiveCardsPackage={adaptiveCardsPackage}
/>

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60877533

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档