update 通知公告显示优化

This commit is contained in:
2021-07-01 22:26:11 +08:00
parent c4c35998a6
commit 850d490a1b
22 changed files with 435 additions and 193 deletions

View File

@@ -5536,6 +5536,12 @@
<param name="input"></param>
<returns></returns>
</member>
<member name="M:Ewide.Core.Service.Notice.SysNoticeService.GetUnreadCount">
<summary>
获取接收到的通知公告总数
</summary>
<returns></returns>
</member>
<member name="M:Ewide.Core.Service.Notice.SysNoticeService.UpdatePublicInfo(Ewide.Core.SysNotice)">
<summary>
更新发布信息

View File

@@ -1,4 +1,6 @@
using Furion.DatabaseAccessor;
using Dapper;
using Ewide.Core.Extension;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
@@ -9,6 +11,7 @@ using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Ewide.Core.Service.Notice
@@ -26,15 +29,20 @@ namespace Ewide.Core.Service.Notice
private readonly ISysNoticeUserService _sysNoticeUserService;
private readonly IDapperRepository _dapperRepository;
public SysNoticeService(IRepository<SysNotice> sysNoticeRep,
IRepository<SysNoticeUser> sysNoticeUserRep,
IUserManager userManager,
ISysNoticeUserService sysNoticeUserService)
ISysNoticeUserService sysNoticeUserService, IDapperRepository dapperRepository)
{
_sysNoticeRep = sysNoticeRep;
_sysNoticeUserRep = sysNoticeUserRep;
_userManager = userManager;
_sysNoticeUserService = sysNoticeUserService;
_dapperRepository = dapperRepository;
}
/// <summary>
@@ -150,7 +158,6 @@ namespace Ewide.Core.Service.Notice
var noticeUserRead = new NoticeUserRead
{
UserId = u.UserId,
UserName = _userManager.Name,
ReadStatus = u.ReadStatus,
ReadTime = u.ReadTime
};
@@ -158,18 +165,17 @@ namespace Ewide.Core.Service.Notice
});
}
var noticeResult = notice.Adapt<NoticeDetailOutput>();
noticeResult.NoticeUserIdList = noticeUserIdList;
noticeResult.NoticeUserReadInfoList = noticeUserReadInfoList;
if (_userManager.SuperAdmin)
{
noticeResult.NoticeUserIdList = noticeUserIdList;
noticeResult.NoticeUserReadInfoList = noticeUserReadInfoList;
}
// 如果该条通知公告为已发布,则将当前用户的该条通知公告设置为已读
if (notice.Status == (int)NoticeStatus.PUBLIC)
await _sysNoticeUserService.Read(notice.Id, _userManager.UserId, (int)NoticeUserStatus.READ);
return noticeResult;
}
[HttpGet("/sysNotice/detailById")]
public async Task<SysNotice> GetNotice(string id)
{
return await _sysNoticeRep.FirstOrDefaultAsync(u => u.Id == id);
}
/// <summary>
/// 修改通知公告状态
/// </summary>
@@ -201,18 +207,45 @@ namespace Ewide.Core.Service.Notice
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysNotice/received")]
public async Task<dynamic> ReceivedNoticePageList([FromQuery] NoticeInput input)
[HttpPost("/sysNotice/received")]
public async Task<dynamic> ReceivedNoticePageList([FromBody] NoticeInput input)
{
var searchValue = !string.IsNullOrEmpty(input.SearchValue?.Trim());
var notices = await _sysNoticeRep.DetachedEntities.Join(_sysNoticeUserRep.DetachedEntities, u => u.Id, e => e.NoticeId, (u, e) => new { u, e })
.Where(u => u.e.UserId == _userManager.UserId)
.Where(searchValue, u => EF.Functions.Like(u.u.Title, $"%{input.SearchValue.Trim()}%") || EF.Functions.Like(u.u.Content, $"%{input.SearchValue.Trim()}%"))
.Where(input.Type > 0, u => u.u.Type == input.Type)
.Where(u => u.u.Status != (int)NoticeStatus.DELETED)
.Select(u => u.u.Adapt<NoticeReceiveOutput>())
.ToPagedListAsync(input.PageIndex, input.PageSize);
return PageDataResult<NoticeReceiveOutput>.PageResult(notices);
var sql = @"SELECT
SN.*,
SU.Avatar
FROM sys_notice SN
LEFT JOIN sys_notice_user SNU ON SN.Id = SNU.NoticeId
LEFT JOIN sys_user SU ON SN.PublicUserId = SU.Id
WHERE SNU.UserId = @UserId AND SN.Status <> @Status";
var data = await _dapperRepository.QueryPageDataDynamic(
sql,
input,
new
{
_userManager.UserId,
Status = (int)NoticeStatus.DELETED
}
);
data.Items = data.Items.Select(p => {
var r = p.Adapt<dynamic>();
r.Content = Regex.Replace(r.Content, @"<\/?.+?\/?>", "").Replace("\r\n", "");
return r;
});
return data;
}
/// <summary>
/// 获取接收到的通知公告总数
/// </summary>
/// <returns></returns>
[HttpGet("/sysNotice/unread")]
public async Task<int> GetUnreadCount()
{
return await _sysNoticeUserRep.Where(u => u.UserId == _userManager.UserId && u.ReadStatus == (int)NoticeUserStatus.UNREAD).CountAsync();
}
/// <summary>

