wenlele
2 years ago
20 changed files with 1553 additions and 17 deletions
@ -0,0 +1,155 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
async function addEditFile (ctx, next) { |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const data = ctx.request.body; |
||||
|
const { higherFileId, fileName } = data |
||||
|
|
||||
|
|
||||
|
let onefind = await models.ProjectFolder.findOne({ |
||||
|
where: { |
||||
|
higherFileId: higherFileId || null, |
||||
|
fileName |
||||
|
} |
||||
|
}) |
||||
|
if (onefind) { |
||||
|
throw '文件夹名称重复' |
||||
|
} |
||||
|
|
||||
|
|
||||
|
if (data && data.id) { |
||||
|
await models.ProjectFolder.update(data, { |
||||
|
where: { |
||||
|
id: data.id |
||||
|
} |
||||
|
}) |
||||
|
} else { |
||||
|
await models.ProjectFolder.create(data) |
||||
|
} |
||||
|
|
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
"message": '添加文件夹失败' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function fileList (ctx, next) { |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const { projectId, limit, page } = ctx.query; |
||||
|
|
||||
|
let options = { where: {}, } |
||||
|
if (projectId) { |
||||
|
options.where.projectId = projectId |
||||
|
} |
||||
|
|
||||
|
let res = await models.ProjectFolder.findAll(options) |
||||
|
|
||||
|
ctx.status = 200; |
||||
|
ctx.body = res || [] |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
"message": '添加文件夹失败' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function delFile (ctx, next) { |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const { id } = ctx.params; |
||||
|
|
||||
|
|
||||
|
await models.ProjectFolder.destroy({ where: { id } }) |
||||
|
|
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
"message": '删除文件夹失败' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function addFile (ctx, next) { |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const data = ctx.request.body; |
||||
|
|
||||
|
await models.ProjectFolderFile.create(data) |
||||
|
|
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
"message": '添加文件夹失败' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function folderFileList (ctx, next) { |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const { fileId, limit, page } = ctx.query; |
||||
|
|
||||
|
let options = { where: {}, } |
||||
|
if (JSON.parse(fileId).length) { |
||||
|
options.where.fileId = { $in: JSON.parse(fileId) } |
||||
|
} |
||||
|
|
||||
|
if (limit) { |
||||
|
options.limit = Number(limit) |
||||
|
} |
||||
|
if (page && limit) { |
||||
|
options.offset = Number(page) * Number(limit) |
||||
|
} |
||||
|
|
||||
|
let res = await models.ProjectFolderFile.findAndCountAll(options) |
||||
|
|
||||
|
ctx.status = 200 |
||||
|
ctx.body = res || {} |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
"message": '添加文件列表失败' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
async function delfolderFile (ctx, next) { |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const { id } = ctx.params; |
||||
|
|
||||
|
await models.ProjectFolderFile.destroy({ where: { id } }) |
||||
|
|
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`) |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
"message": '删除文件失败' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
module.exports = { |
||||
|
addEditFile, |
||||
|
fileList, |
||||
|
delFile, |
||||
|
addFile, |
||||
|
folderFileList, |
||||
|
delfolderFile |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
/* eslint-disable*/ |
||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = dc => { |
||||
|
const DataTypes = dc.ORM; |
||||
|
const sequelize = dc.orm; |
||||
|
const ProjectFolder = sequelize.define("projectFolder", { |
||||
|
id: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: true, |
||||
|
field: "id", |
||||
|
autoIncrement: true, |
||||
|
unique: "project_folder_id_uindex" |
||||
|
}, |
||||
|
projectId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "project_id", |
||||
|
autoIncrement: false, |
||||
|
}, |
||||
|
higherFileId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "higher_file_id", |
||||
|
autoIncrement: false, |
||||
|
}, |
||||
|
type: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "type", |
||||
|
autoIncrement: false, |
||||
|
}, |
||||
|
fileName: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "file_name", |
||||
|
autoIncrement: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: "project_folder", |
||||
|
comment: "", |
||||
|
indexes: [] |
||||
|
}); |
||||
|
dc.models.ProjectFolder = ProjectFolder; |
||||
|
return ProjectFolder; |
||||
|
}; |
@ -0,0 +1,70 @@ |
|||||
|
/* eslint-disable*/ |
||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = dc => { |
||||
|
const DataTypes = dc.ORM; |
||||
|
const sequelize = dc.orm; |
||||
|
const ProjectFolderFile = sequelize.define("projectFolderFile", { |
||||
|
id: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: true, |
||||
|
field: "id", |
||||
|
autoIncrement: true, |
||||
|
unique: "project_folder_file_id_uindex" |
||||
|
}, |
||||
|
fileId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "file_id", |
||||
|
autoIncrement: false, |
||||
|
}, |
||||
|
size: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "size", |
||||
|
autoIncrement: false, |
||||
|
}, |
||||
|
uploadTime: { |
||||
|
type:DataTypes.DATE, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "upload_time", |
||||
|
autoIncrement: false, |
||||
|
}, |
||||
|
name: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "name", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
url: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "url", |
||||
|
autoIncrement: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: "project_folder_file", |
||||
|
comment: "", |
||||
|
indexes: [] |
||||
|
}); |
||||
|
dc.models.ProjectFolderFile = ProjectFolderFile; |
||||
|
return ProjectFolderFile; |
||||
|
}; |
@ -0,0 +1,24 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const means = require('../../controllers/means/index'); |
||||
|
|
||||
|
module.exports = function (app, router, opts) { |
||||
|
|
||||
|
app.fs.api.logAttr['GET/file/list'] = { content: '获取文件夹列表', visible: true }; |
||||
|
router.get('/file/list', means.fileList); |
||||
|
|
||||
|
app.fs.api.logAttr['POST/file/addEdit'] = { content: '添加/编辑文件夹', visible: true }; |
||||
|
router.post('/file/addEdit', means.addEditFile); |
||||
|
|
||||
|
app.fs.api.logAttr['DEL/file/del/:id'] = { content: '删除文件夹', visible: true }; |
||||
|
router.del('/file/del/:id', means.delFile); |
||||
|
|
||||
|
app.fs.api.logAttr['POST/file'] = { content: '添加文件', visible: true }; |
||||
|
router.post('/file', means.addFile); |
||||
|
|
||||
|
app.fs.api.logAttr['GET/file'] = { content: '获取文件列表', visible: true }; |
||||
|
router.get('/file', means.folderFileList); |
||||
|
|
||||
|
app.fs.api.logAttr['DEL/file/:id'] = { content: '删除文件夹', visible: true }; |
||||
|
router.del('/file/:id', means.delfolderFile); |
||||
|
}; |
@ -0,0 +1,39 @@ |
|||||
|
create table project_folder |
||||
|
( |
||||
|
id serial not null, |
||||
|
file_name varchar not null, |
||||
|
project_id integer not null, |
||||
|
higher_file_id integer, |
||||
|
type int not null |
||||
|
); |
||||
|
|
||||
|
comment on column project_folder.project_id is '自定义项目或者项企项目'; |
||||
|
|
||||
|
comment on column project_folder.higher_file_id is '上级文件id'; |
||||
|
|
||||
|
comment on column project_folder.type is '1.项目资料 |
||||
|
2.维修FQA |
||||
|
3.故障资料 |
||||
|
4.运维规范'; |
||||
|
|
||||
|
create unique index project_folder_id_uindex |
||||
|
on project_folder (id); |
||||
|
|
||||
|
alter table project_folder |
||||
|
add constraint project_folder_pk |
||||
|
primary key (id); |
||||
|
|
||||
|
|
||||
|
|
||||
|
create table project_folder_file |
||||
|
( |
||||
|
id serial not null, |
||||
|
name varchar, |
||||
|
size integer, |
||||
|
upload_time timestamp, |
||||
|
file_id integer, |
||||
|
url varchar |
||||
|
); |
||||
|
|
||||
|
create unique index project_folder_file_id_uindex |
||||
|
on project_folder_file (id); |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,517 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import React, { Component } from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
|
import { Spin, Upload, message, Modal, Card, Button } from '@douyinfe/semi-ui'; |
||||
|
import moment from 'moment'; |
||||
|
import { IconPlus, IconCloudUploadStroked, IconCrossStroked } from '@douyinfe/semi-icons'; |
||||
|
import OSS from 'ali-oss'; |
||||
|
import { RouteRequest } from '@peace/utils'; |
||||
|
import { RouteTable } from '$utils' |
||||
|
import { v4 as uuidv4 } from 'uuid'; |
||||
|
import request from 'superagent'; |
||||
|
|
||||
|
class Uploads extends Component { |
||||
|
constructor(props) { |
||||
|
super(props); |
||||
|
this.ApiRoot = localStorage.getItem('tyApiRoot') |
||||
|
this.qnDomain = localStorage.getItem('qnDomain'); |
||||
|
this.aliAdmin = localStorage.getItem('aliAdmin'); |
||||
|
this.aliBucket = localStorage.getItem('aliBucket'); |
||||
|
this.aliRegion = localStorage.getItem('aliRegion'); |
||||
|
this.state = { |
||||
|
fileUploading: false, |
||||
|
fileList: [], |
||||
|
curPreviewPic: '', |
||||
|
curPreviewVideo: '', |
||||
|
delPicIng: false, |
||||
|
removeFilesList: [], |
||||
|
stsRes: {} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
dealName = (uploaded) => { |
||||
|
let realName = uploaded && uploaded.split('/')[2] |
||||
|
// let x1 = realName.split('.')
|
||||
|
// let postfix = x1.pop()
|
||||
|
// let allName = x1.join('.')
|
||||
|
// let x2 = allName.split('_')
|
||||
|
// let showName = `${x2[0]}.${postfix}`
|
||||
|
return realName |
||||
|
} |
||||
|
|
||||
|
// 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
|
||||
|
// });
|
||||
|
// };
|
||||
|
|
||||
|
setFileList = (nextEditData, isQiniu, isAli) => { |
||||
|
let defaultFileList = []; |
||||
|
if (nextEditData.length) { |
||||
|
defaultFileList = nextEditData.map((u, index) => { |
||||
|
let fileUrl = |
||||
|
isQiniu ? `/_file-server/${u.storageUrl}` |
||||
|
: isAli ? `/_file-ali-server/${u.storageUrl}` |
||||
|
: `${this.ApiRoot}/${u.storageUrl}`; |
||||
|
|
||||
|
return { |
||||
|
uid: -index - 1, |
||||
|
name: this.dealName(u.storageUrl), |
||||
|
status: 'done', |
||||
|
storageUrl: u.storageUrl, |
||||
|
url: fileUrl, |
||||
|
size: u.size || -1 |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
this.setState({ |
||||
|
fileList: defaultFileList |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
componentWillMount () { |
||||
|
this.setState({ |
||||
|
delPicIng: true |
||||
|
}) |
||||
|
RouteRequest.get(RouteTable.getAliSts).then(async (result) => { |
||||
|
this.setState({ |
||||
|
delPicIng: false, |
||||
|
stsRes: result |
||||
|
}) |
||||
|
}, (err) => { |
||||
|
this.setState({ |
||||
|
delPicIng: false |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
componentDidMount () { |
||||
|
const { value, defaultValue, isQiniu, isAli } = this.props; |
||||
|
if (defaultValue) { |
||||
|
this.setFileList(defaultValue, isQiniu, isAli) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
UNSAFE_componentWillReceiveProps (np) { |
||||
|
const { dispatch, value: thisEditData, onChange } = this.props; |
||||
|
const { value: nextEditData, isQiniu, isAli } = np; |
||||
|
// this.setFileList(nextEditData, isQiniu)
|
||||
|
// const setFileList = () => {
|
||||
|
// let defaultFileList = [];
|
||||
|
// defaultFileList = nextEditData.map((u, index) => {
|
||||
|
// let fileUrl = isQiniu ? `/_file-server/${u.storageUrl}` : `${this.ApiRoot}/${u.storageUrl}`;
|
||||
|
// return {
|
||||
|
// uid: -index - 1,
|
||||
|
// name: this.dealName(u.storageUrl),
|
||||
|
// status: 'done',
|
||||
|
// storageUrl: u.storageUrl,
|
||||
|
// url: fileUrl,
|
||||
|
// size: u.size || -1
|
||||
|
// };
|
||||
|
// });
|
||||
|
// this.setState({
|
||||
|
// fileList: defaultFileList
|
||||
|
// });
|
||||
|
// };
|
||||
|
if (nextEditData && nextEditData.length) { |
||||
|
if (!thisEditData || !this.state.fileList.length) { |
||||
|
this.setFileList(nextEditData, isQiniu, isAli); |
||||
|
} else if (nextEditData.length != thisEditData.length) { |
||||
|
this.setFileList(nextEditData, isQiniu, isAli); |
||||
|
} else { |
||||
|
let repeat = true; |
||||
|
for (let i = 0; i < thisEditData.length; i++) { |
||||
|
if (thisEditData[i] != nextEditData[i]) { |
||||
|
repeat = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (!repeat) { |
||||
|
this.setFileList(nextEditData, isQiniu, isAli); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// else{
|
||||
|
// this.setState({
|
||||
|
// fileList:[],
|
||||
|
// })
|
||||
|
// }
|
||||
|
} |
||||
|
|
||||
|
render () { |
||||
|
const UploadPath = { |
||||
|
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf', 'pptx', 'png', 'jpg', 'svg', 'jpeg', 'rar', 'zip', 'jpeg', 'mp4'], |
||||
|
report: ['doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf'], |
||||
|
data: ['txt', 'xls', 'xlsx', 'csv'], |
||||
|
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, |
||||
|
isQiniu, |
||||
|
isAli, |
||||
|
} = this.props; |
||||
|
const { fileList, curPreviewPic, curPreviewVideo, delPicIng, removeFilesList, stsRes } = this.state; |
||||
|
const that = this; |
||||
|
let uploadType_ = uploadType || 'project'; |
||||
|
let maxFilesNum_ = maxFilesNum || 1; |
||||
|
let defaultFileTypes = fileTypes || UploadPath[uploadType_]; |
||||
|
// debugger
|
||||
|
const uploadProps = { |
||||
|
name: 'checkFile_', |
||||
|
multiple: false, |
||||
|
showUploadList: showUploadList || true, |
||||
|
action: |
||||
|
isQiniu ? `/_upload/attachments/${uploadType_}` |
||||
|
: isAli ? `/_upload/attachments/ali/${uploadType_}` |
||||
|
: `${this.ApiRoot}/attachments/${uploadType_}`, |
||||
|
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 }); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
customRequest: isQiniu ? undefined : async (params) => { |
||||
|
try { |
||||
|
const client = new OSS({ |
||||
|
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
|
||||
|
region: that.aliRegion, |
||||
|
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
|
||||
|
accessKeyId: stsRes.AccessKeyId, |
||||
|
accessKeySecret: stsRes.AccessKeySecret, |
||||
|
// 从STS服务获取的安全令牌(SecurityToken)。
|
||||
|
stsToken: stsRes.SecurityToken, |
||||
|
// 填写Bucket名称,例如examplebucket。
|
||||
|
bucket: that.aliBucket, |
||||
|
}); |
||||
|
|
||||
|
let uploadRes = null |
||||
|
let uploadPath = `/${uploadType_}/${uuidv4()}/` + params.file.name |
||||
|
if ( |
||||
|
// false &&
|
||||
|
params.file.size < 1024 * 1024 * 1 |
||||
|
) { |
||||
|
params.onProgress({ percent: 40 }) |
||||
|
uploadRes = await client.put( |
||||
|
uploadPath, |
||||
|
params.file |
||||
|
); |
||||
|
} else { |
||||
|
uploadRes = await client.multipartUpload(uploadPath, params.file, { |
||||
|
progress: (p, _checkpoint) => { |
||||
|
// Object的上传进度。
|
||||
|
// console.log(p);
|
||||
|
// 分片上传的断点信息。
|
||||
|
// console.log(_checkpoint);
|
||||
|
params.onProgress({ percent: p * 100 }) |
||||
|
}, |
||||
|
// 设置并发上传的分片数量。
|
||||
|
parallel: 4, |
||||
|
// 设置分片大小。默认值为1 MB,最小值为100 KB。
|
||||
|
partSize: 1024 * 1024 * 3, // 3m
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// console.log(uploadRes);
|
||||
|
let { name: url, res } = uploadRes; |
||||
|
let size = params.file.size; |
||||
|
let nextFileList = fileList; |
||||
|
let url_ = url.startsWith('/') ? url.substring(1) : url |
||||
|
nextFileList[nextFileList.length - 1] = { |
||||
|
uid: -moment().unix(), |
||||
|
name: params.file.name, |
||||
|
status: 'done', |
||||
|
storageUrl: url_, |
||||
|
url: `/_file-ali-server/${url_}`, |
||||
|
size: size |
||||
|
}; |
||||
|
onChange(nextFileList); |
||||
|
that.setState({ |
||||
|
fileUploading: false, |
||||
|
fileList: nextFileList |
||||
|
}); |
||||
|
if (onStateChange) { |
||||
|
onStateChange({ uploading: false }); |
||||
|
} |
||||
|
params.onSuccess({ |
||||
|
result: { |
||||
|
uploaded: url, |
||||
|
url: res.requestUrls[0] |
||||
|
}, |
||||
|
}) |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
params.onError({}) |
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
onChange (info) { |
||||
|
console.log(111,info); |
||||
|
const status = info.file.status; |
||||
|
if (status === 'uploading') { |
||||
|
that.setState({ |
||||
|
fileList: info.fileList |
||||
|
}); |
||||
|
} |
||||
|
if (status === 'done') { |
||||
|
let { uploaded, url } = info.file.response; |
||||
|
let size = info.file.size; |
||||
|
let nextFileList = fileList; |
||||
|
nextFileList[nextFileList.length - 1] = { |
||||
|
uid: -moment().unix(), |
||||
|
name: that.dealName(uploaded), |
||||
|
status: 'done', |
||||
|
storageUrl: uploaded, |
||||
|
url: |
||||
|
isQiniu ? '/_file-server/' + uploaded : |
||||
|
isAli ? `/_file-ali-server/${uploaded}` : |
||||
|
url, |
||||
|
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); |
||||
|
} |
||||
|
}); |
||||
|
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]); |
||||
|
if (curPreviewPic == file.url) { |
||||
|
that.setState({ |
||||
|
curPreviewPic: '' |
||||
|
}); |
||||
|
} |
||||
|
if (curPreviewVideo == file.url) { |
||||
|
that.setState({ |
||||
|
curPreviewVideo: '' |
||||
|
}); |
||||
|
} |
||||
|
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 if ( |
||||
|
UploadPath.video.some((img) => img == filePostfix) |
||||
|
&& isAli |
||||
|
) { |
||||
|
that.setState({ |
||||
|
curPreviewVideo: that.aliAdmin + '/' + file.storageUrl |
||||
|
}); |
||||
|
} else { |
||||
|
//message.warn('仅支持图片预览');
|
||||
|
preview(file.storageUrl) |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const preview = (url) => { |
||||
|
let link = isQiniu ? encodeURI(`${this.qnDomain}/${url}`) : |
||||
|
isAli ? encodeURI(`${this.aliAdmin}/${url}`) : '' |
||||
|
if (link) |
||||
|
if (url.indexOf("pdf") !== -1 || url.indexOf("csv") !== -1) { |
||||
|
window.open(link) |
||||
|
} else { |
||||
|
window.open(`https://view.officeapps.live.com/op/view.aspx?src=${link}`) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
let fileList_ = fileList |
||||
|
// .map(f => {
|
||||
|
// if (f.storageUrl) {
|
||||
|
// let realName = f.storageUrl.split('/').pop()
|
||||
|
// if (f.name != realName) {
|
||||
|
// f.name = realName
|
||||
|
// }
|
||||
|
// }
|
||||
|
// return f
|
||||
|
// })
|
||||
|
//下载文件
|
||||
|
const handleDownload = (file) => { |
||||
|
saveAs(file) |
||||
|
}; |
||||
|
const saveAs = (file) => { |
||||
|
let url = null |
||||
|
if (file.storageUrl.endsWith('mp4')) { |
||||
|
const client = new OSS({ |
||||
|
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
|
||||
|
region: that.aliRegion, |
||||
|
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
|
||||
|
accessKeyId: stsRes.AccessKeyId, |
||||
|
accessKeySecret: stsRes.AccessKeySecret, |
||||
|
// 从STS服务获取的安全令牌(SecurityToken)。
|
||||
|
stsToken: stsRes.SecurityToken, |
||||
|
// 填写Bucket名称,例如examplebucket。
|
||||
|
bucket: that.aliBucket, |
||||
|
}); |
||||
|
// 配置响应头实现通过URL访问时自动下载文件,并设置下载后的文件名。
|
||||
|
const response = { |
||||
|
'content-disposition': `attachment; filename=${encodeURIComponent(file.name)}` |
||||
|
} |
||||
|
// 填写Object完整路径。Object完整路径中不能包含Bucket名称。
|
||||
|
url = client.signatureUrl(file.storageUrl, { response }); |
||||
|
} |
||||
|
|
||||
|
const link = document.createElement('a'); |
||||
|
link.href = url || file.url; |
||||
|
link.download = file.name; |
||||
|
link.style.display = 'none'; |
||||
|
link.click(); |
||||
|
} |
||||
|
//自定义下载
|
||||
|
return ( |
||||
|
<div> |
||||
|
<Spin spinning={delPicIng}> |
||||
|
<Upload {...uploadProps} fileList={fileList_} showUploadList={{ showDownloadIcon: true, showRemoveIcon: true, }} onDownload={handleDownload} |
||||
|
> |
||||
|
{ |
||||
|
disabled ? ( |
||||
|
'' |
||||
|
) : |
||||
|
listType == 'picture-card' ? |
||||
|
( |
||||
|
fileList.length >= maxFilesNum_ ? null : ( |
||||
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 85, height: 30, border: '1px solid #a29696' }}> |
||||
|
<IconPlus /> |
||||
|
<div>添加附件</div> |
||||
|
</div> |
||||
|
) |
||||
|
) : ( |
||||
|
<Button disabled={fileList.length >= maxFilesNum_} icon={<IconCloudUploadStroked />}> 文件上传 </Button> |
||||
|
) |
||||
|
} |
||||
|
</Upload> |
||||
|
{ |
||||
|
curPreviewPic ? ( |
||||
|
<Card bodyStyle={{ padding: 8 }}> |
||||
|
<div style={{ marginBottom: 8 }} > |
||||
|
<span>图片预览</span> |
||||
|
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewPic: '' }) }}> |
||||
|
<IconCrossStroked style={{ fontSize: 20 }} /> |
||||
|
</span> |
||||
|
</div> |
||||
|
<img style={{ width: '100%' }} src={curPreviewPic} /> |
||||
|
</Card> |
||||
|
) : '' |
||||
|
} |
||||
|
{ |
||||
|
curPreviewVideo ? (<Card bodyStyle={{ padding: 8 }}> |
||||
|
<div style={{ marginBottom: 8 }} > |
||||
|
<span>视频预览</span> |
||||
|
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewVideo: '' }) }}> |
||||
|
<IconCrossStroked style={{ fontSize: 20 }} /> |
||||
|
</span> |
||||
|
</div> |
||||
|
<video controls style={{ width: '100%' }}> |
||||
|
<source src={curPreviewVideo} type="video/mp4"></source> |
||||
|
</video> |
||||
|
</Card>) : '' |
||||
|
} |
||||
|
</Spin> |
||||
|
</div > |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function mapStateToProps (state) { |
||||
|
const { auth } = state |
||||
|
return { |
||||
|
user: auth.user |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(Uploads); |
@ -1,2 +1,8 @@ |
|||||
'use strict'; |
'use strict'; |
||||
|
|
||||
|
import * as means from './means' |
||||
|
|
||||
|
export default { |
||||
|
...means, |
||||
|
|
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
import { ApiTable, basicAction } from '$utils' |
||||
|
|
||||
|
//添加、编辑文件夹
|
||||
|
export function addEditFile (data = {}) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "post", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "POST_ADDEDITFILE", |
||||
|
data, |
||||
|
url: `${ApiTable.addEditFile}`, |
||||
|
msg: { option: data.id ? '文件夹编辑' : '添加文件夹' }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
//获取文件夹列表
|
||||
|
export function fileList (query = {}) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_FILELIST", |
||||
|
query, |
||||
|
url: `${ApiTable.fileList}`, |
||||
|
msg: { option: '获取文件夹列表' }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//删除文件夹
|
||||
|
export function delFile (id) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "del", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "DEL_DELFILE", |
||||
|
url: `${ApiTable.delFile.replace('{id}', id)}`, |
||||
|
msg: { option: '删除文件夹' }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
//添加文件
|
||||
|
export function addFile (data = {}) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "post", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "POST_FILE", |
||||
|
data, |
||||
|
url: `${ApiTable.file}`, |
||||
|
msg: { option: '添加文件' }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//获取文件列表
|
||||
|
export function folderFileList (query = {}) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "get", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "GET_FOLDERFILELIST", |
||||
|
query, |
||||
|
url: `${ApiTable.file}`, |
||||
|
msg: { option: '获取文件列表' }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
//删除文件
|
||||
|
export function delfolderFile (id) { |
||||
|
return (dispatch) => basicAction({ |
||||
|
type: "del", |
||||
|
dispatch: dispatch, |
||||
|
actionType: "DEL_DELFILE", |
||||
|
url: `${ApiTable.delfolderFile.replace('{id}', id)}`, |
||||
|
msg: { option: '删除文件' }, |
||||
|
}); |
||||
|
} |
@ -0,0 +1,86 @@ |
|||||
|
import React, { useState, useRef, useEffect } from "react"; |
||||
|
import { connect } from "react-redux"; |
||||
|
import { Modal, Form } from "@douyinfe/semi-ui"; |
||||
|
import { IconAlertCircle } from '@douyinfe/semi-icons'; |
||||
|
|
||||
|
|
||||
|
function FileModal (props) { |
||||
|
const { close, success, dispatch, actions, editData, pepProjectId, higherFile } = props; |
||||
|
const { means } = actions; |
||||
|
const form = useRef();//表单 |
||||
|
|
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Modal |
||||
|
title={editData?.id ? '编辑文件夹' : '新建文件夹'} |
||||
|
okText="确定" |
||||
|
cancelText="取消" |
||||
|
visible={true} |
||||
|
onOk={() => { |
||||
|
form.current.validate().then((v) => { |
||||
|
dispatch(means.addEditFile({ |
||||
|
id: editData?.id, |
||||
|
projectId: pepProjectId, |
||||
|
fileName: v.fileName, |
||||
|
higherFileId: editData?.higherFileId || v.higherFileId || null, |
||||
|
type: 1 |
||||
|
})).then(v => { |
||||
|
if (v.success) { |
||||
|
close() |
||||
|
success() |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
}} |
||||
|
width={607} |
||||
|
onCancel={() => { |
||||
|
close() |
||||
|
}} |
||||
|
> |
||||
|
<div style={{ margin: "0px 25px" }}> |
||||
|
<Form |
||||
|
allowEmpty |
||||
|
labelPosition="left" |
||||
|
labelAlign="right" |
||||
|
labelWidth="100px" |
||||
|
getFormApi={(formApi) => (form.current = formApi)} |
||||
|
> |
||||
|
|
||||
|
<Form.Input |
||||
|
field='fileName' |
||||
|
label='文件夹名称' |
||||
|
rules={[{ required: true, message: "请输入文件夹名称" }]} |
||||
|
initValue={editData?.fileName} |
||||
|
/> |
||||
|
|
||||
|
{editData?.id ? "" : <Form.Select |
||||
|
field="higherFileId" |
||||
|
label='上级文件夹' |
||||
|
style={{ width: '100%' }} |
||||
|
> |
||||
|
{higherFile?.map((item, index) => ( |
||||
|
<Form.Select.Option key={index} value={item.value}> |
||||
|
{item.name} |
||||
|
</Form.Select.Option> |
||||
|
))} |
||||
|
</Form.Select>} |
||||
|
|
||||
|
|
||||
|
</Form> |
||||
|
</div> |
||||
|
</Modal> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
function mapStateToProps (state) { |
||||
|
const { auth, global, members } = state; |
||||
|
return { |
||||
|
// loading: members.isRequesting, |
||||
|
user: auth.user, |
||||
|
actions: global.actions, |
||||
|
// members: members.data, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export default connect(mapStateToProps)(FileModal); |
@ -0,0 +1,9 @@ |
|||||
|
|
||||
|
.tip{ |
||||
|
display: none; |
||||
|
} |
||||
|
.dd:hover{ |
||||
|
.tip{ |
||||
|
display: inline-block; |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue