练习 - 创建具有地理位置功能的 SPFx ACE

已完成

在本练习中,你将使用 Viva Connections 中的地理位置功能的主文本卡片模板创建一个SharePoint 框架 (SPFx) 自适应卡片扩展 (ACE) 。

你将实现的方案是校园穿梭服务的简化版本,大型大学或公司必须帮助员工和访客在建筑物之间往返。 ACE 是航天飞机的潜水员。 司机将使用 ACE 预订行程,并指示他们正在接乘客或将乘客送到目的地的路上。

先决条件

开发用于Viva Connections的 ACE 需要在租户中设置 Microsoft 365 租户、SharePoint Online 和Viva Connections。 使用以下资源准备租户:

还需要在工作站上安装必要的开发人员工具:

重要

在大多数情况下,安装以下工具的最新版本是最佳选择。 此模块在发布和最后测试过程中,使用了此处列出的版本。

创建 SharePoint 列表以存储数据

第一步是创建新的 SharePoint 列表来存储每个校园穿梭巴士司机的数据。

在浏览器中,导航到要测试本练习中创建的项目的 SharePoint 网站。

从可用选项中选择 “新建 ”,然后选择 “列出 ”:

新列表体验的屏幕截图。

“创建列表 ”对话框中,选择“ 空白列表”。

将列表 的名称 设置为 “Campus Shuttle ”,然后选择“ 创建”。

当浏览器刷新以显示新列表时,向列表添加几个列。 依次选择“ 添加列”、“ 文本”和“ 下一步 ”按钮。 在 “创建列 ”面板上,输入以下值,然后选择“ 保存”

  • 名称:OriginLocation
  • 类型:单行文本

使用以下值重复此过程,向列表添加更多列:

  • 列:
    • 名称:DestinationName
    • 类型:单行文本
  • 列:
    • 名称:DestinationLocation
    • 类型:单行文本
  • 列:
    • 名称:状态
    • 类型:选项
    • 选项
      • en route
      • 可用

使用用于存储 ACE 数据的列表,现在可以创建项目。

创建和准备 SPFx 项目

打开命令提示符,移动到要在其中创建 SPFx 项目的文件夹。 然后,执行以下命令来运行 SharePoint Yeoman 生成器:

yo @microsoft/sharepoint

使用以下命令完成显示的提示:

  • 解决方案名称是什么?:AceCampusShuttle
  • 要创建哪种类型的客户端组件?:自适应卡片扩展
  • 要使用哪个模板?:主文本模板
  • 你的自适应卡片扩展名称是什么?:Campus Shuttle

预配项目所需的文件夹后,生成器将通过自动运行 npm install 来安装所有依赖项包。 在 NPM 完成下载所有依赖项后,在 Visual Studio Code 中打开项目。

添加示例数据

你将创建的 ACE 将为航天飞机司机提供从已知位置列表中选择目标或在地图上选择一个点的选项。

在项目中创建一个新文件 ./src/adaptiveCardExtensions/campusShuttle/assets/campus_locations.json ,并为位置对象添加数组。 每个位置都应具有 名称纬度经度 属性。 或者,可以将以下 JSON 粘贴到包含位于佛罗里达州盖恩斯维尔的佛罗里达大学的几个位置的文件中,美国。

[
  { "title": "UF: Reitz Student Union", "latitude": 29.6463258, "longitude": -82.3499756 },
  { "title": "UF: The Hub", "latitude": 29.648018, "longitude": -82.345664 },
  { "title": "UF: Department of Computer and Information Science and Engineering", "latitude": 29.6476101, "longitude": -82.3466208 },
  { "title": "UF: Materials Science and Engineering", "latitude": 29.6476101, "longitude": -82.3466208 },
  { "title": "UF: Turlington Hall", "latitude": 29.6476101, "longitude": -82.3466208 },
  { "title": "UF: McCarty Hall A", "latitude": 29.6476101, "longitude": -82.3466208 },
  { "title": "UF: Peabody Hall", "latitude": 29.6502915, "longitude": -82.3433807 },
  { "title": "UF: Norman Hall", "latitude": 29.6486165, "longitude": -82.3398393 },
  { "title": "UF: Warrington College of Business", "latitude": 29.65093, "longitude": -82.3402091 },
  { "title": "UF: Mechanical and Aerospace Engineering Building A", "latitude": 29.6436917, "longitude": -82.3478054 },
  { "title": "UF: New Physics Building (NPB)", "latitude": 29.6439734, "longitude": -82.3506927 },
  { "title": "UF: Murphree Hall", "latitude": 29.6508923, "longitude": -82.3480633 }
]

添加 SharePoint REST API 服务帮助程序

接下来,让我们向项目添加 SharePoint REST 服务,以处理为存储穿梭车司机数据而创建的 SharePoint 列表的所有读取和写入操作。

在项目中创建一个新文件 ./src/adaptiveCardExtensions/campusShuttle/sp.service.ts ,并向其添加以下代码:

import { AdaptiveCardExtensionContext } from '@microsoft/sp-adaptive-card-extension-base';
import { SPHttpClient } from '@microsoft/sp-http'

export const STATUS_HIRED = 'hired';
export const STATUS_ENROUTE = 'en route';
export const STATUS_AVAILABLE = 'available';

export interface ILocation {
  latitude: number;
  longitude: number;
}

export interface IListItem {
  ['@odata.type']?: string;
  Id?: string;
  Title: string;
  Status: string;
  OriginLocation?: string | ILocation;
  DestinationName?: string;
  DestinationLocation?: string | ILocation;
}

export const fetchListItem = async (spContext: AdaptiveCardExtensionContext, listId: string): Promise<IListItem> => {
  if (!listId) { return Promise.reject('No listId specified.'); }

  const listApiUrl = `${spContext.pageContext.web.absoluteUrl}/_api/web/lists/GetById(id='${listId}')`;
  const user = spContext.pageContext.user.loginName;

  const response: { value: IListItem[] } = await (await spContext.spHttpClient.get(
    `${listApiUrl}/items/?$select=Id,Title,Status,OriginLocation,DestinationName,DestinationLocation&$filter=Title eq '${user}'&$top=1`,
    SPHttpClient.configurations.v1
  )).json();

  if (response.value.length === 0) { return Promise.resolve(undefined); }

  const convertedTrip = response.value[0];

  if (convertedTrip) {
    const origin = convertedTrip.OriginLocation as string;
    convertedTrip.OriginLocation = <ILocation>{
      latitude: Number(origin.split(',')[0]),
      longitude: Number(origin.split(',')[1])
    };
  }
  if (convertedTrip) {
    const destination = convertedTrip.DestinationLocation as string;
    convertedTrip.DestinationLocation = <ILocation>{
      latitude: Number(destination.split(',')[0]),
      longitude: Number(destination.split(',')[1])
    };
  }

  return Promise.resolve(convertedTrip);
}

