zmh
2 years ago
11 changed files with 943 additions and 86 deletions
Binary file not shown.
@ -0,0 +1,325 @@ |
|||
'use strict'; |
|||
|
|||
import React, { Component } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { Spin, Upload, message, Modal, Card, Button } from 'antd'; |
|||
import moment from 'moment'; |
|||
import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons'; |
|||
import { RouteRequest } from '@peace/utils'; |
|||
import { RouteTable } from '$utils' |
|||
|
|||
|
|||
class Uploads extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
fileUploading: false, |
|||
fileList: [], |
|||
curPreviewPic: '', |
|||
delPicIng: false, |
|||
removeFilesList: [] |
|||
}; |
|||
} |
|||
dealName = (uploaded) => { |
|||
let realName = uploaded.split('/')[2] |
|||
let x1 = realName.split('.') |
|||
let x2 = x1[0].split('_') |
|||
let showName = `${x2[0]}.${x1[1]}` |
|||
return showName |
|||
} |
|||
|
|||
// setFileList = (value) => {
|
|||
// let defaultFileList = [];
|
|||
// defaultFileList = value.map((u, index) => {
|
|||
// let fileUrl = `${this.ApiRoot}/${u.url}`;
|
|||
// return {
|
|||
// uid: -index - 1,
|
|||
// name: this.dealName(u.url),
|
|||
// status: 'done',
|
|||
// storageUrl: u.url,
|
|||
// url: fileUrl
|
|||
// };
|
|||
// });
|
|||
// onChange(defaultFileList)
|
|||
// this.setState({
|
|||
// fileList: defaultFileList
|
|||
// });
|
|||
// };
|
|||
|
|||
componentDidMount() { |
|||
const { value } = this.props; |
|||
if (value) { |
|||
// this.setState(value);
|
|||
this.setState({ fileList: value }) |
|||
|
|||
} |
|||
} |
|||
|
|||
componentWillReceiveProps(np) { |
|||
const { dispatch, value: thisEditData, onChange } = this.props; |
|||
const { value: nextEditData } = np; |
|||
|
|||
const setFileList = () => { |
|||
let defaultFileList = []; |
|||
defaultFileList = nextEditData.map((u, index) => { |
|||
let fileUrl = u.filename; |
|||
return { |
|||
uid: -index - 1, |
|||
name: u.name, |
|||
status: 'done', |
|||
storageUrl: u.filename, |
|||
url: fileUrl, |
|||
size: u.size || -1 |
|||
}; |
|||
}); |
|||
this.setState({ |
|||
fileList: defaultFileList |
|||
}); |
|||
}; |
|||
|
|||
if (nextEditData && nextEditData.length) { |
|||
if (!thisEditData || !this.state.fileList.length) { |
|||
setFileList(); |
|||
} else if (nextEditData.length != thisEditData.length) { |
|||
setFileList(); |
|||
} else { |
|||
let repeat = true; |
|||
for (let i = 0; i < thisEditData.length; i++) { |
|||
if (thisEditData[i] != nextEditData[i]) { |
|||
repeat = false; |
|||
break; |
|||
} |
|||
} |
|||
if (!repeat) { |
|||
setFileList(); |
|||
} |
|||
} |
|||
} |
|||
// else{
|
|||
// this.setState({
|
|||
// fileList:[],
|
|||
// })
|
|||
// }
|
|||
} |
|||
//删除文件
|
|||
deleteFile(file) { |
|||
if (file.url) { |
|||
RouteRequest.delete(RouteTable.cleanUpUploadTrash, { url: file.url }); |
|||
}; |
|||
} |
|||
render() { |
|||
const UploadPath = { |
|||
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'png', 'jpg', 'rar', 'zip'], |
|||
report: ['doc', 'docx', 'xls', 'xlsx', 'pdf'], |
|||
data: ['txt', 'xls', 'xlsx'], |
|||
image: ['png', 'jpg', 'svg', 'jpeg'], |
|||
three: ['js'], |
|||
video: ['mp4'] |
|||
}; |
|||
/** |
|||
* uploadType 【string】 主要区别文件上传路径 以及类型 以 web/routes/attachment/index.js 中 UploadPath 的 key 值为准;默认 project; |
|||
* disabled 【boolean】 上传是否可用 |
|||
* maxFilesNum 【number】 最大上传数量 |
|||
* fileTypes 【array[string]】 可允许上传的文件类型; |
|||
* maxFileSize 【number】 单个文件最大大小 M |
|||
* listType 【antd】 upload 组件的属性 |
|||
* onChange 【function】 文件数量变化时候回调 返回文件 |
|||
* value 【array[obj]】 编辑数据 [{url:'xxx', [size:999]}] |
|||
* onStateChange 【function】 文件状态改变回调函数 上传中 return { uploading:true/false } |
|||
*/ |
|||
const { |
|||
uploadType, |
|||
disabled, |
|||
maxFilesNum, |
|||
fileTypes, |
|||
maxFileSize, |
|||
listType, |
|||
onChange, |
|||
value, |
|||
showUploadList, |
|||
onStateChange |
|||
} = this.props; |
|||
const { fileList, curPreviewPic, delPicIng, removeFilesList } = this.state; |
|||
const that = this; |
|||
let uploadType_ = uploadType || 'project'; |
|||
let maxFilesNum_ = maxFilesNum || 1; |
|||
let defaultFileTypes = fileTypes || UploadPath[uploadType_]; |
|||
const uploadProps = { |
|||
name: 'checkFile_', |
|||
multiple: false, |
|||
showUploadList: showUploadList || true, |
|||
action: "/_upload/new?type=project", |
|||
listType: listType || 'text', |
|||
disabled: disabled, |
|||
beforeUpload: (file) => { |
|||
if (fileList.length >= maxFilesNum_) { |
|||
message.warning(`最多选择${maxFilesNum_}个文件上传`); |
|||
return false; |
|||
} |
|||
if (file.name.length > 60) { |
|||
message.warning(`文件名过长(大于60字符),请修改后上传`); |
|||
return false; |
|||
} |
|||
const extNames = file.name.split('.'); |
|||
var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/; |
|||
// if (!reg.exec(file.name)) {
|
|||
// message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`);
|
|||
// return false;
|
|||
// }
|
|||
let isDAE = false; |
|||
if (extNames.length > 0) { |
|||
let fileType = extNames[extNames.length - 1].toLowerCase(); |
|||
isDAE = defaultFileTypes.some((f) => f == fileType); |
|||
} |
|||
if (!isDAE) { |
|||
message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`); |
|||
return false; |
|||
} |
|||
const isLt = file.size / 1024 / 1024 < (maxFileSize || 3); |
|||
if (!isLt) { |
|||
message.error(`文件必须小于${maxFileSize || 3}MB!`); |
|||
return false; |
|||
} |
|||
this.setState({ |
|||
fileUploading: true |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: true }); |
|||
} |
|||
}, |
|||
onChange(info) { |
|||
const status = info.file.status; |
|||
if (status === 'uploading') { |
|||
that.setState({ |
|||
fileList: info.fileList |
|||
}); |
|||
} |
|||
if (status === 'done') { |
|||
let { filename } = info.file.response; |
|||
let size = info.file.size; |
|||
let nextFileList = fileList; |
|||
nextFileList[nextFileList.length - 1] = { |
|||
uid: -moment().unix(), |
|||
name: info.file.name, |
|||
status: 'done', |
|||
storageUrl: filename, |
|||
url: filename, |
|||
size: size |
|||
}; |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileUploading: false, |
|||
fileList: nextFileList |
|||
}); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} else if (status === 'error') { |
|||
that.setState({ |
|||
fileUploading: false |
|||
}); |
|||
message.error(`${info.file.name} 上传失败,请重试`); |
|||
if (onStateChange) { |
|||
onStateChange({ uploading: false }); |
|||
} |
|||
} |
|||
}, |
|||
onRemove(file) { |
|||
let nextFileList = []; |
|||
fileList.map((f, i) => { |
|||
if (f.uid != file.uid) { |
|||
nextFileList.push(f); |
|||
} |
|||
}); |
|||
that.deleteFile(file); |
|||
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]); |
|||
if (curPreviewPic == file.url) { |
|||
that.setState({ |
|||
curPreviewPic: '' |
|||
}); |
|||
} |
|||
onChange(nextFileList); |
|||
that.setState({ |
|||
fileList: nextFileList, |
|||
removeFilesList: nextRemoveFiles |
|||
}); |
|||
}, |
|||
onPreview(file) { |
|||
let filePostfix = file.url.split('.').pop(); |
|||
filePostfix = filePostfix.toLowerCase(); |
|||
if (UploadPath.image.some((img) => img == filePostfix)) { |
|||
that.setState({ |
|||
curPreviewPic: file.url |
|||
}); |
|||
} else { |
|||
message.warn('仅支持图片预览'); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
let fileList_ = fileList |
|||
// .map(f => {
|
|||
// if (f.storageUrl) {
|
|||
// let realName = f.storageUrl.split('/').pop()
|
|||
// if (f.name != realName) {
|
|||
// f.name = realName
|
|||
// }
|
|||
// }
|
|||
// return f
|
|||
// })
|
|||
|
|||
return ( |
|||
<div> |
|||
<Spin spinning={delPicIng}> |
|||
<Upload {...uploadProps} fileList={fileList_}> |
|||
{ |
|||
disabled ? ( |
|||
'' |
|||
) : |
|||
listType == 'picture-card' ? |
|||
( |
|||
fileList.length >= maxFilesNum_ ? null : ( |
|||
<div style={{}}> |
|||
<PlusOutlined /> |
|||
<div>上传图片</div> |
|||
</div> |
|||
) |
|||
) : ( |
|||
<Button disabled={fileList.length >= maxFilesNum_} icon={<UploadOutlined />}> 文件上传 </Button> |
|||
) |
|||
} |
|||
</Upload> |
|||
{ |
|||
curPreviewPic ? ( |
|||
<Card |
|||
bodyStyle={{ |
|||
padding: 8 |
|||
}} |
|||
> |
|||
<div style={{ marginBottom: 8 }} > |
|||
<span>文件预览</span> |
|||
<span |
|||
style={{ float: 'right' }} |
|||
onClick={() => { this.setState({ curPreviewPic: '' }); }} |
|||
> |
|||
<CloseOutlined style={{ fontSize: 20 }} /> |
|||
</span> |
|||
</div> |
|||
<img style={{ width: '100%' }} src={curPreviewPic}></img> |
|||
</Card> |
|||
) : '' |
|||
} |
|||
</Spin> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth } = state |
|||
return { |
|||
user: auth.user |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(Uploads); |
@ -0,0 +1,160 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { Modal, Input, Form, Select, InputNumber, Tooltip, Tag } from 'antd'; |
|||
import { UploadLocal } from '$components'; |
|||
const { TextArea } = Input; |
|||
const MetadataFileModal = (props) => { |
|||
const { onConfirm, onCancel, editData, metadataModels } = props; |
|||
const [form] = Form.useForm(); |
|||
const [editUrl, setEditUrl] = useState([]); |
|||
useEffect(() => { |
|||
}, []); |
|||
const handleOk = () => { |
|||
form.validateFields().then(values => { |
|||
if (onConfirm) { |
|||
let dataSave = JSON.parse(JSON.stringify(values)); |
|||
dataSave.attributesParam = {}; |
|||
metadataModels.map(m => { |
|||
dataSave.attributesParam[m.attributeCode] = values[m.attributeCode]; |
|||
delete dataSave[m.attributeCode]; |
|||
}) |
|||
onConfirm(dataSave); |
|||
} |
|||
}) |
|||
} |
|||
const validatorNull = (rule, value, getFieldValue, validateFields, label) => { |
|||
if (!value || !value.trim().length) { |
|||
return Promise.reject(new Error(`${label}不可空字符串`)); |
|||
} |
|||
return Promise.resolve(); |
|||
} |
|||
const renderModelItems = () => { |
|||
const items = metadataModels.filter(mm => mm.modelType === '文件').map(m => { |
|||
if (m.control === '文本框') { |
|||
const rules = [{ required: !m.nullable, message: '' }] |
|||
if (!m.nullable) { |
|||
rules.push(({ getFieldValue, validateFields }) => ({ |
|||
validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields, m.attributeName) } |
|||
})) |
|||
rules.push({ max: m.length, message: `${m.attributeName}不超过${m.length}个字符` }) |
|||
} |
|||
return <Form.Item |
|||
label={m.attributeName.length > 10 ? <Tooltip title={m.attributeName}> |
|||
{m.attributeName.substring(0, 10) + '...'} |
|||
</Tooltip> : m.attributeName} |
|||
name={m.attributeCode} |
|||
rules={rules}> |
|||
<Input style={{ width: '90%' }} placeholder={`请输入${m.attributeName}`} /> |
|||
</Form.Item> |
|||
} else if (m.control === '数字输入框') { |
|||
const rules = [{ required: !m.nullable, message: `${m.attributeName}不可空` }] |
|||
let maxValue = ''; |
|||
if (m.length) { |
|||
while (m.length > 0) { |
|||
maxValue += '9' |
|||
m.length--; |
|||
} |
|||
} |
|||
return <Form.Item |
|||
label={m.attributeName.length > 10 ? <Tooltip title={m.attributeName}> |
|||
{m.attributeName.substring(0, 10) + '...'} |
|||
</Tooltip> : m.attributeName} |
|||
name={m.attributeCode} |
|||
rules={rules}> |
|||
<InputNumber min={0} max={maxValue ? parseInt(maxValue) : 0} precision={0} style={{ width: '90%' }} placeholder={`请输入${m.attributeName}`} /> |
|||
</Form.Item> |
|||
} else { |
|||
return <Form.Item |
|||
label={m.attributeName.length > 10 ? <Tooltip title={m.attributeName}> |
|||
{m.attributeName.substring(0, 10) + '...'} |
|||
</Tooltip> : m.attributeName} |
|||
name={m.attributeCode} |
|||
rules={[{ required: !m.nullable, message: `${m.attributeName}不可空` }]}> |
|||
<Select |
|||
placeholder={`请选择${m.attributeName}`} |
|||
style={{ width: '90%' }} |
|||
showSearch |
|||
optionFilterProp='children' |
|||
getPopupContainer={triggerNode => triggerNode.parentNode} |
|||
filterOption={(input, option) => option.props.children |
|||
.toLowerCase().indexOf(input.toLowerCase()) >= 0} |
|||
> |
|||
<Select.Option value={'是'} key={'是'}>是</Select.Option> |
|||
<Select.Option value={'否'} key={'否'}>否</Select.Option> |
|||
</Select> |
|||
</Form.Item > |
|||
} |
|||
}) |
|||
return items; |
|||
} |
|||
|
|||
const vsjunct = (params) => { |
|||
if (params.length) { |
|||
let appendix = [] |
|||
for (let p of params) { |
|||
appendix.push({ |
|||
fName: p.name, |
|||
size: p.size, |
|||
fileSize: p.size, |
|||
storageUrl: p.storageUrl,//必须有storageUrl
|
|||
}) |
|||
} |
|||
setEditUrl(appendix) |
|||
} else { |
|||
setEditUrl([]) |
|||
} |
|||
} |
|||
const handleCancel = () => { |
|||
if (editData.add) { |
|||
if (form.getFieldValue('files') && form.getFieldValue('files').length) { |
|||
onCancel(form.getFieldValue('files')[0]); |
|||
} else { |
|||
onCancel(null); |
|||
} |
|||
} |
|||
} |
|||
return ( |
|||
<Modal title={editData.title} open={true} destroyOnClose |
|||
okText='确定' width={800} |
|||
onOk={() => handleOk(null)} |
|||
onCancel={() => handleCancel()}> |
|||
<Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 18 }} initialValues={editData.record || {}}> |
|||
<Form.Item |
|||
label='文件名称' |
|||
name='name' |
|||
rules={[{ required: true, message: '' }, { max: 50, message: `文件名称不超过50个字符` }, |
|||
({ getFieldValue, validateFields }) => ({ |
|||
validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields, '文件名称') } |
|||
})]}> |
|||
<Input style={{ width: '90%' }} placeholder={`请输入文件名称`} /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
label='文件描述' |
|||
name='description' |
|||
rules={[{ max: 255, message: `文件描述不超过255个字符` }]}> |
|||
<TextArea rows={4} style={{ width: '90%' }} placeholder={`请输入文件描述`} /> |
|||
</Form.Item> |
|||
<Form.Item |
|||
label='文件' |
|||
name='files' |
|||
rules={[{ required: true, message: '文件不可为空' }]}> |
|||
<UploadLocal |
|||
isLocal={true} |
|||
maxFilesNum={1} |
|||
maxFileSize={40} |
|||
onChange={vsjunct} |
|||
fileTypes={["jpg", "png", "gif", "txt", "doc", "docx", "pdf", "xls", "xlsx", "zip", "rar"]} |
|||
value={editUrl} |
|||
defaultValue={editUrl} |
|||
fileList={editData.record.files || []} |
|||
/> |
|||
</Form.Item> |
|||
<Form.Item style={{ paddingLeft: 190 }}> |
|||
<Tag color="orange">文件大小不超过40MB,开放资源包含多个文件,建议将文件进行压缩,形成压缩包再上传</Tag> |
|||
<Tag color="orange">支持的文件格式:jpg,png,gif,txt,doc,docx,pdf,xsl,xlsx,zip,rar</Tag> |
|||
</Form.Item> |
|||
{renderModelItems()} |
|||
</Form> |
|||
</Modal > |
|||
) |
|||
} |
|||
export default MetadataFileModal; |
@ -1,105 +1,386 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { Spin, Table, Popconfirm } from 'antd'; |
|||
import { Spin, Table, Popconfirm, Button, Input } from 'antd'; |
|||
import { ButtonGroup } from '$components'; |
|||
import moment from 'moment'; |
|||
import FileSaver from 'file-saver'; |
|||
import MetadataFileModal from '../components/metadataFileModal'; |
|||
import MetadataTagModal from '../components/metadataTagModal'; |
|||
import MetadataResourceModal from '../components/metadataResourceModal'; |
|||
import { RouteRequest } from '@peace/utils'; |
|||
import { RouteTable } from '$utils' |
|||
|
|||
const FilesTable = (props) => { |
|||
const { user, dispatch, actions, clientHeight, resourceCatalogId, isRequesting } = props; |
|||
const { user, dispatch, actions, clientHeight, resourceCatalogId, resourceCatalogKey, |
|||
isRequesting, metadataModels, tagList, metadataResourceApplications } = props; |
|||
const { metadataManagement } = actions; |
|||
const [resourceCatalogData, setResourceCatalogData] = useState([]); |
|||
const [limit, setLimit] = useState(10) |
|||
const [offset, setOffset] = useState(0) |
|||
const SortValues = { 'ascend': 'asc', 'descend': 'desc' }; |
|||
const [tableData, setTableData] = useState([]); |
|||
const [tableDataCount, setTableDataCount] = useState(0);//Table数据
|
|||
const [updateAtSort, setUpdateAtSort] = useState('descend'); |
|||
const [keywords, setKeywords] = useState(''); |
|||
const [limit, setLimit] = useState(10); |
|||
const [currentPage, setCurrentPage] = useState(1); |
|||
const [selectedRowKeys, setSelectedRowKeys] = useState([]); |
|||
const [selectedRows, setSelectedRows] = useState([]); |
|||
const [modalVisible, setModalVisible] = useState(false); |
|||
const [editData, setEditData] = useState({}); |
|||
const [tagModalVisible, setTagModalVisible] = useState(false); |
|||
const [editTagData, setEditTagData] = useState({}); |
|||
const [resourceModalVisible, setResourceModalVisible] = useState(false); |
|||
const [editResourceData, setEditResourceData] = useState({}); |
|||
|
|||
useEffect(() => { |
|||
initData(resourceCatalogId); |
|||
}, []); |
|||
dispatch(metadataManagement.getTagList()); |
|||
setUpdateAtSort('descend'); |
|||
initData({ limit, offset: currentPage - 1, orderDirection: SortValues[updateAtSort] }); |
|||
}, [resourceCatalogId]); |
|||
|
|||
const initData = (resourceCatalogId) => { |
|||
dispatch(metadataManagement.getMetadataFiles({ catalog: resourceCatalogId, limit, offset })).then(res => { |
|||
const { data } = res.payload; |
|||
const initData = (query = {}) => { |
|||
dispatch(metadataManagement.getMetadataFiles({ catalog: resourceCatalogId, keywords, orderBy: 'updateAt', ...query })).then(res => { |
|||
if (res.success) { |
|||
setTableData(res.payload.data.rows); |
|||
setTableDataCount(res.payload.data.count); |
|||
let resourceNames = []; |
|||
res.payload.data.rows.map(r => { |
|||
resourceNames.push(r.name); |
|||
}) |
|||
if (resourceNames.length) |
|||
dispatch(metadataManagement.getMetadataResourceApplications({ resourceNames: resourceNames.join(','), type: '文件' })) |
|||
} |
|||
|
|||
}) |
|||
} |
|||
const onEdit = (record) => { |
|||
dispatch(metadataManagement.getMetadataModels({ modelTypes: '文件' })).then(res => { |
|||
if (res.success) { |
|||
setEditData({ |
|||
title: '修改文件元数据', record: { |
|||
...record, ...record.attributesParam, |
|||
files: [{ |
|||
url: "\\assets\\files\\common\\" + record.name + '.' + record.type, name: record.name |
|||
}] |
|||
} |
|||
}); |
|||
setModalVisible(true); |
|||
} |
|||
}) |
|||
} |
|||
const onEdit = (record) => { } |
|||
const confirmDelete = (id) => { } |
|||
const marking = (id) => { } |
|||
const applyResources = (id) => { } |
|||
const columns = [{ |
|||
title: '文件名称', |
|||
dataIndex: 'name', |
|||
key: 'name', |
|||
width: '20%' |
|||
}, { |
|||
title: '文件描述', |
|||
dataIndex: 'description', |
|||
key: 'description', |
|||
width: '20%' |
|||
}, { |
|||
title: '文件类型', |
|||
dataIndex: 'type', |
|||
key: 'type', |
|||
width: '20%' |
|||
}, { |
|||
title: '标签', |
|||
dataIndex: 'tags', |
|||
key: 'tags', |
|||
width: '20%' |
|||
}, { |
|||
title: '大小', |
|||
dataIndex: 'size', |
|||
key: 'size', |
|||
width: '20%' |
|||
}, { |
|||
title: '创建者', |
|||
dataIndex: 'createBy', |
|||
key: 'createBy', |
|||
width: '10%', |
|||
}, { |
|||
title: '修改时间', |
|||
dataIndex: 'updateAt', |
|||
key: 'updateAt', |
|||
width: '20%', |
|||
render: (text, record, index) => { |
|||
return text ? moment(text).format('YYYY-MM-DD HH:mm') : '' |
|||
} |
|||
}, { |
|||
title: '操作', |
|||
dataIndex: 'action', |
|||
width: '20%', |
|||
render: (text, record) => { |
|||
return <div> |
|||
<a onClick={() => onEdit(record)}>编辑</a> |
|||
|
|||
<Popconfirm |
|||
title="是否确认删除该元数据?若确认删除则关联的数据将一并删除!" |
|||
onConfirm={() => confirmDelete(record.id)} |
|||
> <a>删除</a></Popconfirm> |
|||
|
|||
<a onClick={() => marking(record.id)}>打标</a> |
|||
|
|||
<a onClick={() => applyResources(record.id)}>申请资源</a> |
|||
</div> |
|||
const confirmDelete = (record) => { |
|||
dispatch(metadataManagement.delMetadataFiles(record.id)).then(res => { |
|||
if (res.success) { |
|||
onSearch(); setModalVisible(false); |
|||
deleteFile({ url: "\\assets\\files\\common\\" + record.name + '.' + record.type }) |
|||
} |
|||
}); |
|||
} |
|||
//删除文件
|
|||
const deleteFile = (file) => { |
|||
if (file.url) { |
|||
RouteRequest.delete(RouteTable.cleanUpUploadTrash, { url: file.url }); |
|||
}; |
|||
} |
|||
const marking = (id) => { |
|||
dispatch(metadataManagement.getTagMetadata(id, 'file')).then(res => { |
|||
if (res.success) { |
|||
const obj = { tagSet: [], tags: [], id: id }; |
|||
if (res.payload.data.length) { |
|||
obj.tagSet = res.payload.data.map(d => d.tagSet); |
|||
obj.tags = res.payload.data.map(d => d.id); |
|||
} |
|||
setEditTagData({ record: obj }); |
|||
setTagModalVisible(true); |
|||
} |
|||
}) |
|||
} |
|||
const onConfirmTag = (values) => { |
|||
dispatch(metadataManagement.postTagMetadata({ file: editTagData.record.id, ...values })).then(res => { |
|||
if (res.success) { |
|||
onSearch(); setTagModalVisible(false); |
|||
} |
|||
}); |
|||
} |
|||
const applyResources = (record) => { |
|||
setEditResourceData({ record: { resourceName: record.name, applyBy: user.id, applyByName: user.name, resourceType: '文件' } }); |
|||
setResourceModalVisible(true); |
|||
} |
|||
|
|||
const onConfirmResource = (values) => { |
|||
dispatch(metadataManagement.postMetadataResourceApplications(values)).then(res => { |
|||
if (res.success) { |
|||
onSearch(); setResourceModalVisible(false); |
|||
} |
|||
}); |
|||
} |
|||
const onTableChange = (pagination, filters, sorter) => { |
|||
let limit = Number.parseInt(pagination.pageSize); |
|||
let offset = Number.parseInt(pagination.current) - 1; |
|||
setCurrentPage(pagination.current); |
|||
setLimit(limit); |
|||
let query = { offset, limit, orderDirection: SortValues[updateAtSort] }; |
|||
if (sorter.columnKey === 'updateAt') { |
|||
query.orderDirection = SortValues[sorter.order]; |
|||
setUpdateAtSort(sorter.order); |
|||
} |
|||
}]; |
|||
setSelectedRowKeys([]); |
|||
setSelectedRows([]); |
|||
initData(query); |
|||
} |
|||
|
|||
const getfilesize = (size) => { |
|||
if (!size) |
|||
return "0K"; |
|||
var num = 1024.00; //byte
|
|||
if (size < num) |
|||
return size + "B"; |
|||
if (size < Math.pow(num, 2)) |
|||
return (size / num).toFixed(2) + "KB"; //kb
|
|||
if (size < Math.pow(num, 3)) |
|||
return (size / Math.pow(num, 2)).toFixed(2) + "M"; //M
|
|||
if (size < Math.pow(num, 4)) |
|||
return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G
|
|||
return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T
|
|||
} |
|||
|
|||
const columns = [ |
|||
{ |
|||
title: '文件名称', |
|||
dataIndex: 'name', |
|||
key: 'name', |
|||
width: '16%', |
|||
ellipsis: true |
|||
}, { |
|||
title: '文件描述', |
|||
dataIndex: 'description', |
|||
key: 'description', |
|||
width: '29%', |
|||
ellipsis: true |
|||
}, { |
|||
title: '类型', |
|||
dataIndex: 'type', |
|||
key: 'type', |
|||
width: '10%' |
|||
}, { |
|||
title: '标签', |
|||
dataIndex: 'tags', |
|||
key: 'tags', |
|||
width: '18%', |
|||
ellipsis: true, |
|||
render: (text, record, index) => { |
|||
let tagName = record.tagFiles.map(tagSet => tagSet.tag.name); |
|||
return tagName.join(','); |
|||
} |
|||
}, { |
|||
title: '大小', |
|||
dataIndex: 'size', |
|||
key: 'size', |
|||
width: '10%', |
|||
render: (text) => <span>{getfilesize(text)}</span> |
|||
}, { |
|||
title: '修改时间', |
|||
dataIndex: 'updateAt', |
|||
key: 'updateAt', |
|||
width: '18%', |
|||
sortOrder: updateAtSort, |
|||
sorter: (a, b) => moment(a.updateAt).valueOf() - moment(b.updateAt).valueOf(), |
|||
sortDirections: ['descend', 'ascend', 'descend'], |
|||
render: (text, record, index) => { |
|||
return text && moment(text).format('YYYY-MM-DD HH:mm:ss') || '' |
|||
} |
|||
}, { |
|||
title: '操作', |
|||
dataIndex: 'action', |
|||
width: '8%', |
|||
render: (text, record) => { |
|||
let resourceApplicationsRecords = metadataResourceApplications.filter(ra => |
|||
ra.applyBy == user.id && ra.resourceName === record.name); |
|||
return <ButtonGroup> |
|||
<a style={{ marginLeft: 10 }} onClick={() => onEdit(record)}>编辑</a> |
|||
<Popconfirm |
|||
title="是否确认删除该元数据?" |
|||
onConfirm={() => confirmDelete(record.id)} |
|||
> <a style={{ marginLeft: 10 }}>删除</a></Popconfirm> |
|||
<a style={{ marginLeft: 10 }} onClick={() => marking(record.id)}>打标</a> |
|||
{resourceApplicationsRecords.length === 0 ? |
|||
<a style={{ marginLeft: 10 }} onClick={() => applyResources(record)}>申请资源</a> : |
|||
<span style={{ marginLeft: 10, color: "#c0c0c0" }} title='已存在资源申请'>申请资源</span>} |
|||
</ButtonGroup> |
|||
} |
|||
}]; |
|||
|
|||
const onSearch = () => { |
|||
setSelectedRowKeys([]); |
|||
setSelectedRows([]); |
|||
setCurrentPage(1); |
|||
initData({ limit, offset: 0, orderDirection: SortValues[updateAtSort] }); |
|||
} |
|||
const handleExport = (isAll = false) => { |
|||
let tableHeader = `<tr>`; |
|||
columns.filter(c => c.dataIndex != 'action').map(c => { tableHeader += `<th><div>${c.title}</div></th>`; }); |
|||
tableHeader += '</tr>'; |
|||
if (isAll) { |
|||
dispatch(metadataManagement.getMetadataFiles({ catalog: resourceCatalogId })).then(res => { |
|||
if (res.success) { |
|||
handleExportTable(tableHeader, res.payload.data.rows, isAll); |
|||
} |
|||
}) |
|||
} else { |
|||
let data = [] |
|||
if (updateAtSort === 'descend') { |
|||
data = selectedRows.sort((a, b) => moment(b.updateAt).valueOf() - moment(a.updateAt).valueOf()); |
|||
} else { |
|||
data = selectedRows.sort((a, b) => moment(a.updateAt).valueOf() - moment(b.updateAt).valueOf()); |
|||
} |
|||
handleExportTable(tableHeader, data); |
|||
} |
|||
} |
|||
const handleExportTable = (tableHeader, contentData, isAll = false) => { |
|||
let tableContent = ''; |
|||
contentData.map(cd => { |
|||
tableContent += `<tr>`; |
|||
tableContent += `<th style="font-weight:600"><div>${cd.name}</div></th>`; |
|||
tableContent += `<th style="font-weight:600"><div>${cd.description}</div></th>`; |
|||
tableContent += `<th style="font-weight:600"><div>${cd.type}</div></th>`; |
|||
let tagName = cd.tagFiles.map(tagSet => tagSet.tag.name); |
|||
tableContent += `<th style="font-weight:600"><div>${tagName.join(',')}</div></th>`; |
|||
tableContent += `<th style="font-weight:600"><div>${cd.size}</div></th>`; |
|||
tableContent += `<th style="font-weight:600"><div>${moment(cd.updateAt).format('YYYY-MM-DD HH:mm:ss')}</div></th>`; |
|||
tableContent += `</tr>`; |
|||
}) |
|||
let exportTable = `\uFEFF<table border="1">
|
|||
${tableHeader} |
|||
${tableContent} |
|||
</table>`; |
|||
let tempStr = new Blob([exportTable], { type: 'text/plain;charset=utf-8' }); |
|||
FileSaver.saveAs(tempStr, `文件元数据导出.xls`); |
|||
} |
|||
//新建、修改
|
|||
const onConfirm = (values) => { |
|||
let obj = {} |
|||
if (editData.add) { |
|||
obj = { createBy: user.id, catalog: resourceCatalogId, catalogKey: resourceCatalogKey, ...values } |
|||
if (values.files && values.files.length) { |
|||
obj.type = values.files[0].name.split('.').pop(); |
|||
obj.size = values.files[0].size; |
|||
} |
|||
dispatch(metadataManagement.postMetadataFiles(obj)).then(() => { |
|||
onSearch(); setModalVisible(false); |
|||
}); |
|||
} else { |
|||
obj = { catalog: resourceCatalogId, catalogKey: resourceCatalogKey, ...values } |
|||
if (values.files && values.files.length) { |
|||
obj.type = values.files[0].name.split('.').pop(); |
|||
obj.size = values.files[0].size; |
|||
} |
|||
dispatch(metadataManagement.putMetadataFiles(editData.record.id, obj)).then(res => { |
|||
if (res.success) { |
|||
onSearch(); setModalVisible(false); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
return <Spin spinning={isRequesting}> |
|||
<Table scroll={{ y: clientHeight - 320 }} |
|||
rowKey='filesId' |
|||
<div style={{ marginBottom: 16 }}> |
|||
<Button type='primary' onClick={() => { |
|||
dispatch(metadataManagement.getMetadataModels({ modelTypes: '文件' })).then(res => { |
|||
if (res.success) { |
|||
setEditData({ add: true, title: '新建文件元数据', record: {} }); |
|||
setModalVisible(true); |
|||
} |
|||
}) |
|||
}}>新建</Button> |
|||
{ |
|||
tableDataCount == 0 ? <Button disabled={tableDataCount == 0} style={{ marginLeft: 16 }} onClick={() => handleExport()}>导出</Button> : |
|||
selectedRowKeys && selectedRowKeys.length ? |
|||
<Button disabled={tableDataCount == 0} style={{ marginLeft: 16 }} onClick={() => handleExport()}>导出</Button> |
|||
: <Popconfirm title={'是否导出全部?'} onConfirm={() => handleExport(true)} okText="确定" cancelText="取消"> |
|||
<Button disabled={tableDataCount == 0} style={{ marginLeft: 16 }}> 导出</Button> |
|||
</Popconfirm> |
|||
} |
|||
<Button type='primary' style={{ marginLeft: 16, float: 'right' }} onClick={onSearch}>查询</Button> |
|||
<Input style={{ width: 220, float: 'right' }} placeholder="输入名称/类型" |
|||
allowClear onPressEnter={onSearch} onChange={e => setKeywords(e.target.value || '')} /> |
|||
</div > |
|||
<Table |
|||
scroll={{ y: clientHeight - 320 }} |
|||
rowKey='id' |
|||
columns={columns} |
|||
dataSource={[]}> |
|||
dataSource={tableData} |
|||
onChange={onTableChange} |
|||
pagination={{ |
|||
current: currentPage, |
|||
pageSize: limit, |
|||
total: tableDataCount, |
|||
showSizeChanger: true, |
|||
// showQuickJumper: true,
|
|||
showTotal: (total) => { return <span style={{ fontSize: 15 }}>{`共${Math.ceil(total / limit)}页,${total}项`}</span> }, |
|||
onShowSizeChange: (currentPage, pageSize) => { |
|||
setCurrentPage(currentPage); |
|||
setLimit(pageSize); |
|||
}, |
|||
onChange: (page, pageSize) => { |
|||
setSelectedRowKeys([]); |
|||
setSelectedRows([]); |
|||
setCurrentPage(page); |
|||
setLimit(pageSize); |
|||
let queryParams = { |
|||
orderDirection: SortValues[updateAtSort], |
|||
page: page - 1, |
|||
size: pageSize |
|||
}; |
|||
initData(queryParams); |
|||
} |
|||
}} |
|||
rowSelection={{ |
|||
onChange: (selectedRowKeys, selectedRows) => { |
|||
setSelectedRowKeys(selectedRowKeys) |
|||
setSelectedRows(selectedRows); |
|||
}, |
|||
selectedRowKeys: selectedRowKeys |
|||
}} |
|||
> |
|||
</Table> |
|||
</Spin> |
|||
|
|||
{ |
|||
modalVisible ? |
|||
<MetadataFileModal |
|||
metadataModels={metadataModels.filter(m => m.modelType === '文件')} |
|||
editData={editData} |
|||
onCancel={(file) => { |
|||
setModalVisible(false) |
|||
if (file) |
|||
deleteFile(file); |
|||
}} |
|||
onConfirm={onConfirm} /> : '' |
|||
} |
|||
{ |
|||
tagModalVisible ? |
|||
<MetadataTagModal |
|||
tagList={tagList} |
|||
editData={editTagData} |
|||
onCancel={() => setTagModalVisible(false)} |
|||
onConfirm={onConfirmTag} /> : '' |
|||
} |
|||
{ |
|||
resourceModalVisible ? |
|||
<MetadataResourceModal |
|||
editData={editResourceData} |
|||
onCancel={() => setResourceModalVisible(false)} |
|||
onConfirm={onConfirmResource} /> : '' |
|||
} |
|||
</Spin > |
|||
} |
|||
function mapStateToProps(state) { |
|||
const { global, auth, metadataFiles } = state; |
|||
const { global, auth, metadataDatabases, metadataModels, tagList, tagMetadata, metadataResourceApplications } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
clientHeight: global.clientHeight, |
|||
isRequesting: metadataFiles.isRequesting |
|||
isRequesting: metadataDatabases.isRequesting || metadataModels.isRequesting || tagList.isRequesting |
|||
|| tagMetadata.isRequesting || metadataResourceApplications.isRequesting, |
|||
metadataModels: metadataModels.data, |
|||
tagList: tagList.data || [], |
|||
tagMetadata: tagMetadata.data || [], |
|||
metadataResourceApplications: metadataResourceApplications.data || [] |
|||
}; |
|||
} |
|||
export default connect(mapStateToProps)(FilesTable) |
Loading…
Reference in new issue