View File

@@ -48,18 +48,7 @@ namespace Ewide.Core.Service.Notice
}.InsertAsync();
}
}
[HttpGet("/NoticeUser/getCount")]
public async Task<int> GetCount()
{
return await _sysNoticeUserRep.Where(u => u.UserId == _userManager.UserId && u.ReadStatus == (int)NoticeUserStatus.UNREAD).CountAsync();
}
[HttpGet("/NoticeUser/GetNoticeInfo")]
public async Task<List<SysNotice>> GetNoticeInfo()
{
var noticeIdList = await _sysNoticeUserRep.Where(u => u.UserId == _userManager.UserId && u.ReadStatus == (int)NoticeUserStatus.UNREAD).Select(p => p.NoticeId).ToListAsync();
return await _sysNoticeRep.Where(s => noticeIdList.Contains(s.Id)).ToListAsync();
}
/// <summary>
/// 更新
/// </summary>

View File

@@ -97,7 +97,10 @@
"sysFileInfo:preview",
"sysUser:updateInfo",
"sysUser:updatePwd",
"sysUser:updateAvatar"
"sysUser:updateAvatar",
"sysNotice:received",
"sysNotice:unread",
"sysNotice:detail"
]
}
}

View File

@@ -29,7 +29,7 @@ module.exports = {
],
webpack: {
plugins: [
new MonacoWebpackPlugin()
//new MonacoWebpackPlugin()
]
}
}

View File

@@ -23,6 +23,7 @@
"react-color": "^2.19.3",
"react-cropper": "^2.1.8",
"react-dom": "^17.0.2",
"react-infinite-scroller": "^1.2.4",
"react-json-view": "^1.21.3",
"react-monaco-editor": "^0.43.0",
"react-router": "^5.2.0",

View File

