SharePoint Framework (SPFx) 大型列表 Web 部件,使用 React 和 Office UI 组件





5.00/5 (1投票)
SharePoint 框架 Web 部件,用于使用 React 和 REST API 检索 SharePoint 列表项,并使用 DetailsList Office UI fabric 组件显示结果。
引言
此 Web 部件将检索超出阈值限制的 SharePoint 列表项,并使用 DetailsList
Office UI fabric 组件显示结果。由于 SharePoint 列表视图的阈值限制,没有管理员权限的用户将无法浏览 SharePoint 列表项。添加和配置此 Web 部件将允许至少拥有站点读取权限的用户查看列表中的所有项。
先决条件
- 通过运行 Yeoman SharePoint 生成器创建项目
ViewAllItemsWebPart
- 安装 React 分页组件
- 安装 jQuery
Using the Code
以下是作为此 Web 部件一部分创建的组件:
- 自定义属性窗格 - 一个自定义属性窗格控件,用于显示多选下拉列表,以便在选择
list
属性时填充列。有关如何创建自定义属性窗格控件,请参考此 链接。 - Config - 如果 Web 部件属性未配置,则会渲染 Config 组件。
- Paging - 这是 React 分页组件。用于为列表视图添加分页。
- ViewAllItems - 渲染
DetailsList
Office UI fabric 组件并将列表项绑定到它的组件。 - services - 此处包含使用 Rest API 从 SharePoint 获取列表和列的方法。
让我们开始修改 Yeoman 生成器生成的脚手架。
WebPart.manifest.json
编辑 Web 部件清单 JSON 文件属性部分,以包含三个属性:列表名称(listName
)、列名称(selectedIds
)和页面大小(pageSize
),并设置默认值,如下所示。
"properties": {
"listName": "",
"selectedIds":[],
"pageSize": 100
}
定义 Web 部件属性常量和属性类型
在 loc 文件夹下,更新 en-us.js 和 mystrings.js 以包含以下脚本。这些脚本用于定义 Web 部件属性窗格配置中所需的属性常量和属性类型。
en-us.js
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"ListNameFieldLabel": "Select List",
"ColumnFieldLabel": "Select Columns",
"PageSizeFieldLabel": "Select Page Size"
}
});
mystrings.d.ts
declare interface IViewAllItemsWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
ListNameFieldLabel: string;
ColumnFieldLabel: string;
PageSizeFieldLabel: string;
}
declare module 'ViewAllItemsWebPartStrings' {
const strings: IViewAllItemsWebPartStrings;
export = strings;
}
Webpart.ts
将以下 import
添加到 webpart.ts 文件中
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneDropdown,
IPropertyPaneDropdownOption
} from '@microsoft/sp-webpart-base';
import * as strings from 'ViewAllItemsWebPartStrings';
import ViewAllItems from './components/ViewAllItems';
import { IViewAllItemsProps } from './components/IViewAllItemsProps';
import { IList } from './services/IList';
import { IListColumn } from './services/IListColumn';
import { ListService } from './services/ListService';
import { MultiSelectBuilder, IItemProp, PropertyPaneMultiSelect }
from './CustomPropertyPane/PropertyPaneMultiSelect';
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
删除 getPropertyPaneConfiguration
方法中现有的代码行,并添加以下代码行:
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneDropdown('listName', {
label: strings.ListNameFieldLabel,
options: this.lists,
disabled: this.listsDropdownDisabled,
}),
PropertyPaneMultiSelect("selectedIds", {
label: "Select Columns",
selectedItemIds: this.properties.selectedIds, //Ids of Selected Items
onload: () => this.loadColumns(), //On load function to items
//for drop down
onPropChange: this.onPropertyPaneFieldChanged,// On Property Change
//function
properties: this.properties, //Web Part properties
key: "targetkey", //unique key
disabled: !this.properties.listName
}),
PropertyPaneDropdown('pageSize',{
label: strings.PageSizeFieldLabel,
options:[
{key: '10', text: '10'},
{key: '25', text: '25'},
{key: '50', text: '50'},
{key: '100', text: '100'}
]
})
]
}
]
}
]
};
}
PropertyPaneMultiselect
是自定义属性窗格组件。请遵循上面提供的链接将其添加到此 Web 部件项目中。
将 onPropertyPaneConfigurationStart
和 onPropertyPaneFieldChanged
方法添加到 webpart
类中,以创建级联下拉列表并加载属性窗格控件的数据。
protected onPropertyPaneConfigurationStart(): void {
this.listsDropdownDisabled = !this.lists;
if (this.lists) {
return;
}
this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'lists');
this.loadLists()
.then((listOptions: IPropertyPaneDropdownOption[]): void => {
this.lists = listOptions;
this.listsDropdownDisabled = false;
this.context.propertyPane.refresh();
this.context.statusRenderer.clearLoadingIndicator(this.domElement);
this.render();
});
}
protected onPropertyPaneFieldChanged(propertyPath: string,
oldValue: any, newValue: any): void {
if (propertyPath === 'listName' && newValue) {
// push new list value
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
// get previously selected column
const previousItem: string[] = this.properties.selectedIds;
// reset selected item
this.properties.selectedIds = [];
// push new item value
this.onPropertyPaneFieldChanged('selectedIds', previousItem,
this.properties.selectedIds);
//this.columnsDropdown.render(this.domElement);
this.render();
// refresh the item selector control by repainting the property pane
this.context.propertyPane.refresh();
}
else {
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
}
}
如果您注意到上面的方法正在调用 loadLists
和 loadColumns
方法来获取数据,请将以下方法添加到 webpart
类中。
private loadLists(): Promise<IPropertyPaneDropdownOption[]> {
const dataService = new ListService(this.context);
return new Promise<IPropertyPaneDropdownOption[]>(resolve => {
dataService.getLists()
.then((response: IList[]) => {
var options : IPropertyPaneDropdownOption[] = [];
response.forEach((item: IList) => {
options.push({"key": item.Title, "text": item.Title});
});
resolve(options);
});
});
}
private loadColumns(): Promise<IItemProp[]> {
if (!this.properties.listName) {
// resolve to empty options since no list has been selected
return Promise.resolve();
}
const dataService = new ListService(this.context);
return new Promise<IItemProp[]>(resolve => {
dataService.getColumns(this.properties.listName)
.then((response) => {
var options : IItemProp[] = [];
this.properties.selectedColumnsAndType = [];
response.forEach((column: IListColumn) => {
options.push({"key": column.StaticName, "text": column.Title});
this.properties.selectedColumnsAndType.push
({"key": column.StaticName, "text": column.TypeDisplayName});
});
resolve(options);
});
});
}
反过来,这些方法会引用 ListService
类来获取数据。向解决方案添加新文件夹 services,并添加文件 ILists.ts、IListColumn.ts 和 ListService.ts。
ILists.ts
接口 IList.ts 定义了将从 REST API 请求中请求的列表元数据。此接口将获取 list id
和 title
。
export interface IList {
Id: string;
Title: string;
}
IListColumns.ts
接口 IListColumns
定义了将从 API 请求中请求的列元数据。这将获取列标题、内部名称和列类型。
export interface IListColumn {
Title: string;
StaticName: string;
TypeDisplayName: string;
}
ListService.ts
将以下脚本添加到 ListService.ts 文件中。此文件将包含 getLists
和 GetColumns
方法,这些方法通过将站点的当前上下文附加到 SharePoint REST API 作为 url
参数传递给 HTTP 请求来从 SharePoint 获取 Lists
和 Columns
信息。
export class ListService {
constructor(private context: IWebPartContext) {
}
public getLists(): Promise<IList[]> {
var httpClientOptions : ISPHttpClientOptions = {};
httpClientOptions.headers = {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
};
return new Promise<IList[]>((resolve: (results: IList[]) => void,
reject: (error: any) => void): void => {
this.context.spHttpClient.get(this.context.pageContext.web.serverRelativeUrl +
`/_api/web/lists?$select=id,title&$filter=Hidden eq false`,
SPHttpClient.configurations.v1,
httpClientOptions
)
.then((response: SPHttpClientResponse): Promise<{ value: IList[] }> => {
return response.json();
})
.then((lists: { value: IList[] }): void => {
resolve(lists.value);
}, (error: any): void => {
reject(error);
});
});
}
public getColumns(listName: string): Promise<IListColumn[]> {
var httpClientOptions : ISPHttpClientOptions = {};
httpClientOptions.headers = {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
};
return new Promise<IListColumn[]>((resolve: (results: IListColumn[]) => void,
reject: (error: any) => void): void => {
this.context.spHttpClient.get(this.context.pageContext.web.serverRelativeUrl +
`/_api/web/lists/GetByTitle('${listName}')/fields?
$filter=TypeDisplayName ne
'Attachments' and Hidden eq false and ReadOnlyField eq false`,
SPHttpClient.configurations.v1,
httpClientOptions
)
.then((response: SPHttpClientResponse): Promise<{ value: IListColumn[] }> => {
return response.json();
})
.then((listColumns: { value: IListColumn[] }): void => {
resolve(listColumns.value);
}, (error: any): void => {
reject(error);
});
});
}
}
将以下方法添加到 webpart
类中。当调用 Web 部件 render
方法时,这些方法将作为属性传递给 ReactDOM
元素。
needsCofiguration
- 这决定了渲染视图所需的webpart
属性是否已配置。selectedColumns
- 这将返回从自定义属性窗格控件中选择的列。configureWebPart
- 调用此方法将打开webpart
属性窗格。
private needsConfiguration(): boolean {
return this.properties.listName === null ||
this.properties.listName === undefined ||
this.properties.listName.trim().length === 0 ||
this.properties.selectedIds === null ||
this.properties.selectedIds === undefined ||
this.properties.selectedIds.length === 0;
}
private selectedColumns(): IItemProp[] {
if(this.properties.selectedColumnsAndType === null ||
this.properties.selectedColumnsAndType===undefined ||
this.properties.selectedColumnsAndType.length === 0){
return [];
}
else{
return this.properties.selectedColumnsAndType.filter
(obj => this.properties.selectedIds.indexOf(obj.key) !== -1);
}
}
private configureWebPart(): void {
this.context.propertyPane.open();
}
删除 Web 部件 render
方法中的现有代码,复制并粘贴以下代码:
public render(): void {
const element: React.ReactElement<IViewAllItemsProps> = React.createElement(
ViewAllItems,
{
spHttpClient: this.context.spHttpClient,
siteUrl: this.context.pageContext.web.absoluteUrl,
listName: this.properties.listName,
needsConfiguration: this.needsConfiguration(),
configureWebPart: this.configureWebPart,
displayMode: this.displayMode,
selectedColumns: this.selectedColumns(),
pageSize: this.properties.pageSize
}
);
ReactDom.render(element, this.domElement);
}
render
方法将加载 ViewAllItems.tsx 组件中的 JSX。向解决方案添加文件 ViewAllItems.tsx、IViewAllItemsProps.ts、ViewAllItems.module.scss。
IViewAllItemsProps.ts
此接口文件定义了将组件加载到 ReactDOM 所需的所有属性。
export interface IViewAllItemsProps {
spHttpClient: SPHttpClient;
siteUrl: string;
listName: string;
selectedColumns: IItemProp[];
needsConfiguration:boolean;
configureWebPart: () => void;
displayMode: DisplayMode;
pageSize: number;
}
ViewAllItems.module.scss
这些文件包含 ViewAllItems.tsx 文件中使用的所有样式。
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.viewAllItems {
.container {
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.status{
float:left
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,
BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}
ViewAllItems.tsx
ViewAllItems
类继承自 React.Component
,它期望 Props
和 State
作为参数。我们已经定义了 Props
(IViewAllItemsProps.ts)。在类上方的 tsx 文件中,定义 state
,如下所示。
export interface IViewAllItemsState {
items?: any[];
columns?:IColumn[];
status?: string;
currentPage?: number;
itemCount?: number;
pageSize?: number;
}
在类内部,添加构造函数的定义。这会在组件最初加载时设置默认的 props 和 state。
private selectQuery: string[] = [];//global variable to set the select query params
//for the rest api request
private expandQuery: string[] = [];//global variable to expand the params
//for people picker
//and lookup column types
constructor(props: IViewAllItemsProps){
super(props);
this.state ={
items:[],
columns: this.buildColumns(this.props),
currentPage:1,
pageSize: this.props.pageSize
};
this._onPageUpdate = this._onPageUpdate.bind(this);
this.getListItemsCount(`${this.props.siteUrl}/_api/web/lists/GetByTitle
('${props.listName}')/ItemCount`);
const queryParam = this.buildQueryParams(props);
this.readItems(`${this.props.siteUrl}/_api/web/lists/GetByTitle
('${props.listName}')/items${queryParam}`);
}
将 componentWillReceiveProps
方法添加到 ViewAllItems
类中。这是 React 的生命周期方法,如果此组件提供的属性发生更改,它将渲染 JSX。
public componentWillReceiveProps(nextProps: IViewAllItemsProps): void{
this.setState({
columns:this.buildColumns(nextProps),
pageSize: nextProps.pageSize
});
this.getListItemsCount(`${this.props.siteUrl}/_api/web/lists/GetByTitle
('${this.props.listName}')/ItemCount`);
//const selectColumns = nextProps.selectedColumns === null ||
//nextProps.selectedColumns===undefined ||
//nextProps.selectedColumns.length === 0? "" :
//'?$select='+nextProps.selectedColumns.join();
const queryParam = this.buildQueryParams(nextProps);
this.readItems(`${this.props.siteUrl}/_api/web/lists/GetByTitle
('${nextProps.listName}')/items${queryParam}`);
}
向类添加 render
方法,并使用以下代码片段。当 JSX 元素加载到 DOM 时,它将通过调用 webpart
类上的此方法来检查 webpart needsConfiguration
,该方法返回一个布尔值。如果方法返回 true
,它将把 config 组件 JSX 加载到 DOM,否则它将把当前 JSX 加载到 DOM。
public render(): JSX.Element {
const { needsConfiguration, configureWebPart} = this.props;
let {items, columns, pageSize} = this.state;
return (
<div className={styles.viewAllItems}>
<div>
{needsConfiguration &&
<Config configure={configureWebPart} {...this.props}/>
}
{ needsConfiguration === false &&
<div>
<div>
<div>
<div className={styles.status}>{this.state.status}</div>
<Paging
totalItems={ this.state.itemCount }
itemsCountPerPage={ this.state.pageSize }
onPageUpdate={ this._onPageUpdate }
currentPage={ this.state.currentPage }/>
<div></div>
<DetailsList
items = {items}
columns = {columns}
isHeaderVisible = {true}
layoutMode = {LayoutMode.justified}
constrainMode ={ConstrainMode.unconstrained}
checkboxVisibility={CheckboxVisibility.hidden}
onColumnHeaderClick={ this._onColumnClick }
/>
</div>
</div>
</div>
}
</div>
</div>
);
}
将 readItems
方法添加到 ViewAllItems
类中。此方法将从 SharePoint 返回列表项。
private readItems(url: string) {
this.setState({
items: [],
status: 'Loading all items...'
});
this.props.spHttpClient.get(url,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
}).then((response: SPHttpClientResponse): Promise<{value: any[]}> =>{
return response.json();
}).then((response: {value: any[]}): void => {
//this.props.Status(`${response.d.__next}`);
//this.props.siteUrl = response['odata.nextLink'];
this.setState({
items: response.value,
//columns: _buildColumns(response.value),
status: `Showing items ${(this.state.currentPage - 1)*this.props.pageSize +1} -
${(this.state.currentPage -1) * this.props.pageSize +
response.value.length}
of ${this.state.itemCount}`
});
}, (error: any): void => {
this.setState({
items: [],
status: 'Loading all items failed with error: ' + error
});
});
}
添加 getListItemsCount
方法,该方法返回列表项计数,分页组件需要此计数来构建分页。
Private getListItemsCount(url: string) {
this.props.spHttpClient.get(url,SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version':''
}
}).then((response: SPHttpClientResponse): Promise<{value: number}> =>{
return response.json();
}).then((response: {value: number}): void => {
this.setState({
itemCount: response.value
});
});
}
将 _onPageUpdate
方法添加到 ViewAllItems
类中。当在视图中单击页码时,将触发此方法。此方法通过将更新后的 url
作为参数传递来调用 readItems
方法。Url
是使用 skiptoken
、页面 ID 和页面大小作为参数构建的,以便在单击页码时从列表中获取正确的项。
private _onPageUpdate(pageNumber: number) {
this.setState({
currentPage: pageNumber,
});
const p_ID = (pageNumber - 1)*this.props.pageSize;
const selectColumns = '&$select='+this.selectQuery;
const expandColumns = '&$expand='+this.expandQuery;
const queryParam = `%24skiptoken=Paged%3dTRUE%26p_ID=${p_ID}&
$top=${this.props.pageSize}`;
var url = `${this.props.siteUrl}/_api/web/lists/GetByTitle
('${this.props.listName}')/items?`+
queryParam + selectColumns+expandColumns;
this.readItems(url);
}
将 _onColumnClick
方法添加到 ViewAllItems
类中。当用户单击列表视图中的列标题时,将触发此方法。此方法对显示的搜索结果进行排序。
private _onColumnClick(event: React.MouseEvent<HTMLElement>, column: IColumn) {
let { items, columns } = this.state;
let isSortedDescending = column.isSortedDescending;
// If we've sorted this column, flip it.
if (column.isSorted) {
isSortedDescending = !isSortedDescending;
}
// Sort the items.
items = items!.concat([]).sort((a, b) => {
let firstValue = a[column.fieldName];
let secondValue = b[column.fieldName];
if (isSortedDescending) {
return firstValue > secondValue ? -1 : 1;
} else {
return firstValue > secondValue ? 1 : -1;
}
});
// Reset the items and columns to match the state.
this.setState({
items: items,
columns: columns!.map(col => {
col.isSorted = (col.key === column.key);
if (col.isSorted) {
col.isSortedDescending = isSortedDescending;
}
return col;
})
});
}
将 buildQueryParams
和 buildColumns
方法添加到 ViewAllItems
类中。
buildQueryParams
- 此方法返回 SharePoint REST API 需要的url
查询字符串,用于选择和展开在webpart
属性窗格中选择的列。buildColumns
- 此方法返回DetailsList
Office UI fabric 组件生成视图所需的列数组。
private buildQueryParams(props: IViewAllItemsProps): string{
this.selectQuery = [];
this.expandQuery = [];
props.selectedColumns.forEach(element => {
if(element.text === "Person or Group" || element.text === "Lookup"){
this.selectQuery.push(element.key+"/Title");
this.expandQuery.push(element.key);
}
else{
this.selectQuery.push(element.key);
}
});
const queryParam = `?%24skiptoken=Paged%3dTRUE%26p_ID=1&$top=${props.pageSize}`;
const selectColumns = this.selectQuery === null || this.selectQuery===undefined ||
this.selectQuery.length === 0? "" : '&$select='+this.selectQuery.join();
const expandColumns = this.expandQuery === null || this.expandQuery===undefined ||
this.expandQuery.length === 0? "" : '&$expand='+this.expandQuery.join();
return queryParam+selectColumns+expandColumns;
}
private buildColumns(props: IViewAllItemsProps): IColumn[]{
const columns: IColumn[]=[];
props.selectedColumns.forEach(element => {
if(element.text === "Person or Group" || element.text === "Lookup"){
const column: IColumn ={
key: element.key,
name: element.key.indexOf("_x0020_") !== -1?
element.key.replace("_x0020_"," "):element.key,
fieldName: element.key,
minWidth: 100,
maxWidth: 350,
isResizable: true,
data: 'string',
onRender: (item: any) => {
return (
<span>
{ item[element.key]["Title"] }
</span>
);
}
};
columns.push(column);
}
else{
const column: IColumn ={
key: element.key,
name: element.key.indexOf("_x0020_") !== -1?
element.key.replace("_x0020_"," "):element.key,
fieldName: element.key,
minWidth: 100,
maxWidth: 350,
isResizable: true,
data: 'string',
isMultiline: element.text === "Multiple lines of text" ? true:false
};
columns.push(column);
}
});
return columns;
}
Config 组件
向解决方案添加文件夹 Config,然后向此文件夹添加文件 IConfigProp.ts、Config.module.scss 和 Config.tsx。
IConfigProp.ts
接口 IConfigProp.ts 定义了加载 Config.tsx 组件所需的属性。
import { DisplayMode } from '@microsoft/sp-core-library';
export interface IConfigProps {
displayMode: DisplayMode;
configure: () => void;
}
Config.module.scss
此文件包含样式化 Config.tsx 组件所需的样式。
.placeholder {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
.placeholderContainer {
-webkit-box-align: center;
-ms-flex-align: center;
-ms-grid-row-align: center;
align-items: center;
color: "[theme:neutralSecondary, default: #666666]";
background-color: "[theme:neutralLighter, default: #f4f4f4]";
width: 100%;
padding: 80px 0;
.placeholderHead {
color: "[theme:neutralPrimary, default: #333333]";
.placeholderHeadContainer {
height: 100%;
white-space: nowrap;
text-align: center;
}
.placeholderIcon {
display: inline-block;
vertical-align: middle;
white-space: normal;
}
.placeholderText {
display: inline-block;
vertical-align: middle;
white-space: normal
}
}
.placeholderDescription {
width: 65%;
vertical-align: middle;
margin: 0 auto;
text-align: center;
.placeholderDescriptionText {
color: "[theme:neutralSecondary, default: #666666]";
font-size: 17px;
display: inline-block;
margin: 24px 0;
font-weight: 100;
}
button {
font-size: 14px;
font-weight: 400;
box-sizing: border-box;
display: inline-block;
text-align: center;
cursor: pointer;
vertical-align: top;
min-width: 80px;
height: 32px;
background-color: "[theme:themePrimary, default: #0078d7]";
color: #fff;
user-select: none;
outline: transparent;
border-width: 1px;
border-style: solid;
border-color: transparent;
border-image: initial;
text-decoration: none;
}
}
}
}
[dir=ltr] .placeholder,
[dir=rtl] .placeholder {
.placeholderContainer {
.placeholderHead {
.placeholderText {
padding-left: 20px;
}
}
}
}
.placeholderOverlay {
position: relative;
height: 100%;
z-index: 1;
.placeholderSpinnerContainer {
position: relative;
width: 100%;
margin: 164px 0
}
}
Config.tsx
在 Config.tsx 中,用以下代码片段替换类。_handleButtonClick
事件将触发传递到此组件作为属性的 webpart.ts 文件中的 configureWebPart
方法。
import * as React from 'react';
import { Fabric } from 'office-ui-fabric-react';
import { DisplayMode } from '@microsoft/sp-core-library';
import { IConfigProps } from './IConfigProps';
import styles from './Config.module.scss';
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
export class Config extends React.Component<IConfigProps, {}> {
constructor(props: IConfigProps){
super(props);
this._handleBtnClick = this._handleBtnClick.bind(this);
}
public render(): JSX.Element {
return (
<Fabric>
{ this.props.displayMode === DisplayMode.Edit &&
<div className={`${styles.placeholder}`}>
<div className={styles.placeholderContainer}>
<div className={styles.placeholderHead}>
<div className={styles.placeholderHeadContainer}>
<i className={`${styles.placeholderIcon} ms-fontSize-su
ms-Icon ms-ICon--CheckboxComposite`}></i>
<span className={`${styles.placeholderText} ms-fontWeight-light
ms-fontSize-xxl`}>Configure your web part</span>
</div>
</div>
<div className={styles.placeholderDescription}>
<span className={styles.placeholderDescriptionText}>
Please configure the web part.</span>
</div>
<div className={styles.placeholderDescription}>
<PrimaryButton
text="Configure"
ariaLabel="Configure"
ariaDescription="Please configure the web part"
onClick={this._handleBtnClick} />
</div>
</div>
</div>
}
{ this.props.displayMode === DisplayMode.Read &&
<div className={`${styles.placeholder}`}>
<div className={styles.placeholderContainer}>
<div className={styles.placeholderHead}>
<div className={styles.placeholderHeadContainer}>
<i className={`${styles.placeholderIcon} ms-fontSize-su ms-Icon
ms-ICon--CheckboxComposite`}></i>
<span className={`${styles.placeholderText}
ms-fontWeight-light ms-fontSize-xxl`}>
Configure your web part</span>
</div>
</div>
<div className={styles.placeholderDescription}>
<span className={styles.placeholderDescriptionText}>
Please configure the web part.</span>
</div>
</div>
</div>
}
</Fabric>
);
}
private _handleBtnClick(event?: React.MouseEvent<HTMLButtonElement>) {
this.props.configure();
}
}
Paging 组件
向解决方案添加文件夹 Paging,然后向此文件夹添加文件 IPagingProp.ts、Paging.module.scss 和 Paging.tsx。
IPagingProp.ts
接口 IPagingProp.ts 定义了加载 Paging.tsx 组件所需的属性。
export interface IPagingProps {
totalItems: number;
itemsCountPerPage: number;
onPageUpdate: (pageNumber: number) => void;
currentPage: number;
}
Paging.module.scss
此文件包含样式化 Paging.tsx 组件所需的样式。
.paginationContainer {
text-align: right;
.searchWp__paginationContainer__pagination {
display: inline-block;
text-align: center;
ul {
display: inline-block;
padding-left: 0;
border-radius: 4px;
li {
display: inline;
a {
float: left;
padding: 5px 10px;
text-decoration: none;
border-radius: 15px;
i {
font-size: 10px;
}
}
a:visited {
color: inherit;
}
a.active {
background-color: #0078d7!important;
color: white!important;
}
}
}
}
}
Paging.tsx
在 Paging.tsx 中,用以下代码片段替换类。_onPageUpdate
事件将触发传递到此组件作为属性的 ViewAllItems.tsx 文件中的 _onPageUpdate
方法。
import * as React from "react";
import {IPagingProps} from "./IPagingProps";
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import Pagination from "react-js-pagination";
import styles from './Paging.module.scss';
export default class Paging extends React.Component<IPagingProps, null> {
constructor(props: IPagingProps) {
super(props);
this._onPageUpdate = this._onPageUpdate.bind(this);
}
public render(): React.ReactElement<IPagingProps> {
return(
<div className={`${styles.paginationContainer}`}>
<div className={`${styles.searchWp__paginationContainer__pagination}`}>
<Pagination
activePage={this.props.currentPage}
firstPageText={<i className="ms-Icon
ms-Icon--ChevronLeftEnd6" aria-hidden="true"></i>}
lastPageText={<i className="ms-Icon
ms-Icon--ChevronRightEnd6" aria-hidden="true"></i>}
prevPageText={<i className="ms-Icon
ms-Icon--ChevronLeft" aria-hidden="true"></i>}
nextPageText={<i className="ms-Icon
ms-Icon--ChevronRight" aria-hidden="true"></i>}
activeLinkClass={ `${styles.active}` }
itemsCountPerPage={ this.props.itemsCountPerPage }
totalItemsCount={ this.props.totalItems }
pageRangeDisplayed={10}
onChange={this.props.onPageUpdate}
/>
</div>
</div>
);
}
private _onPageUpdate(pageNumber: number): void {
this.props.onPageUpdate(pageNumber);
}
打包和部署
- 执行以下
gulp
任务来捆绑您的解决方案。这会通过使用动态标签作为资产的主机 URL 来执行项目的发布版本。此 URL 会根据您的租户 CDN 设置自动更新。gulp bundle --ship
- 执行以下任务来打包您的解决方案。这会在 sharepoint/solution 文件夹中创建一个更新的
webpart.sppkg
包。gulp package-solution --ship
- 将新创建的客户端解决方案包上传或拖放到租户中的应用目录。
- 根据您的租户设置,如果您的租户中未启用 CDN,并且 package-solution.json 中的
includeClientSideAssets
设置为true
,则资产的加载 URL 将动态更新,并直接指向应用目录网站集中的 ClientSideAssets 文件夹。
历史
- 2018年3月20日 - 文章发布