add 文件管理

This commit is contained in:
2021-06-23 17:56:29 +08:00
parent 3734dda2db
commit 32856f4757
9 changed files with 636 additions and 157 deletions

View File

@@ -9,5 +9,6 @@
/// 文件Id /// 文件Id
/// </summary> /// </summary>
public string Id { get; set; } public string Id { get; set; }
public System.DateTime CreatedTime { get; set; }
} }
} }

View File

@@ -1,4 +1,5 @@
using Furion; using Ewide.Core.Extension;
using Furion;
using Furion.DatabaseAccessor; using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions; using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection; using Furion.DependencyInjection;
@@ -50,8 +51,7 @@ namespace Ewide.Core.Service
.Where(input.FileLocation > 0, u => u.FileLocation == input.FileLocation) .Where(input.FileLocation > 0, u => u.FileLocation == input.FileLocation)
.Where(fileBucket, u => EF.Functions.Like(u.FileBucket, $"%{input.FileBucket.Trim()}%")) .Where(fileBucket, u => EF.Functions.Like(u.FileBucket, $"%{input.FileBucket.Trim()}%"))
.Where(fileOriginName, u => EF.Functions.Like(u.FileOriginName, $"%{input.FileOriginName.Trim()}%")) .Where(fileOriginName, u => EF.Functions.Like(u.FileOriginName, $"%{input.FileOriginName.Trim()}%"))
.Select(u => u.Adapt<FileOutput>()) .ToPageData<SysFile, FileOutput>(input);
.ToPagedListAsync(input.PageIndex, input.PageSize);
return PageDataResult<FileOutput>.PageResult(files); return PageDataResult<FileOutput>.PageResult(files);
} }

View File