const getItemEntityType = async (spContext: AdaptiveCardExtensionContext, listApiUrl: string): Promise<string> => {
  const response: { ListItemEntityTypeFullName: string } = await (await spContext.spHttpClient.get(
    `${listApiUrl}?$select=ListItemEntityTypeFullName`,
    SPHttpClient.configurations.v1
  )).json();

  return response.ListItemEntityTypeFullName;
}

const createListItem = async (
  spContext: AdaptiveCardExtensionContext,
  listApiUrl: string,
  listItem: IListItem): Promise<void> => {

  listItem['@odata.type'] = await getItemEntityType(spContext, listApiUrl);

  await spContext.spHttpClient.post(
    `${listApiUrl}/items`,
    SPHttpClient.configurations.v1,
    {
      headers: {
        'ACCEPT': 'application/json; odata.metadata=none',
        'CONTENT-TYPE': 'application/json'
      },
      body: JSON.stringify(listItem)
    }
  );

  return Promise.resolve();
}

export const upsertListItem = async (spContext: AdaptiveCardExtensionContext, listId: string, listItem: IListItem): Promise<void> => {
  if (!listId) { return Promise.reject('No listId specified.'); }

  const listApiUrl = `${spContext.pageContext.web.absoluteUrl}/_api/web/lists/GetById(id='${listId}')`;

  const originLocationObj = (listItem.OriginLocation as ILocation);
  listItem.OriginLocation = `${originLocationObj.latitude},${originLocationObj.longitude}`;
  const destinationLocationObj = (listItem.DestinationLocation as ILocation);
  listItem.DestinationLocation = `${destinationLocationObj.latitude},${destinationLocationObj.longitude}`;

  if (!listItem['@odata.type']) { return createListItem(spContext, listApiUrl, listItem); }

  await spContext.spHttpClient.post(
    `${listApiUrl}/items(${listItem.Id})`,
    SPHttpClient.configurations.v1,
    {
      headers: { 'IF-MATCH': '*', 'X-HTTP-METHOD': 'MERGE' },
      body: JSON.stringify(<IListItem>{
        Title: listItem.Title,
        Status: listItem.Status,
        OriginLocation: listItem.OriginLocation,
        DestinationName: listItem.DestinationName,
        DestinationLocation: listItem.DestinationLocation
      })
    }
  );

  return Promise.resolve();
}

export const deleteListItem = async (spContext: AdaptiveCardExtensionContext, listId: string, listItemId: number): Promise<void> => {
  if (!listId) { return Promise.reject('No listId specified.'); }
  if (!listItemId) { return Promise.reject('No listItemId specified.'); }

  const listApiUrl = `${spContext.pageContext.web.absoluteUrl}/_api/web/lists/GetById(id='${listId}')`;

  await spContext.spHttpClient.post(
    `${listApiUrl}/items(${listItemId})`,
    SPHttpClient.configurations.v1,
    {
      headers: { 'IF-MATCH': '*', 'X-HTTP-METHOD': 'DELETE' }
    }
  );
}

此服务导出将在整个项目中使用的以下内容:

  • 状态选项的三个常量 () :
    • STATUS_AVAILABLE
    • STATUS_ENROUTE
    • STATUS_HIRED
  • fetchListItem():此方法检索当前已登录的驱动程序记录 ((如果存在) )。
  • upsertListItem():此方法创建新的驱动程序记录或更新现有的驱动程序记录。
  • deleteListItem():此方法在行程较长时删除驱动程序记录。

初始化项目

现在,项目添加了一些核心依赖项,现在让我们在 ACE 首次加载到页面上时实现某些核心功能的初始化。 这包括配置 ACE 的状态,并使用户能够设置包含驱动程序行程详细信息的列表的 ID。

更新 ACE 以允许用户设置 SharePoint 列表 ID

在文件 ./src/adaptiveCardExtensions/campusShuttle/CampusShuttleAdaptiveCardExtension.ts 中找到 ACE 类,并在 VS Code 中打开它。

ICampusShuttleAdaptiveCardExtensionProps找到 接口并添加 listId 属性以存储包含驱动程序状态记录的 SharePoint 列表的 ID:

export interface ICampusShuttleAdaptiveCardExtensionProps {
  title: string;
  listId: string;
}

找到并打开文件 ./src/adaptiveCardExtensions/campusShuttle/CampusShuttlePropertyPane.ts。 将一个新的 PropertyPaneTextField 添加到数组, groupFields 以添加一个选项,供用户设置包含驱动程序状态记录的列表的 ID:

groupFields: [
  PropertyPaneTextField('title', {
    label: strings.TitleFieldLabel
  }),
  PropertyPaneTextField('listId', {
    label: 'List ID (GUID)'
  })
]

最后,返回到 CampusShuttleAdaptiveCardExtension.ts 文件,并将以下方法添加到 CampusShuttleAdaptiveCardExtension 类。 当属性窗格中某个属性的值发生更改时,SPFx 运行时将引发此事件。 我们希望 ACE 在找到驱动程序记录时,使用列表 ID 中的更改来尝试初始化 ACE 的状态:

protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
  if (propertyPath === 'listId' && newValue !== oldValue) {
    if (newValue) {
      (async () => {
        const trip = await fetchListItem(this.context, this.properties.listId);
        if (trip) { this.setState({ currentTrip: trip }); }
      })();
    }
  }
}

更新 ACE 状态和初始卡片视图

在 文件中的现有import语句后面添加以下import语句:

import {
  IListItem,
  fetchListItem,
  STATUS_AVAILABLE
} from './sp.service';

找到状态接口 ICampusShuttleAdaptiveCardExtensionState 并添加 属性 currentTrip ,如以下代码所示:

export interface ICampusShuttleAdaptiveCardExtensionState {
  currentTrip: IListItem;
}

初始化现有 onInit() 方法中的状态属性。 将现有 this.state = { }; 替换为以下代码,以设置当前驱动程序的占位符行程状态:

this.state = {
  currentTrip: {
    Title: this.context.pageContext.user.loginName,
    Status: STATUS_AVAILABLE
  }
};

接下来,将以下代码添加到 onInit() 方法中,紧接在 return Promise.resolve(); 语句前面,以从 SharePoint 列表中检索当前驱动程序的行程记录。 如果找到匹配的行程,它会将 ACE 的状态更新为此行程,覆盖初始化状态时创建的空默认行程。

