0

这是我第一次尝试使用 office ui fabric react JS 在 SharePoint Online 中开发 Web 部件应用程序。对于一个测试(CRUD 应用程序)项目,我开发了一个在线图书库应用程序。我一直在努力从共享点列表中保存/获取日期值,但由于某种原因出现错误,如下所示: 错误截图

我的 tsx 文件代码如下:

import * as React from 'react';
import styles from './HelloWorld.module.scss';
import { IHelloWorldProps } from './IHelloWorldProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { BookLibListItem } from './BookLibListItem';
import { IBookLibCollection } from './IBookLibCollection';


import { ISPHttpClientOptions, SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';


import {
  TextField,
  autobind,
  PrimaryButton,
  DetailsList,
  DetailsListLayoutMode,
  CheckboxVisibility,
  SelectionMode,
  Dropdown,
  DatePicker,
  IDatePickerStrings,
  IDropdown,
  IDropdownOption,
  ITextFieldStyles,
  IDropdownStyles,
  DetailsRowCheck,
  Selection,
  DayOfWeek
} from 'office-ui-fabric-react';



const DayPickerStrings: IDatePickerStrings = {
  months: [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ],

  shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],

  days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],

  shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],

  goToToday: 'Go to today',
  prevMonthAriaLabel: 'Go to previous month',
  nextMonthAriaLabel: 'Go to next month',
  prevYearAriaLabel: 'Go to previous year',
  nextYearAriaLabel: 'Go to next year',
  closeButtonAriaLabel: 'Close date picker'
};
/*
export interface IDatePickerBasicExampleState {
  firstDayOfWeek?: DayOfWeek;
}
*/

 // Configure the columns for the DetailsList component
 let _bookListColumns = [
  {
    key: 'ID',
    name: 'ID',
    fieldName: 'ID',
    minWidth: 50,
    maxWidth: 100,
    isResizable: true
  },
  {
    key: 'Title',
    name: 'Title',
    fieldName: 'Title',
    minWidth: 50,
    maxWidth: 100,
    isResizable: true
  },
  {
    key: 'Author',
    name: 'Author',
    fieldName: 'Author',
    minWidth: 50,
    maxWidth: 100,
    isResizable: true
  },
  {
    key: 'Publisher',
    name: 'Publisher',
    fieldName: 'Publisher',
    minWidth: 50,
    maxWidth: 100,
    isResizable: true
  },
  {
    key: 'DateOfPublish',
    name: 'DateOfPublish',
    fieldName: 'DateOfPublish',
    minWidth: 50,
    maxWidth: 100,
    isResizable: true
  },
  {
    key: 'ISBN',
    name: 'ISBN',
    fieldName: 'ISBN',
    minWidth: 50,
    maxWidth: 150,
    isResizable: true
  }  
];

const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: { width: 300 } };
const narrowTextFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: { width: 100 } };
const narrowDropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 300 } };

function SetDate(props) {
  //const firstDayOfWeek = React.useState(DayOfWeek.Sunday);
  
  return (<DatePicker
{...props.value && moment(props.value).isValid() ? { value: moment(props.value).toDate() } : {}}
className={css(styles.dateFormField, 'dateFormField')}
placeholder="Select a date..."
isRequired={props.Required}
ariaLabel={props.Title}
parseDateFromString={(dateStr?: string) => { return moment(dateStr, 'L').toDate(); }}
formatDate={(date: Date) => (typeof date.toLocaleDateString === 'function') ? date.toLocaleDateString(locale as any) : ""}
strings={DayPickerStrings}
allowTextInput={true}
onSelectDate={(date) => {
  if (date) props.valueChanged(date.toISOString());
  else props.valueChanged('');
}}
/>);

  ;
}

function GetDate(props){
  const value = (props.value && moment(props.value).isValid()) ? moment(props.value,"YYYY-MM-DD").format('L') : '';
  return <FieldDateRenderer text={value} />;
}

export default class HelloWorld extends React.Component<IHelloWorldProps, IBookLibCollection, React.FC> {

  private _selection: Selection;
  //firstDayOfWeek = React.useState(DayOfWeek.Sunday);

  
  
  private _onItemsSelectionChanged = () => {
    
    
    this.setState({
      BookListItem: (this._selection.getSelection()[0] as BookLibListItem)
    });
  }

