wangyue
2 years ago
62 changed files with 22705 additions and 2571 deletions
@ -0,0 +1,237 @@ |
|||
'use strict'; |
|||
|
|||
const request = require('superagent'); |
|||
const moment = require('moment'); |
|||
|
|||
async function createProjectDir(ctx, next) { |
|||
let error = { message: '新增项目目录失败' }, rs = null; |
|||
const { roadName } = ctx.query; |
|||
|
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
|
|||
rs = await models.FileRoad.create({ |
|||
roadName |
|||
}) |
|||
error = null; |
|||
} catch (err) { |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = { message: '新增项目目录成功!', result: rs }; |
|||
} |
|||
} |
|||
|
|||
async function getFileDirs(ctx, next) { |
|||
let error = { message: '查询项目目录失败' }, rslt = null; |
|||
|
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
|
|||
rslt = await models.FileRoad.findAll({ |
|||
// include: [{
|
|||
// model: models.FileType
|
|||
// }]
|
|||
}) |
|||
error = null; |
|||
} catch (err) { |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = rslt; |
|||
} |
|||
} |
|||
|
|||
async function delFileDir(ctx, next) { |
|||
let error = { message: '文件夹删除失败' }; |
|||
let rslt = [], fileDirIds = []; |
|||
|
|||
try { |
|||
const { id } = ctx.query, // type == parent / child
|
|||
models = ctx.fs.dc.models; |
|||
const transaction = await ctx.fs.dc.orm.transaction(); |
|||
|
|||
await models.FileRoad.destroy({ where: { rId: id }, transaction }) |
|||
await models.Files.destroy({ where: { roadId: id }, transaction }) |
|||
|
|||
await transaction.commit(); |
|||
|
|||
error = null; |
|||
} catch (err) { |
|||
await transaction.rollback(); |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = { message: '文件夹删除成功' }; |
|||
} |
|||
} |
|||
|
|||
async function uploadFile(ctx, next) { |
|||
let error = { message: '文件上传失败' }, rslt = null; |
|||
const { typeId, userId, userName, fileSize, fileName, fileUrl, fileExt, roadId } = ctx.request.body |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
|
|||
rslt = await models.Files.create({ |
|||
fId: typeId, |
|||
uploaderId: userId, |
|||
roadId, |
|||
createDate: moment().format('YYYY-MM-DD HH:mm:ss'), |
|||
fileSize, |
|||
fileName, |
|||
fileUrl, |
|||
fileExt, |
|||
uploaderName: userName, |
|||
isDelete: false |
|||
}) |
|||
error = null; |
|||
} catch (err) { |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = { message: '文件上传成功', rslt }; |
|||
} |
|||
} |
|||
|
|||
async function deleteFile(ctx, next) { |
|||
let error = { message: '文件删除失败' }, rslt = null; |
|||
const { id } = ctx.query; |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
|
|||
rslt = await models.Files.update({ |
|||
isDelete: true |
|||
}, { |
|||
where: { id: id } |
|||
}) |
|||
error = null; |
|||
} catch (err) { |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = { message: '文件删除成功', rslt }; |
|||
} |
|||
} |
|||
|
|||
async function getFileList(ctx, next) { |
|||
let error = { message: '文件上传失败' }, rslt = { list: [], counter: 0, type: '' }; |
|||
const { fId, limit, offset, searchTxt, roadId } = ctx.query; |
|||
let limit_ = limit, offset_ = offset; |
|||
if (limit == null || limit < 0) { |
|||
limit_ = 10; |
|||
} |
|||
if (offset == null || offset < 0) { |
|||
offset_ = 0; |
|||
} |
|||
try { |
|||
const models = ctx.fs.dc.models; |
|||
let queryOptions = { isDelete: false } |
|||
if (searchTxt && searchTxt.trim() != '') { |
|||
queryOptions.fileName = { $like: `%${searchTxt}%` } |
|||
} |
|||
if (fId) { |
|||
queryOptions.fId = fId |
|||
} |
|||
|
|||
if (roadId) { |
|||
queryOptions.roadId = roadId |
|||
} |
|||
|
|||
rslt.type = await models.FileType.findOne({ |
|||
where: { fId } |
|||
}) |
|||
|
|||
rslt.list = await models.Files.findAll({ |
|||
where: queryOptions, |
|||
offset: offset_, |
|||
limit: limit_, |
|||
order: [ |
|||
['id', 'DESC'] |
|||
] |
|||
}) |
|||
rslt.counter = await models.Files.count({ |
|||
where: queryOptions |
|||
}) |
|||
error = null; |
|||
} catch (err) { |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = rslt; |
|||
} |
|||
} |
|||
|
|||
function updateStructDir(opts) { |
|||
|
|||
return async function (ctx, next) { |
|||
let error = { message: '文件夹名称更新失败' }; |
|||
const models = ctx.fs.dc.models; |
|||
const { id, name } = ctx.query; |
|||
try { |
|||
await models.FileRoad.update({ roadName: name }, { |
|||
where: { |
|||
id: id |
|||
}, |
|||
}) |
|||
|
|||
error = null; |
|||
} catch (err) { |
|||
ctx.status = 500; |
|||
ctx.body = { detail: err, ...error }; |
|||
} |
|||
|
|||
if (error) { |
|||
ctx.status = 400; |
|||
ctx.body = { ...error }; |
|||
} else { |
|||
ctx.status = 200; |
|||
ctx.body = { message: '文件夹名称更新成功' }; |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
uploadFile, |
|||
deleteFile, |
|||
getFileList, |
|||
createProjectDir, |
|||
delFileDir, |
|||
getFileDirs, |
|||
updateStructDir, |
|||
} |
@ -0,0 +1,31 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = function (dc) { |
|||
const FileRoad = dc.orm.define( |
|||
'fileRoad', |
|||
{ |
|||
rId: { |
|||
field: 'id', |
|||
type: dc.ORM.INTEGER, |
|||
primaryKey: true, |
|||
autoIncrement: true, |
|||
allowNull: false |
|||
}, |
|||
roadName: { |
|||
field: 'road_name', |
|||
type: dc.ORM.STRING, |
|||
}, |
|||
originalData: { |
|||
field: 'original_data', |
|||
type: dc.ORM.STRING, |
|||
}, |
|||
}, |
|||
{ |
|||
tableName: 'file_road' |
|||
} |
|||
); |
|||
|
|||
dc.models.FileRoad = FileRoad; |
|||
|
|||
return FileRoad; |
|||
}; |
@ -0,0 +1,32 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = function (dc) { |
|||
const FileType = dc.orm.define( |
|||
'fileType', |
|||
{ |
|||
fId: { |
|||
field: 'id', |
|||
type: dc.ORM.INTEGER, |
|||
primaryKey: true, |
|||
autoIncrement: true, |
|||
allowNull: false |
|||
}, |
|||
fileType: { |
|||
field: 'file_type', |
|||
type: dc.ORM.STRING, |
|||
}, |
|||
rId: { |
|||
field: 'file_road', |
|||
type: dc.ORM.INTEGER, |
|||
allowNull: false |
|||
}, |
|||
}, |
|||
{ |
|||
tableName: 'file_type' |
|||
} |
|||
); |
|||
|
|||
dc.models.FileType = FileType; |
|||
|
|||
return FileType; |
|||
}; |
@ -0,0 +1,73 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = function (dc) { |
|||
const Files = dc.orm.define( |
|||
'files', |
|||
{ |
|||
id: { |
|||
field: 'id', |
|||
type: dc.ORM.INTEGER, |
|||
primaryKey: true, |
|||
autoIncrement: true, |
|||
allowNull: false |
|||
}, |
|||
fId: { |
|||
field: 'file_type', |
|||
type: dc.ORM.INTEGER |
|||
}, |
|||
roadId: { |
|||
field: 'road_id', |
|||
type: dc.ORM.INTEGER |
|||
}, |
|||
uploaderId: { |
|||
field: 'uploader_id', |
|||
type: dc.ORM.INTEGER |
|||
}, |
|||
uploaderName: { |
|||
field: 'uploader_name', |
|||
type: dc.ORM.INTEGER |
|||
}, |
|||
startDate: { |
|||
field: 'start_date', |
|||
type: dc.ORM.DATE, |
|||
}, |
|||
endDate: { |
|||
field: 'end_date', |
|||
type: dc.ORM.DATE, |
|||
}, |
|||
createDate: { |
|||
field: 'create_date', |
|||
type: dc.ORM.DATE, |
|||
}, |
|||
fileSize: { |
|||
field: 'file_size', |
|||
type: dc.ORM.INTEGER, |
|||
}, |
|||
fileName: { |
|||
field: 'file_name', |
|||
type: dc.ORM.STRING, |
|||
}, |
|||
|
|||
fileUrl: { |
|||
field: 'file_url', |
|||
type: dc.ORM.STRING, |
|||
}, |
|||
fileExt: { |
|||
field: 'file_ext', |
|||
type: dc.ORM.STRING, |
|||
}, |
|||
isDelete: { |
|||
field: 'is_delete', |
|||
type: dc.ORM.BOOLEAN, |
|||
}, |
|||
|
|||
}, |
|||
{ |
|||
tableName: 'files' |
|||
} |
|||
); |
|||
|
|||
dc.models.Files = Files; |
|||
|
|||
return Files; |
|||
}; |
@ -0,0 +1,21 @@ |
|||
'use strict'; |
|||
|
|||
const pan = require('../../controllers/file'); |
|||
|
|||
module.exports = function (app, router, opts, panCode) { |
|||
|
|||
router.get('/create/struct/dir', pan.createProjectDir); |
|||
|
|||
router.get('/get/file/dirs', pan.getFileDirs); |
|||
|
|||
router.get('/netdisk-files/dir/delete', pan.delFileDir); |
|||
|
|||
router.get('/netdisk-files/struct/dir/update', pan.updateStructDir(opts)) |
|||
|
|||
router.post('/netdisk-files/upload', pan.uploadFile); |
|||
|
|||
router.get('/netdisk-files/delete', pan.deleteFile); |
|||
|
|||
router.get('/netdisk-files/query', pan.getFileList); |
|||
|
|||
}; |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,89 @@ |
|||
import { basicAction } from '@peace/utils' |
|||
import { ApiTable } from '$utils' |
|||
|
|||
|
|||
export function createFileDir(query) { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'CREATE_FILE_DIR', |
|||
url: ApiTable.createFileDir, |
|||
query, |
|||
msg: { error: '创建文件夹失败' }, |
|||
// reducer: { name: 'uploadFile' }
|
|||
}); |
|||
} |
|||
|
|||
export function delFileDir(query) { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'DEL_FILE_DIR', |
|||
url: ApiTable.delFileDir, |
|||
query, |
|||
msg: { error: '删除文件夹失败' }, |
|||
// reducer: { name: 'uploadFile' }
|
|||
}); |
|||
} |
|||
|
|||
export function queryFileDir(query) { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'QUERY_FILE_DIR', |
|||
url: ApiTable.queryFileDIr, |
|||
query, |
|||
msg: { error: '查询文件夹失败' }, |
|||
reducer: { name: 'fileDirs' } |
|||
}); |
|||
} |
|||
|
|||
export function updateFileDir(query) { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'UPDATE_FILE_DIR', |
|||
url: ApiTable.updateFileDir, |
|||
query, |
|||
msg: { error: '更新文件夹名称失败' }, |
|||
// reducer: { name: 'fileDirs' }
|
|||
}); |
|||
} |
|||
|
|||
|
|||
// data : {typeId, userId, userName, startDate, endDate, fileSize, fileName, fileUrl, fileExt}
|
|||
export function uploadFile(data) { |
|||
return dispatch => basicAction({ |
|||
type: 'post', |
|||
dispatch: dispatch, |
|||
actionType: 'UPLOAD_FILE', |
|||
url: ApiTable.uploadFile, |
|||
data, |
|||
msg: { error: '上传文件失败' }, |
|||
reducer: { name: 'uploadFile' } |
|||
}); |
|||
} |
|||
|
|||
export function deleteFile(id) { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'DELETE_FILE', |
|||
url: ApiTable.deleteFile, |
|||
msg: { error: '删除文件数据失败' }, |
|||
query: { id }, |
|||
reducer: { name: 'fileDel' } |
|||
}); |
|||
} |
|||
|
|||
export function getFileList(query) { // fId, limit, offset, searchTxt
|
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
actionType: 'GET_FILE_LIST', |
|||
url: ApiTable.getFileList, |
|||
query, |
|||
msg: { error: '获取档案数据失败' }, |
|||
reducer: { name: 'fileList' } |
|||
}); |
|||
} |
@ -0,0 +1,94 @@ |
|||
import React, { useEffect } from 'react'; |
|||
import PropTypes from 'prop-types'; |
|||
import { delFileDir, queryFileDir, updateFileDir, } from '../../actions/file'; |
|||
import './menu.less' |
|||
import { message, Modal } from 'antd'; |
|||
import { ExclamationCircleOutlined } from '@ant-design/icons' |
|||
|
|||
const { confirm } = Modal; |
|||
|
|||
const FunctionMenu = props => { |
|||
const { dispatch, onDelDir, selectRoad } = props; |
|||
|
|||
useEffect(() => { |
|||
const box = document.getElementById('tree-box'); |
|||
if (box) |
|||
box.oncontextmenu = function (e) { |
|||
//取消默认的浏览器自带右键 很重要!!
|
|||
e.preventDefault(); |
|||
|
|||
//获取我们自定义的右键菜单
|
|||
var menu = document.querySelector("#rihgt-click-menu"); |
|||
|
|||
//根据事件对象中鼠标点击的位置,进行定位
|
|||
menu.style.left = e.clientX + 'px'; |
|||
menu.style.top = e.clientY + 'px'; |
|||
|
|||
//改变自定义菜单的高宽,让它显示出来
|
|||
menu.style.display = 'block'; |
|||
} |
|||
|
|||
//关闭右键菜单,很简单
|
|||
window.onclick = function (e) { |
|||
//用户触发click事件就可以关闭了,因为绑定在window上,按事件冒泡处理,不会影响菜单的功能
|
|||
document.querySelector('#rihgt-click-menu') ? document.querySelector('#rihgt-click-menu').style.display = 'none' : '' |
|||
} |
|||
}, [true]) |
|||
|
|||
const onDeleteDir = () => { |
|||
|
|||
if (selectRoad) { |
|||
const id = selectRoad |
|||
dispatch(delFileDir({ id })).then(res => { |
|||
const { type } = res; |
|||
if (type == 'DEL_FILE_DIR_SUCCESS') { |
|||
dispatch(queryFileDir()); |
|||
message.success('删除成功') |
|||
} else { |
|||
message.error('删除失败') |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
const showDeleteConfirm = () => { |
|||
|
|||
confirm({ |
|||
title: `是否确认删除该道路?`, |
|||
icon: <ExclamationCircleOutlined />, |
|||
// content: 'Some descriptions',
|
|||
okText: '是', |
|||
okType: 'danger', |
|||
cancelText: '否', |
|||
onOk() { |
|||
onDeleteDir(); |
|||
}, |
|||
onCancel() { |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
const refreshFileDir = () => { |
|||
dispatch(queryFileDir()); |
|||
} |
|||
|
|||
return ( |
|||
<div id="rihgt-click-menu"> |
|||
<div class="context_item"> |
|||
<div class="inner_item" onClick={showDeleteConfirm}> |
|||
<i class="glyphicon glyphicon-file"></i> 删除 |
|||
</div> |
|||
</div> |
|||
<div class="context_item"> |
|||
<div class="inner_item" onClick={refreshFileDir}> |
|||
<i class="glyphicon glyphicon-file"></i> 刷新 |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
) |
|||
} |
|||
|
|||
FunctionMenu.propTypes = {} |
|||
|
|||
export default FunctionMenu |
@ -0,0 +1,43 @@ |
|||
#rihgt-click-menu { |
|||
display: none; |
|||
font-size: 1.1em; |
|||
position: fixed; |
|||
width: 200px; |
|||
height: auto; |
|||
padding: 5px 0px; |
|||
border-radius: 5px; |
|||
top: 10; |
|||
left: 10; |
|||
background-color: #fff; |
|||
box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24); |
|||
color: #333; |
|||
z-index: 999; |
|||
} |
|||
|
|||
#rihgt-click-menu .context_item { |
|||
height: 32px; |
|||
line-height: 32px; |
|||
cursor: pointer; |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
#rihgt-click-menu .context_item:hover { |
|||
background-color: #ddd; |
|||
} |
|||
|
|||
#rihgt-click-menu .context_item .inner_item { |
|||
margin: 0px 10px; |
|||
} |
|||
|
|||
#rihgt-click-menu .context_item .inner_item i { |
|||
margin: 0 5px 0 0; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
#rihgt-click-menu .context_hr { |
|||
height: 1px; |
|||
border-top: 1px solid #bbb; |
|||
margin: 3px 10px; |
|||
} |
@ -0,0 +1,73 @@ |
|||
import React, { useState, useEffect } from 'react' |
|||
import PropTypes from 'prop-types'; |
|||
import { Modal, Input, Row, Col } from 'antd'; |
|||
const { Search } = Input; |
|||
const RoadModal = props => { |
|||
const { isVisible, onSubmit, onCancel, roads } = props; |
|||
const [roadName, setRoadName] = useState(''); |
|||
const [isRepeated, setIsRepeated] = useState(true); |
|||
|
|||
let timer = null; |
|||
useEffect(() => { |
|||
if (timer) |
|||
clearTimeout(timer) |
|||
else { |
|||
timer = setTimeout(() => { |
|||
if (roads.some(item => item.roadName == roadName)) { |
|||
setIsRepeated(true) |
|||
} else { |
|||
setIsRepeated(false); |
|||
} |
|||
}, 500); |
|||
} |
|||
|
|||
}, [roadName]); |
|||
|
|||
|
|||
useEffect(() => { |
|||
if (!isVisible) { |
|||
setRoadName('') |
|||
setIsRepeated(false) |
|||
} |
|||
return () => { |
|||
setRoadName('') |
|||
} |
|||
}, [isVisible]) |
|||
|
|||
|
|||
const onInputText = (e) => { |
|||
const value = e.target.value; |
|||
setRoadName(value); |
|||
} |
|||
|
|||
const onConfirm = () => { |
|||
if (!isRepeated) |
|||
if (roadName && roadName.trim() != '') { |
|||
onSubmit(roadName) |
|||
} |
|||
} |
|||
|
|||
|
|||
return ( |
|||
<Modal title="添加道路" visible={isVisible} onOk={onConfirm} onCancel={onCancel} > |
|||
<Row type="flex" style={{ alignContent: 'center' }}> |
|||
<Col span={6}> |
|||
<span style={{ color: 'gray', lineHeight: "32px" }}>请输入道路名称:</span> |
|||
</Col> |
|||
<Col span={18}> |
|||
<Search placeholder='请输入道路名称' onChange={onInputText} value={roadName} /> |
|||
</Col> |
|||
<Row> |
|||
{ |
|||
isRepeated ? <span style={{ color: 'red' }}>道路名称重复</span> |
|||
: '' |
|||
} |
|||
</Row> |
|||
</Row> |
|||
</Modal> |
|||
) |
|||
} |
|||
|
|||
RoadModal.propTypes = {} |
|||
|
|||
export default RoadModal |
@ -0,0 +1,46 @@ |
|||
import React, { useState, useEffect } from 'react' |
|||
import { Modal, Input, Row, Col } from 'antd'; |
|||
import Upload from '../../../../components/Upload'; |
|||
const UploadModal = props => { |
|||
const { isVisible, onSubmit, onCancel, } = props; |
|||
const [files, setFiles] = useState() |
|||
|
|||
|
|||
useEffect(() => { |
|||
if (!isVisible) { |
|||
} |
|||
return () => { |
|||
} |
|||
}, [isVisible]) |
|||
|
|||
|
|||
|
|||
const onConfirm = () => { |
|||
onSubmit(files); |
|||
} |
|||
|
|||
const onFileUploaded = (fileList) => { |
|||
console.log('fileList: ', fileList); |
|||
setFiles(fileList); |
|||
} |
|||
|
|||
return ( |
|||
<Modal title="文件上传" visible={isVisible} onOk={onConfirm} onCancel={onCancel} > |
|||
<Row type="flex" style={{ alignContent: 'center' }}> |
|||
<Upload |
|||
uploadType={'project'} |
|||
maxFilesNum={1} |
|||
maxFileSize={10} |
|||
onChange={onFileUploaded} |
|||
clearFileList={isVisible} |
|||
// value
|
|||
// onStateChange
|
|||
/> |
|||
</Row> |
|||
</Modal> |
|||
) |
|||
} |
|||
|
|||
UploadModal.propTypes = {} |
|||
|
|||
export default UploadModal |
@ -0,0 +1,442 @@ |
|||
import { connect } from 'react-redux'; |
|||
import './protable.less' |
|||
import { Card, Button, Row, DatePicker, Input, Modal, message, Image } from 'antd'; |
|||
import ProTable from '@ant-design/pro-table'; |
|||
import { getFileList, createFileDir, queryFileDir, uploadFile, deleteFile } from '../actions/file'; |
|||
import { ExclamationCircleOutlined } from '@ant-design/icons'; |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { httpDel } from '@peace/utils' |
|||
import { PinyinHelper } from '@peace/utils'; |
|||
import RoadModal from './file/roadModal'; |
|||
// import Pdfh5 from "pdfh5";
|
|||
// import "pdfh5/css/pdfh5.css";
|
|||
import FunctionMenu from './file/functionMenu'; |
|||
const { confirm } = Modal; |
|||
|
|||
// @ts-ignore
|
|||
import UploadModal from './file/uploadModal'; |
|||
|
|||
// var pdfh5 = null;
|
|||
const DetailList = (props) => { |
|||
const { fileList, loading, dispatch, handelRefresh, onPageChange } = props; |
|||
const [imgSrc, setImgSrc] = useState({ imageView: false, imgSrc: '' }) |
|||
const [pdfView, setPdfView] = useState({ showPDF: false, pdfName: '', pdfurl: '' }) |
|||
var tyApiRoot = localStorage.getItem('tyApiRoot') |
|||
|
|||
// useEffect(() => {
|
|||
// if (pdfView.showPDF) {
|
|||
// pdfh5 = new Pdfh5("#pdf-loader", {
|
|||
// pdfurl: pdfView.pdfurl
|
|||
// })
|
|||
// }
|
|||
// }, [pdfView])
|
|||
|
|||
const handleRemove = (record, filePath) => { |
|||
if (record) { |
|||
dispatch(deleteFile(record.id)).then(res => { |
|||
if (res.type == 'DELETE_FILE_SUCCESS') { |
|||
message.success("文件删除成功"); |
|||
handelRefresh() |
|||
} else { |
|||
message.error("文件删除失败") |
|||
} |
|||
}) |
|||
} |
|||
let url = `${tyApiRoot}/attachments`, msg = {}; |
|||
const actionType = "DEL_FILE_RECORD"; |
|||
if (filePath) { |
|||
httpDel(dispatch, { url, actionType, msg, query: { src: filePath } }) |
|||
} |
|||
} |
|||
const overviewPDF = (record, filePath) => { |
|||
setPdfView({ showPDF: true, pdfName: record.fileName, pdfurl: filePath }) |
|||
} |
|||
|
|||
const showDeleteConfirm = (record, filePath) => { |
|||
confirm({ |
|||
title: '是否确认删除该文件?', |
|||
icon: <ExclamationCircleOutlined />, |
|||
// content: 'Some descriptions',
|
|||
okText: '是', |
|||
okType: 'danger', |
|||
cancelText: '否', |
|||
onOk() { |
|||
handleRemove(record, filePath); |
|||
}, |
|||
onCancel() { |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
const columns = [ |
|||
{ |
|||
title: '资料名称', |
|||
key: 'fileName', |
|||
dataIndex: 'fileName', |
|||
align: 'center', |
|||
}, { |
|||
title: '所属道路', |
|||
key: 'road', |
|||
dataIndex: 'road', |
|||
align: 'center', |
|||
render: (text, record) => { |
|||
return ''; |
|||
} |
|||
}, { |
|||
title: '资料类型', |
|||
key: 'address', |
|||
dataIndex: 'address', |
|||
align: 'center', |
|||
render: (text, record) => { |
|||
return ''; |
|||
} |
|||
}, |
|||
{ |
|||
title: '文件类型', |
|||
key: 'fileExt', |
|||
dataIndex: 'fileExt', |
|||
align: 'center' |
|||
}, |
|||
{ |
|||
title: '文件大小', |
|||
width: 100, |
|||
key: 'fileSize', |
|||
dataIndex: 'fileSize', |
|||
align: 'center', |
|||
render: (text, record) => { |
|||
let size = 0; |
|||
if (record.fileSize < 1024 * 1024) { |
|||
size = (record.fileSize / 1024).toFixed(2) + 'KB' |
|||
} else { |
|||
size = (record.fileSize / 1024 / 1024).toFixed(2) + 'MB' |
|||
} |
|||
return <span>{size}</span> |
|||
} |
|||
}, { |
|||
title: '创建时间', |
|||
key: 'createDate', |
|||
dataIndex: 'createDate', |
|||
valueType: 'dateTime', |
|||
align: 'center' |
|||
}, { |
|||
title: '操作', |
|||
width: 200, |
|||
key: 'option', |
|||
valueType: 'option', |
|||
align: 'center', |
|||
render: (text, record) => { |
|||
const regEx = /\bhttps?:\/\/[^:\/]+/ig; |
|||
const path = record.fileUrl; |
|||
const filename = path.substr(path.lastIndexOf("/") + 1); |
|||
const filePath = path.replace(regEx, ""); |
|||
const filePath_ = `${tyApiRoot}/attachments?src=${filePath}&filename=${filename}`; |
|||
|
|||
return <span> |
|||
{/* {<a style={{ color: '#333398' }} href={fpath}>下载</a>} */} |
|||
{<a style={{ color: '#333398' }} onClick={() => { |
|||
window.open(filePath_); |
|||
}} >下载</a>} |
|||
<span className="ant-divider" /> |
|||
<a style={{ color: '#333398' }} onClick={() => { showDeleteConfirm(record, filePath) }}>删除</a> |
|||
{ |
|||
['.png', '.jpg'].some(item => item == record.fileExt) ? |
|||
[<span className="ant-divider" />, |
|||
<a style={{ color: '#333398' }} onClick={() => { setImgSrc({ imageView: true, imgSrc: path }) }}>预览</a>] |
|||
: '' |
|||
} |
|||
{/* { |
|||
['.pdf'].some(item => item == record.fileExt) ? |
|||
[<span className="ant-divider" />, |
|||
<a style={{ color: '#333398' }} onClick={() => overviewPDF(record, path)}>预览</a>] |
|||
: '' |
|||
} */} |
|||
</span> |
|||
}, |
|||
}, |
|||
]; |
|||
return [ |
|||
<ProTable |
|||
columns={columns} |
|||
dataSource={fileList?.list || []} |
|||
loading={loading} |
|||
pagination={{ |
|||
total: fileList?.count || 0, |
|||
pageSize: 10, |
|||
defaultPageSize: 10, |
|||
showSizeChanger: false, |
|||
onChange: (page, pageSize) => { |
|||
onPageChange(page, pageSize) |
|||
} |
|||
}} |
|||
rowKey="key" |
|||
toolBarRender={false} |
|||
search={false} |
|||
/>, |
|||
<Image |
|||
width={200} |
|||
style={{ display: 'none' }} |
|||
src={imgSrc.imgSrc} |
|||
preview={{ |
|||
visible: imgSrc.imageView, |
|||
src: imgSrc.imgSrc, |
|||
onVisibleChange: value => { |
|||
setImgSrc({ imageView: value, imgSrc: '' }); |
|||
}, |
|||
}} |
|||
/>, |
|||
// <Modal
|
|||
// visible={pdfView.showPDF}
|
|||
// footer={null}
|
|||
// title={pdfView.pdfName}
|
|||
// width={860}
|
|||
// onCancel={() => {
|
|||
// pdfh5 = null;
|
|||
// setPdfView({ showPDF: false, pdfName: '', pdfurl: '' });
|
|||
// }}
|
|||
// >
|
|||
// <div id="pdf-loader" style={{ width: 830, height: 600, overflowY: 'hidden' }} />
|
|||
// </Modal>
|
|||
|
|||
]; |
|||
}; |
|||
|
|||
|
|||
|
|||
const RoadNameList = (props) => { |
|||
const [filterRoad, setFilterRoad] = useState([]); |
|||
const [addVisible, setAddVisible] = useState(false); |
|||
const [selectRoad, setSelectRoad] = useState(); |
|||
const { onChange, roads, loading, queryData, dispatch } = props; |
|||
const columns = [ |
|||
{ |
|||
title: '道路名称', |
|||
key: 'roadName', |
|||
dataIndex: 'roadName', |
|||
align: 'center', |
|||
}, |
|||
|
|||
]; |
|||
|
|||
useEffect(() => { |
|||
if (roads && roads instanceof Array) { |
|||
setSelectRoad(roads[0].rId) |
|||
onChange(roads[0]); |
|||
} |
|||
}, [roads]) |
|||
|
|||
useEffect(() => { |
|||
if (roads) { |
|||
setFilterRoad(roads) |
|||
} |
|||
}, [roads]) |
|||
|
|||
var timer = null; |
|||
const doRoadNameSearch = (e) => { |
|||
const name = e.target.value; |
|||
if (timer) { |
|||
clearTimeout(timer) |
|||
} else { |
|||
setTimeout(() => { |
|||
let _roads = roads.filter(road => PinyinHelper.isSearchMatched(road.roadName, name)); |
|||
setFilterRoad(_roads); |
|||
}, 500); |
|||
} |
|||
} |
|||
|
|||
const createRoadDir = (roadName) => { |
|||
dispatch(createFileDir({ roadName })).then(res => { |
|||
if (res.type == 'CREATE_FILE_DIR_SUCCESS') { |
|||
setAddVisible(false) |
|||
message.success('新增道路文件夹成功'); |
|||
dispatch(queryFileDir()) |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
const onDelDir = () => { |
|||
|
|||
} |
|||
|
|||
return ( |
|||
<div id="tree-box" className='spilce'> |
|||
<ProTable |
|||
columns={columns} |
|||
dataSource={filterRoad} |
|||
loading={loading} |
|||
rowKey="name" |
|||
rowClassName={(record) => { |
|||
return record.rId == selectRoad ? 'list-row-actived' : ''; |
|||
}} |
|||
toolBarRender={() => [ |
|||
<div> |
|||
<Button onClick={() => setAddVisible(true)} type="primary" style={{ width: '100%', marginBottom: 8 }} >新增</Button> |
|||
<Input placeholder='输入道路名称' onChange={doRoadNameSearch} ></Input> |
|||
</div> |
|||
]} |
|||
options={false} |
|||
pagination={false} |
|||
search={false} |
|||
onRow={(record) => { |
|||
return { |
|||
onClick: () => { |
|||
if (record) { |
|||
setSelectRoad(record.rId); |
|||
onChange(record); |
|||
} |
|||
}, |
|||
}; |
|||
}} |
|||
/> |
|||
<RoadModal |
|||
roads={roads} |
|||
isVisible={addVisible} |
|||
onSubmit={createRoadDir} |
|||
onCancel={() => { setAddVisible(false) }} |
|||
/> |
|||
|
|||
<FunctionMenu |
|||
selectRoad={selectRoad} |
|||
dispatch={dispatch} |
|||
onDelDir={onDelDir} |
|||
/> |
|||
</div> |
|||
|
|||
); |
|||
}; |
|||
|
|||
|
|||
|
|||
const FileTable = (props) => { |
|||
const { roads, fileList, dispatch, fileListLoading, roadsLoading, user } = props; |
|||
const [record, setRecord] = useState(); |
|||
const [activeTabKey1, setActiveTabKey1] = useState('1'); |
|||
const [uploadVisible, setUploadVisible] = useState(false); |
|||
const { RangePicker } = DatePicker; |
|||
|
|||
useEffect(() => { |
|||
if (roads && roads instanceof Array) { |
|||
setRecord(roads[0]); |
|||
} |
|||
}, [roads]) |
|||
|
|||
useEffect(() => { |
|||
if (record) { |
|||
queryData(); |
|||
} |
|||
}, [record]) |
|||
|
|||
const queryData = () => { |
|||
const { rId } = record; |
|||
dispatch(getFileList({ fId: activeTabKey1, limit: 10, offset: 0, roadId: rId })) |
|||
} |
|||
|
|||
const onPageChange = (page, pageSize) => { |
|||
dispatch(getFileList({ fId: activeTabKey1, limit: pageSize, offset: (page - 1) * pageSize, roadId: rId })) |
|||
} |
|||
|
|||
useEffect(() => { |
|||
if (record && activeTabKey1) { |
|||
queryData(); |
|||
} |
|||
|
|||
}, [activeTabKey1, record]) |
|||
|
|||
const handelRefresh = () => { |
|||
queryData() |
|||
} |
|||
|
|||
const tabList = [ |
|||
{ |
|||
key: '1', |
|||
tab: '前期资料', |
|||
}, { |
|||
key: '2', |
|||
tab: '施工资料', |
|||
}, { |
|||
key: '3', |
|||
tab: '竣工资料', |
|||
}, { |
|||
key: '4', |
|||
tab: '维修资料', |
|||
}, { |
|||
key: '5', |
|||
tab: '道路资料', |
|||
}, |
|||
]; |
|||
const onTab1Change = (key) => { |
|||
setActiveTabKey1(key); |
|||
}; |
|||
|
|||
const handleChangeRecord = (newRecord) => { |
|||
let target = null; |
|||
if (!record || newRecord.rId != record.rId) { |
|||
target = newRecord; |
|||
} |
|||
setRecord(target); |
|||
} |
|||
const hanleUpload = (fileList) => { |
|||
let fileUrl, fileExt, fileName, fileSize; |
|||
if (fileList && fileList instanceof Array) { |
|||
const file = fileList[0]; |
|||
fileName = file.name; |
|||
fileExt = fileName.substr(fileName.lastIndexOf('.')); |
|||
fileUrl = file.url; |
|||
fileSize = file.size; |
|||
dispatch(uploadFile({ typeId: activeTabKey1, userId: user.id, fileSize, fileName, fileUrl, fileExt, roadId: record.rId })).then(res => { |
|||
if (res.type == 'UPLOAD_FILE_SUCCESS') { |
|||
message.success('文件新增成功'); |
|||
setUploadVisible(false); |
|||
queryData(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div className='card-protable'> |
|||
<Card > |
|||
<RoadNameList |
|||
dispatch={dispatch} |
|||
queryData={queryData} |
|||
onChange={(record) => handleChangeRecord(record)} |
|||
record={record} |
|||
roads={roads} |
|||
loading={roadsLoading} /> |
|||
</Card> |
|||
<Card |
|||
style={{ flex: 1 }} |
|||
tabList={tabList} |
|||
activeTabKey={activeTabKey1} |
|||
onTabChange={(key) => { |
|||
onTab1Change(key); |
|||
}} |
|||
> |
|||
<Row> |
|||
<Button onClick={() => { setUploadVisible(true) }} type="primary" style={{ width: 160, marginBottom: 8 }} >上传</Button> |
|||
</Row> |
|||
<Card style={{ flex: 1 }}> |
|||
<DetailList fileList={fileList} record={record} loading={fileListLoading} dispatch={dispatch} handelRefresh={handelRefresh} onPageChange={onPageChange} /> |
|||
</Card> |
|||
</Card> |
|||
<UploadModal |
|||
isVisible={uploadVisible} |
|||
onCancel={() => { setUploadVisible(false) }} |
|||
onSubmit={hanleUpload} |
|||
/> |
|||
</div> |
|||
|
|||
); |
|||
}; |
|||
|
|||
function mapStateToProps(state) { |
|||
const { fileDirs, fileList, auth } = state; |
|||
return { |
|||
roads: fileDirs.data, |
|||
roadsLoading: fileDirs.isRequesting, |
|||
fileList: fileList.data, |
|||
fileListLoading: fileList.isRequesting, |
|||
user: auth.user, |
|||
}; |
|||
} |
|||
export default connect(mapStateToProps)(FileTable); |
File diff suppressed because it is too large
@ -1,21 +1,30 @@ |
|||
.protable-transpor{ |
|||
.ant-table-cell-fix-left{ |
|||
.protable-transpor { |
|||
.ant-table-cell-fix-left { |
|||
background-color: #ffffff !important; |
|||
} |
|||
.ant-table-cell-fix-right{ |
|||
|
|||
.ant-table-cell-fix-right { |
|||
background-color: #ffffff !important; |
|||
} |
|||
|
|||
|
|||
} |
|||
.spilce{ |
|||
.split-row-select-active { |
|||
background-color: #e6f7ff; |
|||
} |
|||
th { |
|||
display: none; |
|||
} |
|||
|
|||
.list-row-actived { |
|||
background-color: #7cafc6; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
|
|||
.ant-divider { |
|||
width: 0px; |
|||
height: 8px; |
|||
border-left: 1px solid gray; |
|||
margin: 0px 8px; |
|||
opacity: 0.8; |
|||
} |
|||
.card-protable{display: flex; |
|||
flex-direction:row; |
|||
|
|||
.card-protable { |
|||
display: flex; |
|||
flex-direction: row; |
|||
width: 100%; |
|||
} |
@ -0,0 +1,40 @@ |
|||
import React, { useEffect, useState } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import '../style.less'; |
|||
import { queryFileDir } from '../actions/file'; |
|||
import FileTable from '../components/fileTable'; |
|||
const superagent = require('superagent'); |
|||
const patrol = (props) => { |
|||
const { dispatch, user } = props |
|||
|
|||
useEffect(() => { |
|||
dispatch(queryFileDir()) |
|||
}, [true]) |
|||
//批量导出
|
|||
const exports = (ids, counts) => { |
|||
let reportIds = []; |
|||
if (ids.length) |
|||
reportIds = ids |
|||
else |
|||
reportIds = (counts || {}).ids || []; |
|||
superagent.post('/_report/http') |
|||
.send({ id: reportIds.map(i => Number(i)) }).end((err, res) => { |
|||
const resTextIs = res.text.split('/').pop() |
|||
window.open( |
|||
'/_api/' + |
|||
`attachments?src=files/${resTextIs}&filename=${encodeURIComponent(resTextIs)}&token=${user.token}`) |
|||
}) |
|||
} |
|||
|
|||
return ( |
|||
<> <FileTable exports={exports} /> |
|||
</> |
|||
) |
|||
} |
|||
function mapStateToProps(state) { |
|||
const { auth } = state |
|||
return { |
|||
user: auth.user, |
|||
} |
|||
} |
|||
export default connect(mapStateToProps)(patrol); |
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Loading…
Reference in new issue