if (this.properties.listId) {
  const trip = await fetchListItem(this.context, this.properties.listId);
  if (trip) { this.setState({ currentTrip: trip }); }
}

此代码使用 await 关键字, onInit() 但使用方法签名,虽然返回 Promise,但它没有必需的 async 关键字。 更新 onInit() 方法声明以包含此关键字:

public async onInit(): Promise<void> { .. }

最后,注释掉或删除对 对象的以下引用 strings

import * as strings from 'SharePointRestAdaptiveCardExtensionStrings';

删除现有 QuickView 并更新初始 CardView

测试 ACE 之前的最后一步是删除当前的 QuickView。 稍后,我们将向 ACE 添加多个新的 QuickView。

首先从 CampusShuttleAdaptiveCardExtension.ts 文件中删除以下行:

  1. 删除以下 import 语句:

    import { QuickView } from './quickView/QuickView';
    
  2. export删除 QuickView ID 的声明:

    export const QUICK_VIEW_REGISTRY_ID: string = 'CampusShuttle_QUICK_VIEW';
    
  3. CampusShuttleAdaptiveCardExtension 类的 onInit() 方法中,删除注册 QuickView 的以下语句:

    this.quickViewNavigator.register(QUICK_VIEW_REGISTRY_ID, () => new QuickView());
    
  4. 从项目中删除以下文件:

    • ./src/adaptiveCardExtensions/campusShuttle/quickView/template/QuickViewTemplate.json
    • ./src/adaptiveCardExtensions/campusShuttle/quickView/QuickView.ts

接下来,找到并打开 CardView: ./src/adaptiveCardExtensions/campusShuttle/cardView/CardView.ts ,并对其进行以下更改:

  1. 找到引用包中的值的现有 import 语句, @microsoft/sp-adaptive-card-extension-base 并删除以下引用:

    • IExternalLinkCardAction
    • IQuickViewCardAction
  2. 找到引用模块中的值的现有import语句,并删除对常量的引用QUICK_VIEW_REGISTRY_IDCampusShuttleAdaptiveCardExtension

  3. 在现有 import 语句后面添加以下 import 语句:

    import { STATUS_AVAILABLE } from '../sp.service';
    
  4. 将访问器成员的内容 cardButtons() 替换为以下 switch 语句。 在向 ACE 添加新功能时,你将在整个练习中更新此 switch 语句:

public get cardButtons(): [ICardButton] | [ICardButton, ICardButton] | undefined {
  switch (this.state.currentTrip.Status) {
    default:
      return undefined;
      break;
  }
}
  1. 更新访问 data() 器成员以返回将用于当前卡的属性:
public get data(): IPrimaryTextCardParameters {
  return {
    primaryText: strings.PrimaryText,
    description: (this.state.currentTrip.Status === STATUS_AVAILABLE)
      ? `available for hire`
      : `TODO`,
    title: this.properties.title
  };
}
  1. 删除现有 onCardSelection() 访问器成员,因为 CardView 上不再有任何按钮。

测试 ACE

现在,你已准备好测试 ACE 的初始状态。

在控制台中,执行以下语句:

gulp serve --nobrowser

在浏览器中,导航到创建用于存储驱动程序记录的同一站点中的 SharePoint 托管工作台。 例如,如果列表 URL 为 https://contoso.sharepoint.com/sites/MSLearningTeam/Lists/Campus%20Shuttle/AllItems.aspx,则托管工作台的 URL 为 https://contoso.sharepoint.com/sites/MSLearningTeam/_layouts/15/workbench.aspx

选择图标, + 然后从工具箱中选择 “校园穿梭 ”:

SPFx 工具箱的屏幕截图。

将鼠标悬停在 ACE 组件上,然后选择铅笔图标以打开属性窗格:

ACE 的编辑体验的屏幕截图。

在“列表 ID”文本框中输入 SharePoint 列表 的 ID ,然后通过选择右上角的 X 图标关闭属性窗格。

提示

可以从列表的设置页 URL 获取列表的 ID。

首先浏览到列表,然后选择套件栏中的齿轮图标,然后选择 “列表设置” 链接。

列表设置页面的查询字符串包含 List 属性,例如 List=%7B93f11b8b-6201-4199-b263-3ca78408a73b%7D。 这是一个 URL 编码的字符串,其中包含由 括起来的 {}GUID。 %7B删除前缀和%7D后缀以获取列表的 ID。 例如,此列表 ID 为 93f11b8b-6201-4199-b263-3ca78408a73b

如何获取列表设置页的屏幕截图。

你不会注意到 ACE 呈现的更改,因为列表中没有任何记录。

此时,最小项目正在运行。 可以使用 QuickViews 开始向 ACE 添加交互性。

添加使用 QuickViews 创建行程的功能

现在,你将实现功能,使当前用户能够使用 ACE 添加新行程。 创建行程时,需要设置三项内容:

  • 行程的起源/开始位置
  • 行程的目标位置
  • 如果穿梭巴士司机正在从出发地点接乘客的途中,或已接载乘客并在前往目的地的路上

若要实现这一点,需要创建多个 QuickView,并在 Viva Connections 中使用 ACE 的地理位置功能。

创建 StartTrip QuickView

首先,在 ./src/adaptiveCardExtensions/campusShuttle/quickView/template 文件夹中创建一个新文件 StartTripCard.json,并向其添加以下 JSON。 这会使用自适应卡片设置 QuickView 的内容。

{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.2",
  "body": [
    {
      "type": "TextBlock",
      "text": "Start a trip",
      "size": "Large",
      "weight": "Bolder"
    },
    {
      "type": "TextBlock",
      "text": "Select trip status:",
      "size": "medium",
      "weight": "Bolder"
    },
    {
      "id": "tripType",
      "type": "Input.ChoiceSet",
      "value": "$trip.Status",
      "choices": [
        {
          "title": "en route to pickup",
          "value": "en route"
        },
        {
          "title": "starting trip",
          "value": "hired"
        }
      ]
    },
    {
      "type": "TextBlock",
      "text": "Set trip details:",
      "size": "medium",
      "weight": "Bolder"
    }
  ],
  "actions": [
    {
      "id": "originLocation",
      "type": "Action.Submit",
      "title": "(1) Select trip origin from map"
    },
    {
      "id": "destinationLocation",
      "type": "Action.Submit",
      "title": "(2) Select / set trip destination"
    },
    {
      "id": "save",
      "type": "Action.Submit",
      "title": "Save trip",
      "style": "positive"
    }
  ]
}