  private _getListItems(): Promise<BookLibListItem[]> {
    const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items";
    return this.props.context.spHttpClient.get(url,SPHttpClient.configurations.v1)
    .then(response => {
      console.log(response);
    return response.json();
    })
    .then(json => {
    return json.value;
    }) as Promise<BookLibListItem[]>;
    }
    public bindDetailsList(message: string) : void {

      this._getListItems().then(listItems => {
        console.log(listItems);
        this.setState({ BookListItems: listItems,status: message});
      });
    }
  
    public componentDidMount(): void {
      this.bindDetailsList("All Records have been loaded Successfully");  
  
      
    }

    @autobind
  public btnAdd_click(): void {  

    const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items";          
       
       const spHttpClientOptions: ISPHttpClientOptions = {
      "body": JSON.stringify(this.state.BookListItem)
    };

    this.props.context.spHttpClient.post(url, SPHttpClient.configurations.v1, spHttpClientOptions)
    .then((response: SPHttpClientResponse) => {
     
      if (response.status === 201) {
        this.bindDetailsList("Record added and All Records were loaded Successfully");         

       
       
      } else {
        let errormessage: string = "An error has occured i.e.  " + response.status + " - " + response.statusText;
        this.setState({status: errormessage});        
      }
    });
  }

   @autobind
  public btnUpdate_click(): void {

    let id: number = this.state.BookListItem.Id;

    const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items(" + id + ")";          
      
    
    const headers: any = {
      "X-HTTP-Method": "MERGE",
      "IF-MATCH": "*",
    };
       
       const spHttpClientOptions: ISPHttpClientOptions = {
        "headers": headers,
        "body": JSON.stringify(this.state.BookListItem)
    };

    this.props.context.spHttpClient.post(url, SPHttpClient.configurations.v1, spHttpClientOptions)
    .then((response: SPHttpClientResponse) => {
     
      if (response.status === 204) {
        this.bindDetailsList("Record Updated and All Records were loaded Successfully");                
       
      } else {
        let errormessage: string = "An error has occured i.e.  " + response.status + " - " + response.statusText;
        this.setState({status: errormessage});        
      }
    });
  }

  @autobind
  public btnDelete_click(): void {
    let id: number = this.state.BookListItem.Id;

    

    const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items(" + id + ")";          

    
    const headers: any = { "X-HTTP-Method": "DELETE", "IF-MATCH": "*" };

    const spHttpClientOptions: ISPHttpClientOptions = {
      "headers": headers
    };


    this.props.context.spHttpClient.post(url, SPHttpClient.configurations.v1, spHttpClientOptions)
    .then((response: SPHttpClientResponse) => {
      if (response.status === 204) {
        alert("record got deleted successfully....");
        this.bindDetailsList("Record deleted and All Records were loaded Successfully");   
        
      } else {
        let errormessage: string = "An error has occured i.e.  " + response.status + " - " + response.statusText;
        this.setState({status: errormessage}); 
      }
    });
  }

  constructor(props: IHelloWorldProps, state: IBookLibCollection){
    super(props);

    this.state = {

      status: 'Ready',
      BookListItems: [],
      BookListItem:{
        Id: 0,
        Title: "",
        Author: "",
        Publisher: "",
        DateOfPublish: new Date,
        ISBN: 0
      },

    };

    this._selection = new Selection({
      onSelectionChanged: this._onItemsSelectionChanged,
    });

    
  }