@@ -151,6 +151,8 @@
flex: 0 0 100%;
width: 100%;
text-align: inherit;
}
}
.yo-form--short {

View File

@@ -21,26 +21,25 @@
display: flex;
}
.ellipsis {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ellipsis-2 {
.ellipsis-line(@line) {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: 2;
-webkit-line-clamp: @line;
}
.ellipsis-2 {
.ellipsis-line(2);
}
.ellipsis-3 {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
-webkit-line-clamp: 3;
.ellipsis-line(3);
}

View File

@@ -94,44 +94,76 @@
transition: @animation-duration-slow;
transition-property: color;
// 特殊工具按钮
.theme-toggle {
position: relative;
overflow: hidden;
width: 20px;
height: 20px;
margin: 7px 0;
border-radius: 50%;
&--real {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #fff;
}
&--imaginary {
position: absolute;
top: 6px;
left: -6px;
width: 18px;
height: 18px;
transform: @animation-duration-slow transform;
transform: rotate(45deg) scaleY(1);
transform-origin: top right;
border-radius: 50%;
background-color: fade(@layout-header-background, 70%);
}
}
}
&:active {
box-shadow: inset 1px 1px 10px rgba(0, 0, 0, .05);
}
// 特殊工具按钮
.theme-toggle {
position: relative;
overflow: hidden;
width: 20px;
height: 20px;
margin: 7px 0;
border-radius: 50%;
&--real {
position: relative;
width: 20px;
height: 20px;
transition: @animation-duration-slow background-color;
border-radius: 50%;
background-color: fade(@white, 60%);
&::before {
position: absolute;
top: 5px;
left: 5px;
width: 10px;
height: 10px;
content: '';
transition: @animation-duration-slow transform;
transform: scale(0);
border: 2px solid @layout-header-background;
border-radius: 50%;
}
}
&--imaginary {
position: absolute;
top: 6px;
right: -6px;
width: 18px;
height: 18px;
transition: @animation-duration-slow transform;
transform: rotate(45deg) scaleY(1);
transform-origin: top right;
border-radius: 50%;
background-color: @layout-header-background;
}
}
&:hover {
.theme-toggle {
&--real {
background-color: @white;
&::before {
transform: scale(1);
}
}
&--imaginary {
transform: rotate(45deg) scaleY(0);
}
}
}
}
.ant-select-auto-complete {
margin: (@layout-header-height - 10px - 30px) / 2 @padding-md;

View File

@@ -151,6 +151,8 @@
flex: 0 0 100%;
width: 100%;
text-align: inherit;
}
}
.yo-form--short {

View File

@@ -21,26 +21,25 @@
display: flex;
}
.ellipsis {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ellipsis-2 {
.ellipsis-line(@line) {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: 2;
-webkit-line-clamp: @line;
}
.ellipsis-2 {
.ellipsis-line(2);
}
.ellipsis-3 {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
-webkit-line-clamp: 3;
.ellipsis-line(3);
}

View File

@@ -98,6 +98,72 @@
&:active {
box-shadow: inset 1px 1px 10px rgba(0, 0, 0, .05);
}
// 特殊工具按钮
.theme-toggle {
position: relative;
overflow: hidden;
width: 20px;
height: 20px;
margin: 7px 0;
border-radius: 50%;
&--real {
position: relative;
width: 100%;
height: 100%;
transition: @animation-duration-slow background-color;
border-radius: 50%;
background-color: fade(@white, 60%);
&::before {
position: absolute;
top: 5px;
left: 5px;
width: 10px;
height: 10px;
content: '';
transition: @animation-duration-slow transform;
transform: scale(1);
border: 2px solid @layout-header-background;
border-radius: 50%;
}
}
&--imaginary {
position: absolute;
top: 6px;
right: -6px;
width: 18px;
height: 18px;
transition: @animation-duration-slow transform;
transform: rotate(45deg) scaleY(0);
transform-origin: top right;
border-radius: 50%;
background-color: #334454;
}
}
&:hover {
.theme-toggle {
&--real {
background-color: @white;
&::before {
transform: scale(0);
}
}
&--imaginary {
transform: rotate(45deg) scaleY(1);
}
}
}
}
.ant-select-auto-complete {
margin: (@layout-header-height - 10px - 30px) / 2 @padding-md;

View File

@@ -9,6 +9,7 @@ import logManage from './logManage'
import machineManage from './machineManage'
import menuManage from './menuManage'
import noticeManage from './noticeManage'
import noticeReceiveManage from './noticeReceiveManage'
import onlineUserManage from './onlineUserManage'
import orgManage from './orgManage'
import posManage from './posManage'
@@ -31,6 +32,7 @@ const urls = {
...machineManage,
...menuManage,
...noticeManage,
...noticeReceiveManage,
...onlineUserManage,
...orgManage,
...posManage,

View File

@@ -28,20 +28,6 @@ const urls = {
* 修改状态
*/
sysNoticeChangeStatus: ['/sysNotice/changeStatus', 'post'],
/**
* 获取Notice总数
*/
sysNoticeGetCount: ['/NoticeUser/getCount', 'get'],
/**
* 获取Notice详细
*/
sysNoticeInfo: ['/NoticeUser/GetNoticeInfo', 'get'],
/**
* 获取Notice详细ByID
*/
sysNoticeShow: ['/sysNotice/detailById', 'get'],
}
export default urls

View File

@@ -1,8 +1,12 @@
const urls = {
/**
* 获取接收到的通知公告总数
*/
sysNoticeUnread: ['/sysNotice/unread', 'get'],
/**
* 查询我收到的系统通知公告
*/
sysNoticeReceived: ['/sysNotice/received', 'get'],
sysNoticeReceived: ['/sysNotice/received', 'post'],
}
export default urls

View File

@@ -7,7 +7,7 @@ import 'braft-editor/dist/index.css'
export default class index extends Component {
state = {
editorState: BraftEditor.createEditorState(this.props.value), // 设置编辑器初始内容
outputHTML: '<p></p>',
outputHTML: '',
}
/**
* mount后回调
@@ -43,7 +43,7 @@ export default class index extends Component {
const { editorState, outputHTML } = this.state
//localStorage.setItem('props', JSON.stringify(this.props))
const controls = ['bold', 'italic', 'underline', 'text-color', 'separator', 'media']
const controls = ['bold', 'italic', 'underline', 'text-color', 'separator']
return (
<BraftEditor
value={editorState}

View File

@@ -1,6 +1,7 @@
export { default as AntIcon } from './ant-icon'
export { default as AuthorityView } from './authority-view'
export { default as Auth } from './authorized'
export { default as BraftEditor } from './form/braft-editor'
export { default as ColorSelector } from './form/color-selector'
export { default as ComponentDynamic } from './component-dynamic'
export { default as Container } from './container'
@@ -13,4 +14,3 @@ export { default as QueryList } from './query-list'
export { default as QueryTable } from './query-table'
export { default as QueryTableActions } from './query-table-actions'
export { default as QueryTreeLayout } from './query-tree-layout'
export { default as BraftEditor } from './form/braft-editor'

View File

@@ -96,13 +96,13 @@ export default class form extends Component {
return (
<Form initialValues={initialValues} ref={this.form} className="yo-form">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<div className="yo-form-group yo-form--fluid">
<Form.Item
label="标题"
name="title"
rules={[{ required: true, message: '请输入标题', trigger: 'blur' }]}
>
<Input autoComplete="off" placeholder="请输入标题" className="w-300" />
<Input autoComplete="off" placeholder="请输入标题" />
</Form.Item>
<Form.Item
label="类型"
@@ -120,7 +120,17 @@ export default class form extends Component {
<Form.Item
label="内容"
name="content"
// rules={[{ required: true, message: '请输入内容' }]}
rules={[
{ required: true, message: '请输入内容' },
{
validator: async (field, value) => {
const v = value.replace(/<\/?.+?\/?>/g, '')
if (!v) {
throw Error('请输入内容')
}
},
},
]}
>
<BraftEditor />
</Form.Item>

View File

@@ -28,7 +28,7 @@ const apiAction = {
* 用于弹窗标题
* [必要]
*/
const name = '啥玩意'
const name = '通知公告'
/**
* 统一配置权限标识
@@ -253,6 +253,7 @@ export default class index extends Component {
<Auth auth={{ [authName]: 'add' }}>
<ModalForm
title={`新增${name}`}
width={800}
action={apiAction.add}
ref={this.addForm}
onSuccess={() => this.table.current.onReloadData()}
@@ -264,6 +265,7 @@ export default class index extends Component {
<Auth auth={{ [authName]: 'edit' }}>
<ModalForm
title={`编辑${name}`}
width={800}
action={apiAction.edit}
ref={this.editForm}
onSuccess={() => this.table.current.onReloadData()}

View File

@@ -1,23 +1,19 @@
import React, { Component, useState } from 'react'
import { Layout, Badge, Popover, Menu, Modal } from 'antd'
import { Layout, Badge, Popover, Menu, Modal, Tooltip, Popconfirm } from 'antd'
import { AntIcon, Container } from 'components'
import Logo from '../logo'
import User from './user'
import Search from './search'
import store from 'store'
import { api } from 'common/api'
import Logo from '../logo'
import Search from './search'
import Notice from './notice'
import User from './user'
const { getState, subscribe, dispatch } = store
export default class index extends Component {
state = {
...getState('layout'),
notice: {
count: 0,
data: [],
},
modalVisible: false,
currentNotice: {},
}
constructor(props) {
@@ -28,10 +24,6 @@ export default class index extends Component {
})
}
componentDidMount() {
this.loadNotice()
}
componentWillUnmount() {
this.unsubscribe()
}
@@ -43,27 +35,8 @@ export default class index extends Component {
})
}
async loadNotice() {
const { data } = await api.sysNoticeGetCount()
const items = await api.sysNoticeInfo()
this.setState({
notice: {
count: data,
data: items.data,
},
})
}
async showDetail(params, visible) {
this.setState({ currentNotice: params })
if (visible) {
this.setState({ modalVisible: visible })
} else {
this.setState({ modalVisible: visible })
}
}
render() {
const { allowSiderCollapsed, notice, currentNotice } = this.state
const { allowSiderCollapsed, theme } = this.state
return (
<Layout.Header>
<Container mode="fluid">
@@ -80,57 +53,38 @@ export default class index extends Component {
<Search />
</div>
<div className="header-actions">
<span
className="header-action"
onClick={() => window.realodContentWindow()}
>
<AntIcon type="reload" />
</span>
<Popover
arrowPointAtCenter={true}
overlayClassName="yo-user-popover"
placement="bottomRight"
content={
<Menu selectable={false}>
<Menu.Divider />
{notice.data.map(item => (
<Menu.Item
onClick={() => this.showDetail(item, true)}
key={item.id}
>
{item.title}
</Menu.Item>
))}
</Menu>
}
>
<span className="header-action">
<Badge count={notice.count}>
<AntIcon type="bell" />
</Badge>
</span>
<Modal
title={currentNotice.title}
width={1000}
style={{ top: 120 }}
visible={this.state.modalVisible}
onOk={() => this.showDetail(false)}
onCancel={() => this.showDetail(false)}
style={{ zIndex: 1000 }}
<Tooltip placement="bottom" title="重新加载框架">
<span
className="header-action"
onClick={() => window.realodContentWindow()}
>
<div style={{ textAlign: 'center', fontSize: '30px' }}>
<div
dangerouslySetInnerHTML={{ __html: currentNotice.content }}
></div>
</div>
<div style={{ textAlign: 'right', fontSize: '10px' }}>
<span>发布人{currentNotice.createdUserName}</span>
{' '}
<span>发布时间{currentNotice.createdTime} </span>
</div>
</Modal>
</Popover>
<AntIcon type="reload" />
</span>
</Tooltip>
<Notice />
<Tooltip
placement="bottom"
title={`切换到${{ default: '夜间', dark: '默认' }[theme]}模式`}
>
<Popconfirm
placement="bottomRight"
title="切换模式将刷新整个页面,是否继续?"
onConfirm={() => {
dispatch({
type: 'SET_THEME',
theme: { default: 'dark', dark: 'default' }[theme],
})
window.location.reload()
}}
>
<span className="header-action">
<div className="theme-toggle">
<div className="theme-toggle--real" />
<div className="theme-toggle--imaginary" />
</div>
</span>
</Popconfirm>
</Tooltip>
<User />
</div>
</Container>

View File

@@ -0,0 +1,145 @@
import React, { Component } from 'react'
import { Badge, Divider, List, Menu, Modal, Popover, Row, Spin } from 'antd'
import { AntIcon, Image } from 'components'
import { api } from 'common/api'
import InfiniteScroll from 'react-infinite-scroller'
import moment from 'moment'
export default class notice extends Component {
state = {
count: 0,
list: [],
loading: false,
hasMore: true,
detailVisible: false,
detailLoading: false,
detailData: {},
}
async componentDidMount() {
const { data: count } = await api.sysNoticeUnread()
this.setState({ count })
}
finish() {
this.setState({ loading: false, hasMore: false })
}
async onInfiniteOnLoad(pageIndex) {
this.setState({ loading: true })
const { list } = this.state
if (list.length >= 30) {
return this.finish()
}
const {
data: { items },
} = await api.sysNoticeReceived({
pageIndex,
pageSize: 5,
sortField: 'createdTime',
sortOrder: 'descend',
})
if (!items.length) {
return this.finish()
}
this.setState({
list: [...list, ...items],
loading: false,
})
}
async onOpenDetail(id) {
this.setState({ detailLoading: true, detailVisible: true })
const { data } = await api.sysNoticeDetail({ id })
this.setState({
detailLoading: false,
detailData: data,
})
}
renderList() {
const { list, loading, hasMore } = this.state
return (
<InfiniteScroll
loadMore={pageIndex => this.onInfiniteOnLoad(pageIndex)}
hasMore={!loading && hasMore}
useWindow={false}
threshold={100}
>
<List
itemLayout="vertical"
dataSource={list}
renderItem={item => (
<List.Item key={item.id}>
<List.Item.Meta
avatar={<Image id={item.avatar} type="avatar" />}
title={
<a
className="ellipsis"
onClick={() => this.onOpenDetail(item.id)}
>
{item.title}
</a>
}
description={moment(item.createdTime || item.publicTime).fromNow()}
/>
<div className="ellipsis-3 text-gray">{item.content}</div>
</List.Item>
)}
>
{loading && hasMore && (
<div className="pt-md pb-md text-center">
<Spin indicator={<AntIcon type="loading" />} />
</div>
)}
</List>
</InfiniteScroll>
)
}
render() {
const { count, detailLoading, detailVisible, detailData } = this.state
return (
<Popover
arrowPointAtCenter={true}
placement="bottomRight"
content={this.renderList()}
overlayInnerStyle={{ width: 300, maxHeight: 300, overflowY: 'auto' }}
overlayStyle={{ zIndex: 999 }}
>
<span className="header-action">
<Badge count={count}>
<AntIcon type="bell" />
</Badge>
</span>
<Modal
width={1000}
footer={false}
visible={detailVisible}
onCancel={() => this.setState({ detailVisible: false, detailData: {} })}
>
<Spin spinning={detailLoading} indicator={<AntIcon type="loading" />}>
<div className="h3 mt-lg">{detailData.title}</div>
<Divider />
<div
className="pt-lg pb-lg"
dangerouslySetInnerHTML={{ __html: detailData.content }}
></div>
<Divider />
<Row justify="space-between" className="text-gray">
<span>发布人{detailData.publicUserName}</span>
<span>发布时间{detailData.publicTime} </span>
</Row>
</Spin>
</Modal>
</Popover>
)
}
}

View File

@@ -9285,7 +9285,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU=
@@ -9877,6 +9877,13 @@ react-error-overlay@^6.0.9:
resolved "https://registry.nlark.com/react-error-overlay/download/react-error-overlay-6.0.9.tgz?cache=0&sync_timestamp=1618847933355&other_urls=https%3A%2F%2Fregistry.nlark.com%2Freact-error-overlay%2Fdownload%2Freact-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
integrity sha1-PHQwEMk1lgjDdezWvHbzXZOZWwo=
react-infinite-scroller@^1.2.4:
version "1.2.4"
resolved "https://registry.npm.taobao.org/react-infinite-scroller/download/react-infinite-scroller-1.2.4.tgz#f67eaec4940a4ce6417bebdd6e3433bfc38826e9"
integrity sha1-9n6uxJQKTOZBe+vdbjQzv8OIJuk=
dependencies:
prop-types "^15.5.8"
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.nlark.com/react-is/download/react-is-16.13.1.tgz?cache=0&sync_timestamp=1623273254569&other_urls=https%3A%2F%2Fregistry.nlark.com%2Freact-is%2Fdownload%2Freact-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"