接下来,在 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹中创建文件 StartTrip.ts。 此文件将包含实现 QuickView 的类。

向文件添加以下代码:

import {
  ISPFxAdaptiveCard,
  BaseAdaptiveCardView,
  IActionArguments
} from '@microsoft/sp-adaptive-card-extension-base';
import * as strings from 'CampusShuttleAdaptiveCardExtensionStrings';
import {
  ICampusShuttleAdaptiveCardExtensionProps,
  ICampusShuttleAdaptiveCardExtensionState
} from '../CampusShuttleAdaptiveCardExtension';

import { IListItem, upsertListItem } from '../sp.service';

export interface IStartTripData {
  title: string;
  trip: IListItem;
}

export class StartTrip extends BaseAdaptiveCardView<
  ICampusShuttleAdaptiveCardExtensionProps,
  ICampusShuttleAdaptiveCardExtensionState,
  IStartTripData
> {

  public get data(): IStartTripData {
    return {
      title: strings.Title,
      trip: this.state.currentTrip
    };
  }

  public get template(): ISPFxAdaptiveCard {
    return require('./template/StartTripCard.json');
  }

  public onAction(action: IActionArguments): void {
    if (action.type === 'Submit') {
      if (action.data.tripType) {
        const trip = this.state.currentTrip;
        trip.Status = action.data.tripType;
        this.setState({ currentTrip: trip });
      }

      if (action.id === 'originLocation') {
        // TODO QuickView originLocation
      } else if (action.id === 'destinationLocation') {
        // TODO QuickView destinationLocation
      } else if (action.id === 'save') {
        (async () => {
          await upsertListItem(this.context, this.properties.listId, this.state.currentTrip);
          // TODO QuickView save
        })();
      }
    }
  }

}

StartTrip此文件中的 类包含三个成员:

  • data():此访问器成员将对象返回到自适应卡片呈现引擎,该引擎将用于将属性绑定到用于实现 QuickView 的自适应卡片。
  • template():此访问器成员返回包含自适应卡片定义的 JSON 对象。
  • onAction():当自适应卡中发生某些操作时,将调用此方法。 此时,代码只需保存行程类型 ( | 在) 过程中的值,并包含将添加到项目的更多 QuickView 卡片的占位符。

若要简化项目将包含的 QuickView 的引用,请将一个新的文件 index.ts 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹中,其中包含以下代码:

export * from './StartTrip';

注册并引用 StartTrip QuickView

若要使用此新 QuickView,需要将其注册到 ACE 的 QuickView 导航器。 打开 ./src/adaptiveCardExtensions/campusShuttle/CampusShuttleAdaptiveCardExtension.ts 文件。

在现有 import 语句后面添加以下 import 语句,以导入你创建的新 QuickView:

import {
  StartTrip
} from './quickView';

在类声明之前找到常量, CampusShuttleAdaptiveCardExtension 并添加以下声明:

export const QUICK_VIEW_START_TRIP_REGISTRY_ID: string = 'CampusShuttle_StartTrip_QUICK_VIEW';

接下来,在 class' onInit() 方法中CampusShuttleAdaptiveCardExtension,找到以下行:

this.cardNavigator.register(CARD_VIEW_REGISTRY_ID, () => new CardView());

添加以下行,将 StartTrip QuickView 注册到 QuickView 导航器。

this.quickViewNavigator.register(QUICK_VIEW_START_TRIP_REGISTRY_ID, () => new StartTrip());

将 StartTrip QuickView 添加到 CardView

最后一步是将 QuickView 添加到 CardView 以使用它。 在 VS Code 中找到并打开以下文件: ./src/adaptiveCardExtensions/campusShuttle/cardView/CardView.ts

import找到从包含 ACE 类声明的文件导入属性和状态接口的语句。 向其添加包含 StartTrip QuickView ID 的常量:

import {
  ICampusShuttleAdaptiveCardExtensionProps,
  ICampusShuttleAdaptiveCardExtensionState,
  QUICK_VIEW_START_TRIP_REGISTRY_ID   // << add this
} from '../CampusShuttleAdaptiveCardExtension';

接下来,在cardButtons()访问器的 switch 语句中,在现有 default 之前添加 case 语句,以显示一个按钮,用于在当前驱动程序的状态可用时预订行程:

switch (this.state.currentTrip.Status) {
  case STATUS_AVAILABLE:
    return [{
      title: 'Book a Trip',
      action: {
        type: 'QuickView',
        parameters: { view: QUICK_VIEW_START_TRIP_REGISTRY_ID }
      }
    }];
    break;
  default:
    return undefined;
    break;
}

最后,注释掉或删除对 对象的以下引用 strings

import * as strings from 'SharePointRestAdaptiveCardExtensionStrings';

测试 ACE 以验证 QuickView 是否正常工作。 如果以前停止了本地 Web 服务器,请在控制台中执行以下操作来重启它:

gulp serve --nobrowser

导航到 SharePoint 托管的工作台,查看 Campus Shuttle ACE:

在编辑模式下呈现的 ACE 的屏幕截图。

请注意 CardView 上的“ 预订行程 ”按钮。 通过选择顶部导航右上角的 “预览” 链接来测试它。 此页面将从编辑模式切换到显示模式,可在其中选择按钮:

预览或显示模式下呈现的 ACE 的屏幕截图。

现在,让我们实现 StartTrip QuickView 的功能。

将功能添加到 StartTrip QuickView