  public render(): React.ReactElement<IHelloWorldProps> {

    const dropdownRef = React.createRef<IDropdown>();

    return (

      <div className={ styles.helloWorld }>
     
      <TextField                  
      label="ID"
      required={ false } 
      value={ (this.state.BookListItem.Id).toString()}
      styles={textFieldStyles}
      onChanged={e => {this.state.BookListItem.Id=e;}}
    />
    <TextField                  
      label="Title"
      required={ true } 
      value={ (this.state.BookListItem.Title)}
      styles={textFieldStyles}
      onChanged={e => {this.state.BookListItem.Title=e;}}
    />
    <TextField                  
      label="Author"
      required={ true } 
      value={ (this.state.BookListItem.Author)}
      styles={textFieldStyles}
      onChanged={e => {this.state.BookListItem.Author=e;}}
    />
    <TextField                  
      label="Publisher"
      required={ true } 
      value={ (this.state.BookListItem.Publisher)}
      styles={textFieldStyles}
      onChanged={e => {this.state.BookListItem.Publisher=e;}}
    />
   <SetDate 
       value={this.state.BookListItem.DateOfPublish} 
       Required={true}
       Title="DateOfPublish"
       //firstDayOfWeek={firstDayOfWeek} 
    /> 
    
    <TextField                  
      label="ISBN"
      required={ true } 
      value={ (this.state.BookListItem.ISBN.toString())}
      styles={textFieldStyles}
      onChanged={e => {this.state.BookListItem.ISBN=e;}}
    />
    

<p className={styles.title}>
         <PrimaryButton
          text='Add'      
          title='Add'              
          onClick={this.btnAdd_click}
        />

        <PrimaryButton
          text='Update'                    
          onClick={this.btnUpdate_click}
        />

        <PrimaryButton
          text='Delete'                    
          onClick={this.btnDelete_click}
        />
        
      </p>


    <div id="divStatus">
      {this.state.status}
    </div>

    <div>
    <DetailsList
          items={ this.state.BookListItems}
          columns={ _bookListColumns }
          setKey='Id'
          checkboxVisibility={ CheckboxVisibility.onHover}
          selectionMode={ SelectionMode.single}
          layoutMode={ DetailsListLayoutMode.fixedColumns }
          compact={ true }
          selection={this._selection}                                         
      />
      </div>  


</div>


    );
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

我使用以下接口作为 SharePoint 中列表的数据模型:

export interface BookLibListItem {
Id: number;
Title: string;
Author: string;
Publisher: string;
DateOfPublish: Date;
ISBN: number;

}

感谢有关此问题的任何建议。

4

2 回答 2

0

Sharepoint 中的日期可能很棘手(尤其是在多个本地化的情况下) - 因此我使用时刻来格式化日期。对于 DatePicker,我正在使用此代码(带有矩库):

<DatePicker
{...props.value && moment(props.value).isValid() ? { value: moment(props.value).toDate() } : {}}
className={css(styles.dateFormField, 'dateFormField')}
placeholder={strings.FormFields.DateFormFieldPlaceholder}
isRequired={props.fieldSchema.Required}
ariaLabel={props.fieldSchema.Title}
parseDateFromString={(dateStr?: string) => { return moment(dateStr, 'L').toDate(); }}
formatDate={(date: Date) => (typeof date.toLocaleDateString === 'function') ? date.toLocaleDateString(locale) : ''}
strings={strings.FormFields}
firstDayOfWeek={props.fieldSchema.FirstDayOfWeek}
allowTextInput={true}
onSelectDate={(date) => {
  if (date) props.valueChanged(date.toISOString());
  else props.valueChanged('');
}}

上面的代码是呈现日期选择器的单独组件。要保存日期,它应该是 ISO 格式(函数 date.toISOString())。

要显示日期,我正在使用 FieldDateRenderer - import { FieldDateRenderer } from "@pnp/spfx-controls-react/lib/FieldDateRenderer";

const value = (props.value && moment(props.value).isValid()) ? moment(props.value,"YYYY-MM-DD").format('L') : '';
return <FieldDateRenderer text={value} />;
于 2021-03-22T07:29:52.283 回答
0

您如何阅读和写入列表?您是在编写自己的请求,还是在使用@pnp/sp(pnpjs)之类的库?如果您一开始不使用 pnpjs,我会推荐它,请查看入门指南

Fluent UI / office-ui-fabric-react<DatePicker/>Date对象作为值并在值更改时输出相同的值。当您从 SharePoint 获取日期时,它可能会表示为 ISO 字符串,您可以将其解析为具有以下内容的Date对象:

const date = new Date(columnValueFromSharePoint);

您不需要像这样moment的其他库。moment如果您需要进行某种复杂的时区调整或日期操作,我只会在这种情况下推荐。当您需要做一些简单的事情时,例如添加几分钟、几小时、几天或几个月,您可以使用标准的原始Date方法非常简单地完成这些操作。

每当您需要回写到DateSharePoint 时,都可以将日期输出为 ISO 字符串:

const result = await sp.web.lists.getByTitle('mylist').items.add({
   Title: 'New Item',
   MyDateColumn: date.toISOString()
});

Fluent UI(还)不包含日期和时间选择器,但@pnp/spfx-controls-react项目包含。与 DatePicker 一样,它采用标准Date对象作为值并在其onChange事件中输出相同的值。这意味着当您需要填充 SharePoint 列时,您可以轻松地将其转换为 ISO 字符串。

在 JavaScript中操作Date对象的参考通常可在MDN Web Docs上找到

于 2021-04-21T01:24:43.020 回答