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{ |
.protable-transpor { |
||||
.ant-table-cell-fix-left{ |
.ant-table-cell-fix-left { |
||||
background-color: #ffffff !important; |
background-color: #ffffff !important; |
||||
} |
} |
||||
.ant-table-cell-fix-right{ |
|
||||
|
.ant-table-cell-fix-right { |
||||
background-color: #ffffff !important; |
background-color: #ffffff !important; |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
.spilce{ |
|
||||
.split-row-select-active { |
.list-row-actived { |
||||
background-color: #e6f7ff; |
background-color: #7cafc6; |
||||
} |
font-weight: 600; |
||||
th { |
} |
||||
display: none; |
|
||||
} |
|
||||
|
.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%; |
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