现在,你将添加 StartTrip QuickView 将使用的三个 QuickView。 一个处理设置行程的出发地位置,另一个处理选择或设置行程的目标,最后一个处理在保存行程时作为确认通知。

  • 使用以下代码将新文件 SetOriginCard.json 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView/template 文件夹:

    {
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "type": "AdaptiveCard",
      "version": "1.2",
      "body": [
        {
          "type": "TextBlock",
          "weight": "Bolder",
          "size": "large",
          "text": "${title}"
        },
        {
          "type": "TextBlock",
          "text": "${description}",
          "wrap": true
        }
      ],
      "actions": [
        {
          "id": "originLocation",
          "type": "VivaAction.GetLocation",
          "title": "Select location on the map",
          "parameters": {
            "chooseLocationOnMap": true
          }
        }
      ]
    }
    

    请注意,此自适应卡片中的单个操作设置为 类型 VivaAction.GetLocation。 这将提示用户从其设备中选择一个位置并返回坐标。

  • 通过使用以下代码将新文件 SetOrigin.ts 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹,实现 SetOrigin QuickView

    import {
      ISPFxAdaptiveCard,
      BaseAdaptiveCardView,
      IGetLocationActionArguments
    } from '@microsoft/sp-adaptive-card-extension-base';
    import {
    
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState
    } from '../CampusShuttleAdaptiveCardExtension';
    
    import { ILocation, IListItem } from '../sp.service';
    
    export interface ISetOriginData {
      title: string;
      description: string;
      trip: IListItem;
    }
    
    export class SetOrigin extends BaseAdaptiveCardView<
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState,
      ISetOriginData
    > {
      public get data(): ISetOriginData {
        return {
          title: 'Set trip starting location',
          description: 'Select the trip origin location by selecting it on the map.',
          trip: this.state.currentTrip
        };
      }
    
      public get template(): ISPFxAdaptiveCard {
        return require('./template/SetOriginCard.json');
      }
    
      public onAction(action: IGetLocationActionArguments): void {
        if (action.type === 'VivaAction.GetLocation'){
    
          const currentTrip = this.state.currentTrip;
          currentTrip.OriginLocation = <ILocation> {
            latitude: action.location.latitude,
            longitude: action.location.longitude
          };
    
          this.setState({ currentTrip: currentTrip });
    
          this.quickViewNavigator.pop();
        }
      }
    }
    

    请注意,当自适应卡片提交 VivaAction.GetLocation 时,此代码在事件处理程序中onAction()如何获取所选位置。 它设置行程中的选定位置,并更新 ACE 状态中的 trip 对象。 对 的最后一 this.quickViewNavigator.pop() 次调用从 QuickView 堆栈中删除此 QuickView,这会触发堆栈中下一个 QuickView 的重新呈现。

现在实现类似的 QuickView 来设置目标位置:

  • 使用以下代码将新文件 SetDestinationCard.json 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView/template 文件夹:

    {
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "type": "AdaptiveCard",
      "version": "1.2",
      "body": [
        {
          "type": "TextBlock",
          "weight": "Bolder",
          "text": "${title}"
        },
        {
          "type": "TextBlock",
          "text": "${description}"
        },
        {
          "type": "TextBlock",
          "text": "Select a known location..."
        },
        {
          "id": "knownDestinationSelection",
          "type": "Input.ChoiceSet",
          "choices": [
            {
              "$data": "${campus_locations}",
              "title": "${title}",
              "value": "${latitude},${longitude}"
            }
          ]
        },
        {
          "type": "TextBlock",
          "text": "... or select a specific location on the map:"
        }
      ],
      "actions": [
        {
          "id": "destinationLocation",
          "type": "VivaAction.GetLocation",
          "title": "Select trip destination from map",
          "parameters": { "chooseLocationOnMap": true }
        },
        {
          "id": "save",
          "type": "Action.Submit",
          "title": "Save destination location",
          "style": "positive"
        }
      ]
    }
    

    此自适应卡片类似于用于源位置的卡,它允许用户在地图上选择位置,或从预定义位置列表中选择。 这些预定义位置位于 ./src/adaptiveCardExtensions/campusShuttle/assets/campus_locations.json 文件中。

    自适应卡片还包含另一个提交操作按钮。 与设置源 QuickView 不同,此按钮将用于关闭设置的目标 QuickView,因为用户有两个选项用于选择行程目的地。

  • 通过使用以下代码将新文件 SetDestination.ts 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹,实现 SetDestination QuickView:

    import {
      ISPFxAdaptiveCard,
      BaseAdaptiveCardView,
      IActionArguments,
      IGetLocationActionArguments
    } from '@microsoft/sp-adaptive-card-extension-base';
    import {
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState
    } from '../CampusShuttleAdaptiveCardExtension';
    
    import { ILocation, IListItem } from '../sp.service';
    
    import { sortBy } from '@microsoft/sp-lodash-subset';
    
    interface ICampusLocations {
      title: string;
      latitude: number;
      longitude: number;
    }
    
    export interface ISetDestinationData {
      title: string;
      description: string;
      campus_locations: ICampusLocations[];
      trip: IListItem;
    }
    
    const LOCATIONS = require('../assets/campus_locations.json');
    
    export class SetDestination extends BaseAdaptiveCardView<
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState,
      ISetDestinationData
    > {
      public get data(): ISetDestinationData {
        return {
          title: 'Set trip destination location',
          description: 'Pick from a list of known locations, or set the destination by selecting it on the map.',
          campus_locations: sortBy(LOCATIONS, (l) => l.title),
          trip: this.state.currentTrip
        };
      }
    
      public get template(): ISPFxAdaptiveCard {
        return require('./template/SetDestinationCard.json');
      }
    
      public onAction(action: IActionArguments | IGetLocationActionArguments): void {
        const currentTrip = this.state.currentTrip;
    
        // if picked a location on the map...
        if (action.type === 'VivaAction.GetLocation') {
          currentTrip.DestinationLocation = <ILocation>{
            latitude: action.location.latitude,
            longitude: action.location.longitude
          };
          this.setState({ currentTrip: currentTrip });
        } else if (action.type === 'Submit' && action.id === 'save') {
          // else, check if picked location from dropdown & save it
          if (action.data.knownDestinationSelection) {
            currentTrip.DestinationLocation = <ILocation>{
              latitude: Number(action.data.knownDestinationSelection.split(',')[0]),
              longitude: Number(action.data.knownDestinationSelection.split(',')[1])
            };
    
           const selectedLocation = LOCATIONS.filter((knownLocation: any) => (
              knownLocation.latitude === (currentTrip.DestinationLocation as ILocation).latitude
              && knownLocation.longitude === (currentTrip.DestinationLocation as ILocation).longitude
            ))[0];
            currentTrip.DestinationName = selectedLocation.title;
          }
          this.setState({ currentTrip: currentTrip });
          this.quickViewNavigator.pop();
        }
      }
    
    }
    

最后一步是将保存确认 QuickView 添加到项目:

  • 使用以下代码将新文件 SaveTripCard.json 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView/template 文件夹:

    {
      "schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "type": "AdaptiveCard",
      "version": "1.2",
      "body": [{
        "type": "TextBlock", "text": "${title}"
      }],
      "actions": [{
        "type": "Action.Submit",
        "id": "close",
        "title": "Close"
      }]
    }
    
  • 使用以下代码将新文件 SaveTrip.ts 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹,以实现 SaveTrip QuickView

    import {
      BaseAdaptiveCardView,
      IActionArguments,
      ISPFxAdaptiveCard
     } from '@microsoft/sp-adaptive-card-extension-base';
    import {
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState
    } from '../CampusShuttleAdaptiveCardExtension';
    
    export interface ISaveTripData {
      title: string;
    }
    
    export class SaveTrip extends BaseAdaptiveCardView<
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState,
      ISaveTripData
    > {
      public get data(): ISaveTripData {
        return {
          title: 'Trip saved successfully.'
        };
      }
    
      public get template(): ISPFxAdaptiveCard {
        return require('./template/SaveTripCard.json');
      }
    
      public onAction(action: IActionArguments): void {
        if (action.id === 'close') {
          this.quickViewNavigator.close();
        }
      }
    }
    