@@ -1,7 +1,12 @@
@import (reference) '../extend.less'; @import (reference) '../extend.less';
.yo-query-bar { .yo-query-bar {
margin-bottom: @padding-md; margin-bottom: @padding-xs;
.ant-form-inline {
.ant-form-item {
margin-bottom: @padding-xs;
}
}
} }
.yo-action-bar { .yo-action-bar {
@@ -71,6 +76,10 @@
} }
} }
.ant-table-sticky-scroll {
display: none;
}
.yo-table { .yo-table {
.ant-table { .ant-table {
margin: 0 !important; margin: 0 !important;
@@ -153,6 +162,10 @@
border-top: @border-width-base @border-style-base @table-border-color; border-top: @border-width-base @border-style-base @table-border-color;
} }
} }
&--row-no {
background-color: @table-header-bg;
}
} }
.yo-table-actions { .yo-table-actions {

View File

@@ -501,7 +501,6 @@
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
min-width: @container-width;
height: 100%; height: 100%;
@layout-header-height: 54px; @layout-header-height: 54px;

View File

@@ -46,7 +46,6 @@ const { getState, subscribe } = store
const stroePath = 'user' const stroePath = 'user'
export default class Auth extends Component { export default class Auth extends Component {
state = getState(stroePath) state = getState(stroePath)
constructor(props) { constructor(props) {
@@ -62,11 +61,10 @@ export default class Auth extends Component {
} }
render() { render() {
const flag = auth.call(this.state, this.props.auth) const flag = auth.call(this.state, this.props.auth)
if (flag) { if (flag) {
return this.props.children return this.props.children || <></>
} }
return <></> return <></>

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react'
import { Form, Button, Table, Tooltip } from 'antd' import { Form, Button, Table, Tooltip } from 'antd'
import { AntIcon } from 'components' import { AntIcon } from 'components'
const propsMap = ['autoLoad', 'loadData', 'pageIndex', 'pageSize'] const propsMap = ['columns', 'autoLoad', 'loadData', 'pageIndex', 'pageSize']
const clearChildren = data => { const clearChildren = data => {
data.forEach(p => { data.forEach(p => {
@@ -17,6 +17,17 @@ const clearChildren = data => {
return data return data
} }
const rowNoColumn = {
title: '#',
dataIndex: 'rowNo',
width: 30,
fixed: true,
align: 'center',
ellipsis: true,
className: 'yo-table--row-no',
render: (text, record, index) => index + 1,
}
/** /**
* 渲染查询栏 * 渲染查询栏
* @returns * @returns
@@ -64,7 +75,7 @@ export default class QueryTable extends Component {
// 加载状态 // 加载状态
loading: false, loading: false,
// 表格类型 // 表格类型
type: '', type: 'tree',
// 数据 // 数据
dataSource: [], dataSource: [],
} }
@@ -105,6 +116,8 @@ export default class QueryTable extends Component {
this.loadData = this.loadData =
typeof this.props.loadData === 'function' ? this.props.loadData : async () => {} typeof this.props.loadData === 'function' ? this.props.loadData : async () => {}
this.rowNumber = typeof this.props.rowNumber === 'boolean' ? this.props.rowNumber : true
if (this.props.pageIndex) { if (this.props.pageIndex) {
this.pageIndex = this.props.pageIndex this.pageIndex = this.props.pageIndex
this.pagination.current = this.pageIndex this.pagination.current = this.pageIndex
@@ -113,6 +126,19 @@ export default class QueryTable extends Component {
this.pageSize = this.props.pageSize this.pageSize = this.props.pageSize
this.pagination.pageSize = this.pageSize this.pagination.pageSize = this.pageSize
} }
// 默认排序
if (this.props.columns) {
for (const column of this.props.columns) {
if (column.defaultSortOrder) {
this.sorter = {
sortField: column.dataIndex,
sortOrder: column.defaultSortOrder,
}
break
}
}
}
} }
/** /**
@@ -247,7 +273,9 @@ export default class QueryTable extends Component {
} }
render() { render() {
const { loading, dataSource } = this.state const { rowNumber } = this
const { loading, dataSource, type } = this.state
const { query, operator, columns } = this.props const { query, operator, columns } = this.props
@@ -262,11 +290,19 @@ export default class QueryTable extends Component {
loading, loading,
pagination: this.pagination, pagination: this.pagination,
dataSource, dataSource,
columns: (columns || []).filter(p => !p.hidden), columns: (() => {
const c = []
if (type !== 'tree' && rowNumber) {
c.push(rowNoColumn)
}
c.push(...(columns || []))
return c.filter(p => !p.hidden)
})(),
bordered: true, bordered: true,
size: 'middle', size: 'middle',
rowKey: record => record.id || Math.random().toString(16).slice(2), rowKey: record => record.id || Math.random().toString(16).slice(2),
sticky: true, sticky: true,
scroll: { x: 'max-content' },
...attrs, ...attrs,
} }

View File

@@ -15,18 +15,17 @@ const apiAction = {
edit: api.sysAppEdit, edit: api.sysAppEdit,
delete: api.sysAppDelete, delete: api.sysAppDelete,
setDefault: api.sysAppSetAsDefault setDefault: api.sysAppSetAsDefault,
} }
// 用于弹窗标题 // 用于弹窗标题
const name = '应用' const name = '应用'
export default class index extends Component { export default class index extends Component {
state = { state = {
codes: { codes: {
commonStatus: [] commonStatus: [],
} },
} }
// 表格实例 // 表格实例
@@ -42,21 +41,25 @@ export default class index extends Component {
{ {
title: '应用名称', title: '应用名称',
dataIndex: 'name', dataIndex: 'name',
width: 300,
sorter: true, sorter: true,
}, },
{ {
title: '唯一编码', title: '唯一编码',
dataIndex: 'code', dataIndex: 'code',
width: 300,
sorter: true, sorter: true,
}, },
{ {
title: '是否默认', title: '是否默认',
dataIndex: 'active', dataIndex: 'active',
width: 200,
sorter: true, sorter: true,
render: (text, record) => (<> render: (text, record) => (
<>
{text ? '是' : '否'} {text ? '是' : '否'}
{ {!record.active && (
!record.active && <Auth auth="sysApp:setAsDefault"> <Auth auth="sysApp:setAsDefault">
<QueryTableActions> <QueryTableActions>
<span></span> <span></span>
<Popconfirm <Popconfirm
@@ -68,18 +71,21 @@ export default class index extends Component {
</Popconfirm> </Popconfirm>
</QueryTableActions> </QueryTableActions>
</Auth> </Auth>
} )}
</>) </>
),
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',
width: 100,
sorter: true, sorter: true,
render: text => (<>{this.bindCodeValue(text, 'common_status')}</>) render: text => <>{this.bindCodeValue(text, 'common_status')}</>,
}, },
{ {
title: '排序', title: '排序',
dataIndex: 'sort', dataIndex: 'sort',
width: 100,
sorter: true, sorter: true,
}, },
] ]
@@ -98,7 +104,8 @@ export default class index extends Component {
title: '操作', title: '操作',
width: 150, width: 150,
dataIndex: 'actions', dataIndex: 'actions',
render: (text, record) => (<QueryTableActions> render: (text, record) => (
<QueryTableActions>
<Auth auth="sysApp:edit"> <Auth auth="sysApp:edit">
<a onClick={() => this.onOpen(this.editForm, record)}>编辑</a> <a onClick={() => this.onOpen(this.editForm, record)}>编辑</a>
</Auth> </Auth>
@@ -111,7 +118,8 @@ export default class index extends Component {
<a>删除</a> <a>删除</a>
</Popconfirm> </Popconfirm>
</Auth> </Auth>
</QueryTableActions>) </QueryTableActions>
),
}) })
} }
} }
@@ -136,11 +144,14 @@ export default class index extends Component {
const { onLoading, onLoadData } = this.table.current const { onLoading, onLoadData } = this.table.current
onLoading() onLoading()
getDictData('common_status').then(res => { getDictData('common_status').then(res => {
this.setState({ this.setState(
codes: res {
}, () => { codes: res,
},
() => {
onLoadData() onLoadData()
}) }
)
}) })
} }
@@ -169,7 +180,7 @@ export default class index extends Component {
name = toCamelCase(name) name = toCamelCase(name)
const codes = this.state.codes[name] const codes = this.state.codes[name]
if (codes) { if (codes) {
const c = codes.find((p) => p.code === code) const c = codes.find(p => p.code == code)
if (c) { if (c) {
return c.value return c.value
} }
@@ -184,7 +195,7 @@ export default class index extends Component {
*/ */
onOpen(modal, record) { onOpen(modal, record) {
modal.current.open({ modal.current.open({
record record,
}) })
} }
@@ -215,18 +226,12 @@ export default class index extends Component {
* @param {*} record * @param {*} record
*/ */
onDelete(record) { onDelete(record) {
this.onAction( this.onAction(apiAction.delete(record), '删除成功')
apiAction.delete(record),
'删除成功'
)
} }
//#region 自定义方法 //#region 自定义方法
async onSetDefault(record) { async onSetDefault(record) {
this.onAction( this.onAction(apiAction.setDefault(record), '设置成功')
apiAction.setDefault(record),
'设置成功'
)
} }
//#endregion //#endregion
@@ -255,7 +260,9 @@ export default class index extends Component {
<Button <Button
icon={<AntIcon type="plus" />} icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)} onClick={() => this.onOpen(this.addForm)}
>新增{name}</Button> >
新增{name}
</Button>
</Auth> </Auth>
} }
/> />

View File

@@ -0,0 +1,398 @@
import React, { Component } from 'react'
import {
Button,
Card,
Form,
Input,
message as Message,
Popconfirm,
Select,
Tag,
Tooltip,
Upload,
} from 'antd'
import { AntIcon, Auth, Container, PhotoPreview, QueryTable, QueryTableActions } from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import { ArrayBufferToBase64, GetFileName, PreviewFileArrayBuffer } from 'util/file'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.sysFileInfoPage,
delete: api.sysFileInfoDelete,
}
/**
* 用于弹窗标题
* [必要]
*/
const name = '/**/'
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'sysFileInfo'
export default class index extends Component {
state = {
codes: {
fileStorageLocation: [],
},
uploading: false,
}
// 表格实例
table = React.createRef()
photoPreview = React.createRef()
columns = [
{
title: '文件名称',
dataIndex: 'fileOriginName',
width: 300,
ellipsis: {
showTitle: false,
},
sorter: true,
render: text => <Tooltip title={text}>{text}</Tooltip>,
},
{
title: '文件后缀',
dataIndex: 'fileSuffix',
width: 120,
sorter: true,
render: text => <Tag color="green">{text}</Tag>,
},
{
title: '文件大小',
dataIndex: 'fileSizeKb',
width: 120,
sorter: true,
render: text => (
<>
{text}
<small>KB</small>
</>
),
},
{
title: '存储位置',
dataIndex: 'fileLocation',
width: 120,
sorter: true,
render: text => this.bindCodeValue(text, 'file_storage_location'),
},
{
title: '文件仓库',
dataIndex: 'fileBucket',
width: 200,
ellipsis: {
showTitle: false,
},
sorter: true,
render: text => <Tooltip title={text}>{text}</Tooltip>,
},
{
title: '唯一标识id',
dataIndex: 'fileObjectName',
width: 250,
ellipsis: {
showTitle: false,
},
sorter: true,
render: text => <Tooltip title={text}>{text}</Tooltip>,
},
{
title: '上传时间',
dataIndex: 'createdTime',
width: 200,
sorter: true,
defaultSortOrder: 'descend',
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ [authName]: 'delete' })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<a onClick={() => this.onFileDownload(record)}>下载</a>
<Auth auth={{ [authName]: 'delete' }}>
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(record)}
>
<a>删除</a>
</Popconfirm>
</Auth>
{['png', 'jpeg', 'jpg', 'gif', 'tif', 'bmp'].includes(
record.fileSuffix
) && <a onClick={() => this.onFilePreview(record)}>预览</a>}
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData('file_storage_location').then(codes => {
this.setState({ codes }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
const { data } = await apiAction.page({
...params,
...query,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} record
*/
onOpen(modal, record) {
modal.current.open({
record,
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
/**
* 删除
* @param {*} record
*/
onDelete(record) {
this.onAction(apiAction.delete(record), '删除成功')
}
//#region 自定义方法
async onFileUpload({ file }) {
this.setState({ uploading: true })
const table = this.table.current
table.onLoading()
const fd = new FormData()
fd.append('file', file)
try {
await api.sysFileInfoUpload(fd)
table.onReloadData()
} catch {
table.onLoaded()
} finally {
this.setState({ uploading: false })
}
}
async onFilePreview({ id }) {
const key = Math.random().toString(16).slice(2)
const hide = Message.loading({
key,
content: '正在获取文件...',
duration: 0,
})
const file = await PreviewFileArrayBuffer(id)
if (file) {
const base64 = await ArrayBufferToBase64(file)
var img = new Image()
img.onload = () => {
const items = [
{
src: base64,
w: img.naturalWidth,
h: img.naturalHeight,
},
]
this.photoPreview.current.initPhotoSwipe(items)
hide()
}
img.onerror = () => {
Message.error({
key,
content: '获取文件失败',
})
}
img.src = base64
} else {
Message.error({
key,
content: '获取文件失败',
})
}
}
async onFileDownload({ id }) {
const key = Math.random().toString(16).slice(2)
const hide = Message.loading({
key,
content: '正在获取文件...',
duration: 0,
})
try {
const { data, headers } = await api.sysFileInfoDownload({ id })
const url = window.URL.createObjectURL(data)
const fileName = GetFileName(headers['content-disposition'])
const a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
window.URL.revokeObjectURL(url)
a.remove()
hide()
} catch {
Message.error({
key,
content: '下载文件失败',
})
}
}
//#endregion
render() {
const { codes, uploading } = this.state
return (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
query={
<Auth auth={{ [authName]: 'page' }}>
<Form.Item label="文件名" name="fileOriginName">
<Input
autoComplete="off"
placeholder="请输入文件名"
className="w-200"
/>
</Form.Item>
<Form.Item label="存储位置" name="fileLocation">
<Select placeholder="请选择存储位置" className="w-200">
{codes.fileStorageLocation.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="文件仓库" name="fileBucket">
<Input
autoComplete="off"
placeholder="请输入文件仓库"
className="w-200"
/>
</Form.Item>
</Auth>
}
operator={
<Auth auth={{ [authName]: 'add' }}>
<Upload customRequest={e => this.onFileUpload(e)} fileList={[]}>
<Button loading={uploading} icon={<AntIcon type="upload" />}>
上传文件
</Button>
</Upload>
</Auth>
}
/>
</Card>
<PhotoPreview ref={this.photoPreview} />
</Container>
)
}
}

View File

@@ -5,14 +5,15 @@ import 'nprogress/nprogress.css'
import AntIcon from 'components/ant-icon' import AntIcon from 'components/ant-icon'
import { Container } from 'components' import { Container } from 'components'
NProgress.configure({ parent: '.ant-layout-content > .yo-tab-external-mount > .yo-tab-external-mount-content' }); NProgress.configure({
parent: '.ant-layout-content > .yo-tab-external-mount > .yo-tab-external-mount-content',
})
class ComponentDynamic extends Component { class ComponentDynamic extends Component {
state = { state = {
// 组件内部组件的key,用于刷新 // 组件内部组件的key,用于刷新
key: null, key: null,
component: null component: null,
} }
shouldComponentUpdate() { shouldComponentUpdate() {
@@ -32,8 +33,7 @@ class ComponentDynamic extends Component {
// 在这里使用setTimeout调用,是防止打开窗口时卡顿 // 在这里使用setTimeout调用,是防止打开窗口时卡顿
setTimeout(async () => { setTimeout(async () => {
let component
let component;
try { try {
component = await import(`../../../../pages${this.props.path}`) component = await import(`../../../../pages${this.props.path}`)
@@ -41,19 +41,22 @@ class ComponentDynamic extends Component {
component = await import('views/error/404') component = await import('views/error/404')
} }
this.setState({ this.setState(
{
key: Math.random().toString(16).slice(2), key: Math.random().toString(16).slice(2),
component: component.default component: component.default,
}, () => { },
() => {
NProgress.done() NProgress.done()
}) }
)
}) })
} }
render() { render() {
if (this.state.component) { if (this.state.component) {
return <this.state.component return (
<this.state.component
key={this.state.key} key={this.state.key}
{...this.props} {...this.props}
supportInfo={ supportInfo={
@@ -64,15 +67,15 @@ class ComponentDynamic extends Component {
</Container> </Container>
} }
/> />
)
} }
return <></> return <></>
} }
} }
export default class index extends Component { export default class index extends Component {
state = { state = {
actived: '' actived: '',
} }
panes = [] panes = []
@@ -83,13 +86,13 @@ export default class index extends Component {
static getDerivedStateFromProps(props) { static getDerivedStateFromProps(props) {
return { return {
actived: props.actived actived: props.actived,
} }
} }
onChange(activeKey) { onChange(activeKey) {
this.props.parent.setState({ this.props.parent.setState({
actived: activeKey actived: activeKey,
}) })
} }
@@ -99,7 +102,7 @@ export default class index extends Component {
} }
} }
onReload = (key) => { onReload = key => {
key = key || this.state.actived key = key || this.state.actived
const pane = this.panes.find(p => p.props.id === key) const pane = this.panes.find(p => p.props.id === key)
if (pane) { if (pane) {
@@ -108,7 +111,6 @@ export default class index extends Component {
} }
render() { render() {
this.panes = [] this.panes = []
return ( return (
@@ -118,11 +120,10 @@ export default class index extends Component {
type="editable-card" type="editable-card"
hideAdd hideAdd
activeKey={this.state.actived} activeKey={this.state.actived}
onChange={(activeKey) => this.onChange(activeKey)} onChange={activeKey => this.onChange(activeKey)}
onEdit={(targetKey, action) => this.onClose(targetKey, action)} onEdit={(targetKey, action) => this.onClose(targetKey, action)}
> >
{ {this.props.panes.map(pane => {
this.props.panes.map(pane => {
return ( return (
<Tabs.TabPane <Tabs.TabPane
closable={pane.closable} closable={pane.closable}
@@ -132,11 +133,37 @@ export default class index extends Component {
trigger={['contextMenu']} trigger={['contextMenu']}
overlay={ overlay={
<Menu> <Menu>
<Menu.Item key="0" onClick={() => this.onReload(pane.key)}>重新加载</Menu.Item> <Menu.Item
key="0"
onClick={() => this.onReload(pane.key)}
>
重新加载
</Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item key="1" onClick={() => window.closeContentWindow(pane.key)}>关闭</Menu.Item> <Menu.Item
<Menu.Item key="2" onClick={() => window.closeOtherContentWindow(pane.key)}>关闭其他标签页</Menu.Item> key="1"
<Menu.Item key="3" onClick={() => window.closeRightContentWindow(pane.key)}>关闭右侧标签页</Menu.Item> onClick={() =>
window.closeContentWindow(pane.key)
}
>
关闭
</Menu.Item>
<Menu.Item
key="2"
onClick={() =>
window.closeOtherContentWindow(pane.key)
}
>
关闭其他标签页
</Menu.Item>
<Menu.Item
key="3"
onClick={() =>
window.closeRightContentWindow(pane.key)
}
>
关闭右侧标签页
</Menu.Item>
</Menu> </Menu>
} }
> >
@@ -148,17 +175,18 @@ export default class index extends Component {
} }
/> />
) )
}) })}
}
</Tabs> </Tabs>
<div className="yo-tab-external-mount-content"> <div className="yo-tab-external-mount-content">
{ {this.props.panes.map(pane => {
this.props.panes.map(pane => {
return ( return (
<div <div
key={pane.key} key={pane.key}
className={ className={
(pane.key === this.state.actived ? 'yo-tab-external-tabpane-active' : 'yo-tab-external-tabpane-inactive') + ' yo-tab-external-tabpane' (pane.key === this.state.actived
? 'yo-tab-external-tabpane-active'
: 'yo-tab-external-tabpane-inactive') +
' yo-tab-external-tabpane'
} }
> >
<ComponentDynamic <ComponentDynamic
@@ -170,8 +198,7 @@ export default class index extends Component {
/> />
</div> </div>
) )
}) })}
}
</div> </div>
</div> </div>
</Layout.Content> </Layout.Content>