若要使用这三个新的 QuickView,需要注册它们:

  1. 打开 ./src/adaptiveCardExtensions/campusShuttle/quickView/index.ts 文件并导出新的 QuickView:

    export * from './StartTrip';
    export * from './SetOrigin';
    export * from './SetDestination';
    export * from './SaveTrip';
    
  2. 打开 ./src/adaptiveCardExtensions/campusShuttle/CampusShuttleAdaptiveCardExtension.ts 文件。

    1. 更新添加的现有 import 语句以导入 StartTrip QuickView 以引用三个新的 QuickView:

      import {
        StartTrip,
        SetOrigin,
        SetDestination,
        SaveTrip
      } from './quickView';
      
    2. 找到声明 CardView 和 QuickViews ID 的常量,并为新的 QuickView 添加以下 ID:

      export const QUICK_VIEW_SET_ORIGIN_REGISTRY_ID: string = 'CampusShuttle_SetOrigin_QUICK_VIEW';
      export const QUICK_VIEW_SET_DESTINATION_REGISTRY_ID: string = 'CampusShuttle_SetDestination_QUICK_VIEW';
      export const QUICK_VIEW_SAVE_TRIP_REGISTRY_ID: string = 'CampusShuttle_SaveTrip_QUICK_VIEW';
      
    3. onInit()在 类的 方法中CampusShuttleAdaptiveCardExtension,在对注册 StartTrip QuickView 的 this.quickViewNavigator.register() 语句的现有调用之后添加以下代码:

      this.quickViewNavigator.register(QUICK_VIEW_SET_ORIGIN_REGISTRY_ID, () => new SetOrigin());
      this.quickViewNavigator.register(QUICK_VIEW_SET_DESTINATION_REGISTRY_ID, () => new SetDestination());
      this.quickViewNavigator.register(QUICK_VIEW_SAVE_TRIP_REGISTRY_ID, () => new SaveTrip());
      

最后一步是将 QuickView 连接到现有的 StartTrip QuickView。

  1. 找到并打开 ./src/adaptiveCardExtensions/campusShuttle/quickView/StartTrip.ts 文件。

  2. 在模块中找到CampusShuttleCopilotAdaptiveCardExtension引用属性&状态接口的现有import语句。 更新它以导入三个新 QuickView 的三个常量:

    import {
      ICampusShuttleCopilotAdaptiveCardExtensionProps,
      ICampusShuttleCopilotAdaptiveCardExtensionState,
      QUICK_VIEW_SET_ORIGIN_REGISTRY_ID,
      QUICK_VIEW_SET_DESTINATION_REGISTRY_ID,
      QUICK_VIEW_SAVE_TRIP_REGISTRY_ID
    } from '../CampusShuttleAdaptiveCardExtension';
    
  3. 通过将 方法中的onAction()注释// TODO QuickView originLocation替换为以下内容来实现 SetOrigin QuickView:

    this.quickViewNavigator.push(QUICK_VIEW_SET_ORIGIN_REGISTRY_ID);
    
  4. 通过将 方法中的onAction()注释// TODO QuickView destinationLocation替换为以下内容来实现 SetDestination QuickView:

    this.quickViewNavigator.push(QUICK_VIEW_SET_DESTINATION_REGISTRY_ID);
    
  5. 通过将 方法中的onAction()注释// TODO QuickView save替换为以下内容来实现 SaveTrip QuickView:

    this.quickViewNavigator.push(QUICK_VIEW_SAVE_TRIP_REGISTRY_ID);
    

请注意,其中每个调用都在 QuickView 推送到导航器上。 这会触发使用堆栈上的新项重新呈现 QuickView。 回想一下,以前我们从堆栈中 弹出 QuickViews。

最后一步是在预订行程后更新 CardView 显示。

  1. 找到并打开 ./src/adaptiveCardExtensions/campusShuttle/cardView/CardView.ts 文件。

  2. 将现有 import 语句 import { STATUS_AVAILABLE } from '../sp.service';替换为以下代码:

    import {
      ILocation,
      STATUS_AVAILABLE,
      STATUS_ENROUTE,
      STATUS_HIRED
    } from '../sp.service';
    
  3. switch 方法中的 cardButtons() 语句更新为以下代码。

    switch (this.state.currentTrip.Status) {
      case STATUS_AVAILABLE:
        return [{
          title: 'Book a Trip',
          action: {
            type: 'QuickView',
            parameters: { view: QUICK_VIEW_START_TRIP_REGISTRY_ID }
          }
        }];
        break;
      case STATUS_ENROUTE:
        return [
          {
            title: 'View pickup location',
            action: {
              type: 'VivaAction.ShowLocation',
              parameters: {
                locationCoordinates: {
                  latitude: (this.state.currentTrip.OriginLocation as ILocation).latitude,
                  longitude: (this.state.currentTrip.OriginLocation as ILocation).longitude
                }
              }
            }
          }
        ];
        break;
      case STATUS_HIRED:
        return [
          {
            title: 'View dropoff location',
            action: {
              type: 'VivaAction.ShowLocation',
              parameters: {
                locationCoordinates: {
                  latitude: (this.state.currentTrip.DestinationLocation as ILocation).latitude,
                  longitude: (this.state.currentTrip.DestinationLocation as ILocation).longitude
                }
              }
            }
          }
        ];
        break;
      default:
        return undefined;
        break;
    }
    

    这些更改将有条件地显示不同的按钮和文本,具体取决于当前航天飞机驱动程序的状态。 请注意,我们现在使用 CardView 中的 操作 VivaAction.ShowLocation 来显示指定的起点和目标位置。

  4. 最后,将 return 方法中的 data() 语句更新为以下代码。

    return {
      primaryText: strings.PrimaryText,
      description: (this.state.currentTrip.Status === STATUS_AVAILABLE)
        ? `available for hire`
        : (this.state.currentTrip.Status === STATUS_ENROUTE)
          ? `Booked - ${STATUS_ENROUTE} to pickup...`
          : (this.state.currentTrip.DestinationName)
            ? `Hired - driving passenger to ${this.state.currentTrip.DestinationName}...`
            : `Hired - driving passenger to destination...`,
      title: this.properties.title
    };
    

    请注意,这将根据航天飞机司机的行程状态更改条件消息。

测试行程创建体验

此时,可以在浏览器中测试完整的行程创建体验。

如果以前停止了本地 Web 服务器,请在控制台中执行以下操作来重启它:

gulp serve --nobrowser

导航到 SharePoint 托管的工作台,查看 Campus Shuttle ACE。

当卡大小设置为“ 中等”时,ACE 只会在 CardView 上显示一个按钮。 当页面处于编辑模式时,打开属性窗格并将卡片的大小更改为 “大 ”,以便这两个按钮将显示在 CardView 上。

接下来,选择工具栏右上角的 “预览” 按钮。

首先选择初始 CardView 上的“ 预订行程 ”按钮。 这会显示之前测试的 “开始行程 ”快速查看。

选择 “ (1) 从地图中选择行程来源 ”按钮以加载 SetOrigin QuickView。 现在,选择 地图上的“选择位置” 按钮。 如果尚未授予浏览器访问位置的权限,系统会提示你执行此操作。 必须批准此请求, VivaAction.GetLocation 操作才能正常工作:

浏览器提示授予对位置的访问权限的屏幕截图。

授予浏览器访问你的位置的权限后,将显示一个包含地图的对话框。 将地图居中定位为行程的上车位置,选择上车并选择“ 共享位置 ”按钮。

选择行程取件的原点位置的屏幕截图。

选择 (2) “选择/设置行程目标 ”按钮以加载 SetDestination QuickView。 这一次,从下拉框中的已知位置之一中进行选择,然后选择“ 保存目标位置 ”按钮。

选择行程目标位置的屏幕截图。

最后,选择要 上车的 行程状态,然后选择“ 保存行程 ”按钮。

显示保存确认 QuickView 的屏幕截图。

保存行程后,请注意,CardView 现在显示基于当前上下文的不同按钮和文本。

司机前往行程取件途中的 CardView 的屏幕截图。

最后,浏览到包含行程数据的列表,以查看测试行程数据的存储方式:

SharePoint 列表中行程的屏幕截图。

校园穿梭巴士 ACE 是良好的旅行创建。 最后两个步骤是实施司机在路上接乘客和将乘客赶到目的地的方案。

在前往乘客取件途中添加功能

现在,让我们实现司机在路上接乘客的方案。

  • 使用以下代码将新文件 UpdateTripCard.json 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView/template 文件夹:

    {
      "schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "type": "AdaptiveCard",
      "version": "1.2",
      "body": [
        {
          "type": "TextBlock",
          "weight": "Bolder",
          "text": "${title}"
        }
      ],
      "actions": [
        {
          "id": "cancel",
          "type": "Action.Submit",
          "title": "Cancel Current Trip"
        },
        {
          "id": "pickup",
          "type": "Action.Submit",
          "title": "Pickup Passenger",
          "style": "positive"
        }
      ]
    }
    
  • 使用以下代码将新文件 UpdateTrip.ts 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹,以实现 UpdateTrip QuickView

    import {
      IActionArguments,
      ISPFxAdaptiveCard,
      BaseAdaptiveCardView
    } from '@microsoft/sp-adaptive-card-extension-base';
    import {
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState
    } from '../CampusShuttleAdaptiveCardExtension';
    
    import {
      STATUS_HIRED,
      upsertListItem
    } from '../sp.service';
    
    export interface IUpdateTripData {
      title: string;
    }
    
    export class UpdateTrip extends BaseAdaptiveCardView<
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState,
      IUpdateTripData
    > {
      public get data(): IUpdateTripData {
        return {
          title: 'Update the existing trip'
        };
      }
    
      public get template(): ISPFxAdaptiveCard {
        return require('./template/UpdateTripCard.json');
      }
    
      public onAction(action: IActionArguments): void {
        if (action.type !== 'Submit') { return; }
    
        switch (action.id) {
          case 'cancel':
            // TODO QuickView cancelTrip
            break
          case 'pickup':
            // update current item status
            const trip = this.state.currentTrip;
            trip.Status = STATUS_HIRED;
    
            // save to list
            (async () => {
              await upsertListItem(this.context, this.properties.listId, trip);
            })();
    
            // update ACE
            this.setState({ currentTrip: trip });
    
            this.quickViewNavigator.close();
            break
          default:
            return;
        }
      }
    
    }
    

如果司机希望通过取消行程或运送乘客来删除行程,我们的项目需要一个确认步骤。 为了实现这一点,我们将使用单个确认 QuickView 来动态处理这两种情况。

  • 使用以下代码将新文件 ConfirmationCard.json 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView/template 文件夹:

    {
      "schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "type": "AdaptiveCard",
      "version": "1.2",
      "body": [
        {
          "type": "TextBlock",
          "text": "${title}",
          "size": "Large"
        },
        {
          "type": "TextBlock",
          "text": "${description}"
        }
      ],
      "actions": [
        {
          "id": "confirm",
          "type": "Action.Submit",
          "title": "${title}",
          "style": "positive"
        }
      ]
    }
    
  • 通过使用以下代码将新文件 ConfirmationQuickView.ts 添加到 ./src/adaptiveCardExtensions/campusShuttle/quickView 文件夹,实现确认 QuickView:

    import {
      IActionArguments,
      ISPFxAdaptiveCard,
      BaseAdaptiveCardView
    } from '@microsoft/sp-adaptive-card-extension-base';
    import {
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState
    } from '../CampusShuttleAdaptiveCardExtension';
    
    import {
      deleteListItem,
      STATUS_AVAILABLE
    } from '../sp.service';
    
    export interface IConfirmationQuickViewData {
      title: string;
      description: string;
    }
    
    export class ConfirmationQuickView extends BaseAdaptiveCardView<
      ICampusShuttleAdaptiveCardExtensionProps,
      ICampusShuttleAdaptiveCardExtensionState,
      IConfirmationQuickViewData
    > {
      constructor(private confirmType: 'cancel' | 'complete') {
        super();
      }
    
      public get data(): IConfirmationQuickViewData {
        return {
          title: `${this.confirmType.substring(0,1).toUpperCase()}${this.confirmType.substring(1,this.confirmType.length)} Trip`,
          description: `Are you sure you want to ${this.confirmType} the trip?`
        };
      }
    
      public get template(): ISPFxAdaptiveCard {
        return require('./template/ConfirmationCard.json');
      }
    
      public onAction(action: IActionArguments): void {
        if (action.type === 'Submit' && action.id === 'confirm') {
          (async () => {
            // delete list item
            await deleteListItem(this.context, this.properties.listId, Number(this.state.currentTrip.Id));
          })();
    
          // update state to initial value
          this.setState({
            currentTrip: {
              Title: this.context.pageContext.user.loginName,
              Status: STATUS_AVAILABLE
            }
          });
    
          // close
          this.quickViewNavigator.close();
        }
      }
    }
    

若要使用这三个新的 QuickView,需要注册它们:

  1. 打开 ./src/adaptiveCardExtensions/campusShuttle/quickView/index.ts 文件并导出新的 QuickView:

    // .. existing export statements
    export * from './UpdateTrip';
    export * from './ConfirmationQuickView';
    
  2. 打开 ./src/adaptiveCardExtensions/campusShuttle/CampusShuttleAdaptiveCardExtension.ts 文件。

    1. 更新现有 import 以导入 QuickView 以引用新的 QuickView:

      import {
        StartTrip,
        SetOrigin,
        SetDestination,
        SaveTrip,
        UpdateTrip,            // << add
        ConfirmationQuickView  // << add
      } from './quickView';
      
    2. 找到声明 CardView 和 QuickViews ID 的常量,并为新的 QuickView 添加以下 ID:

      export const QUICK_VIEW_CANCEL_TRIP_REGISTRY_ID: string = 'CampusShuttleCopilot_CancelTrip_QUICK_VIEW';
      export const QUICK_VIEW_COMPLETE_TRIP_REGISTRY_ID: string = 'CampusShuttleCopilot_CompleteTrip_QUICK_VIEW';
      export const QUICK_VIEW_UPDATE_TRIP_REGISTRY_ID: string = 'CampusShuttleCopilot_UpdateTrip_QUICK_VIEW';
      

      请注意前两个常量如何不引用确认 QuickView? 这是因为我们将创建同一 QuickView 的两个实现。

    3. onInit()在 类的 方法中CampusShuttleAdaptiveCardExtension,在对注册 StartTrip QuickView 的 this.quickViewNavigator.register() 语句的现有调用之后添加以下代码:

      this.quickViewNavigator.register(QUICK_VIEW_CANCEL_TRIP_REGISTRY_ID, () => new ConfirmationQuickView('cancel'));
      this.quickViewNavigator.register(QUICK_VIEW_COMPLETE_TRIP_REGISTRY_ID, () => new ConfirmationQuickView('complete'));
      this.quickViewNavigator.register(QUICK_VIEW_UPDATE_TRIP_REGISTRY_ID, () => new UpdateTrip());
      

最后一步是将 QuickView 连接到现有的 CardView 和 QuickView。

  1. 找到并打开 ./src/adaptiveCardExtensions/campusShuttle/cardView/CardView.ts 文件。

    1. 在模块中找到CampusShuttleCopilotAdaptiveCardExtension引用属性和状态接口的现有import语句。 更新它以导入更新完成新 QuickView 的三个常量 & :

      import {
        ICampusShuttleAdaptiveCardExtensionProps,
        ICampusShuttleAdaptiveCardExtensionState,
        QUICK_VIEW_START_TRIP_REGISTRY_ID,
        QUICK_VIEW_UPDATE_TRIP_REGISTRY_ID,    // << add
        QUICK_VIEW_COMPLETE_TRIP_REGISTRY_ID   // << add
      } from '../CampusShuttleAdaptiveCardExtension';
      
    2. cardButton()在 访问器方法中,更新 switch 语句以添加更多按钮:

      1. 将以下按钮添加到 语句:case STATUS_ENROUTEreturn

        {
          title: 'Update Trip',
          action: {
            type: 'QuickView',
            parameters: { view: QUICK_VIEW_UPDATE_TRIP_REGISTRY_ID }
          }
        }
        
      2. 将以下按钮添加到 语句:case STATUS_HIREDreturn

        {
          title: 'Complete Trip',
          action: {
            type: 'QuickView',
            parameters: { view: QUICK_VIEW_COMPLETE_TRIP_REGISTRY_ID }
          }
        }
        
  2. 找到并打开 ./src/adaptiveCardExtensions/campusShuttle/quickView/UpdateTrip.ts 文件。

    1. 在模块中找到CampusShuttleCopilotAdaptiveCardExtension引用属性和状态接口的现有import语句。 更新它以导入更新完成新 QuickView 的三个常量 & :

      import {
        ICampusShuttleAdaptiveCardExtensionProps,
        ICampusShuttleAdaptiveCardExtensionState,
        QUICK_VIEW_CANCEL_TRIP_REGISTRY_ID    // << add
      } from '../CampusShuttleAdaptiveCardExtension';
      
  3. 通过将 方法中的onAction()注释// TODO QuickView cancelTrip替换为以下内容来实现 SetOrigin QuickView:

    this.quickViewNavigator.push(QUICK_VIEW_CANCEL_TRIP_REGISTRY_ID);
    

测试行程管理体验

此时,可以在浏览器中测试行程管理体验。

如果以前停止了本地 Web 服务器,请在控制台中执行以下操作来重启它:

gulp serve --nobrowser

导航到 SharePoint 托管的工作台以查看 Campus Shuttle ACE,然后选择工具栏右上角的 “预览 ”按钮。

请注意,CardView 现在包含 “更新行程 ”按钮。

更新的 CardView 的屏幕截图。

选择“ 更新行程 ”按钮,请注意有两个选项可用于取消或接载乘客。

UpdateTrip QuickView 的屏幕截图。

选择“ 取消当前行程 ”按钮将重置 ACE 状态,并从 SharePoint 列表中删除驱动程序的记录。 选择“ 取件乘客 ”按钮将更新 ACE 状态和 SharePoint 列表项中的行程,以将行程状态从 “途中 ”更改为 “已租用”。

选择“ 取件乘客 ”按钮。 请注意 CardView 上下文和按钮的更改,以指示行程状态的更改:

司机将乘客带到目的地时的 CardView 的屏幕截图。

最后,选择“ 完成行程 ”按钮以查看确认“快速查看”,然后选择“ 完成行程 ”按钮进行确认。 这将完成行程。

确认 QuickView 的屏幕截图。

在本练习中,你创建了一个 SPFx ACE,其中包含使用 Viva Connections 中的地理位置功能的主要文本卡片选项。

知识检验

1.

开发人员如何通过上下文数据使 CardView 和 QuickViews 动态?

2.

使用 VivaConnections.SelectMedia 操作时,开发人员如何将所选图像 () 保存到 SharePoint 文档库?

3.

若要在 VivaLocation.ShowLocation 操作上设置位置,开发人员可以将 GPS 坐标作为纬度和经度或位置的街道地址提供。