diff --git a/api/app/lib/controllers/latestMetadata/index.js b/api/app/lib/controllers/latestMetadata/index.js
index eb7ade1..850d4f1 100644
--- a/api/app/lib/controllers/latestMetadata/index.js
+++ b/api/app/lib/controllers/latestMetadata/index.js
@@ -31,9 +31,14 @@ async function postResourceCatalog(ctx) {
ctx.status = 400;
ctx.body = { message: '该资源目录名称或代码已存在' }
} else {
- await models.ResourceCatalog.create(ctx.request.body);
- ctx.body = { message: '新建资源目录成功' }
- ctx.status = 200;
+ if (!name || !code) {
+ ctx.body = { message: '参数不全,请重新配置' }
+ ctx.status = 400;
+ } else {
+ await models.ResourceCatalog.create(ctx.request.body);
+ ctx.body = { message: '新建资源目录成功' }
+ ctx.status = 200;
+ }
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
@@ -162,17 +167,17 @@ async function getMetadataDatabases(ctx) {
async function getMetadataFiles(ctx) {
try {
const models = ctx.fs.dc.models;
- const { catalog, limit, offset, keywords, orderBy = 'createAt', orderDirection = 'desc' } = ctx.query;
+ const { catalog, limit, offset, keywords, orderBy = 'updateAt', orderDirection = 'desc' } = ctx.query;
const where = { catalog: catalog };
if (keywords) {
- where['$or'] = [{ name: { $iLike: `%${keywords}%` } }, { type: { $iLike: `%${keywords}%` } }]
+ where['$or'] = [{ name: { $iLike: `%${keywords}%` } }, { type: { $iLike: `%${keywords}%` }, fileName: { $not: null } }]
}
const findObj = {
include: [
- {
- model: models.User,
- attributes: ['id', 'name', 'username'],
- },
+ // {
+ // model: models.User,
+ // attributes: ['id', 'name', 'username'],
+ // },
{
model: models.TagFile,
include: [{
@@ -243,11 +248,14 @@ async function getMetadataModels(ctx) {
try {
const models = ctx.fs.dc.models;
const { modelTypes } = ctx.query;
- const rslt = await models.MetaModel.findAll({
- where: {
- modelType: { $in: modelTypes }
- }
- });
+ let rslt = [];
+ if (modelTypes) {
+ rslt = await models.MetaModel.findAll({
+ where: {
+ modelType: { $in: modelTypes.split(',') }
+ }
+ });
+ }
ctx.status = 200;
ctx.body = rslt;
} catch (error) {
@@ -260,7 +268,7 @@ async function getMetadataModels(ctx) {
}
//新建库表元数据
-async function postMeatadataDatabases(ctx) {
+async function postMetadataDatabases(ctx) {
try {
const { name, code, catalog } = ctx.request.body;
const models = ctx.fs.dc.models;
@@ -271,9 +279,14 @@ async function postMeatadataDatabases(ctx) {
ctx.status = 400;
ctx.body = { message: '该资源目录下元数据名称或代码已存在' }
} else {
- await models.MetadataDatabase.create({ createAt: moment(), ...ctx.request.body });
- ctx.body = { message: '新建元数据成功' }
- ctx.status = 200;
+ if (!name || !code || !catalog) {
+ ctx.body = { message: '参数不全,请重新配置' }
+ ctx.status = 400;
+ } else {
+ await models.MetadataDatabase.create({ createAt: moment(), ...ctx.request.body });
+ ctx.body = { message: '新建元数据成功' }
+ ctx.status = 200;
+ }
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
@@ -285,7 +298,7 @@ async function postMeatadataDatabases(ctx) {
}
//修改库表元数据
-async function putMeatadataDatabases(ctx) {
+async function putMetadataDatabases(ctx) {
try {
const { id } = ctx.params;
const { catalog, name, code } = ctx.request.body;
@@ -297,7 +310,7 @@ async function putMeatadataDatabases(ctx) {
ctx.status = 400;
ctx.body = { message: '该元数据名称或代码已存在' }
} else {
- await models.MetadataDatabase.update(ctx.request.body, { where: { id: id } });
+ await models.MetadataDatabase.update({ updateAt: moment(), ...ctx.request.body }, { where: { id: id } });
ctx.status = 200;
ctx.body = { message: '修改元数据成功' }
}
@@ -314,7 +327,7 @@ async function putMeatadataDatabases(ctx) {
}
}
//删除库表元数据
-async function delMeatadataDatabases(ctx) {
+async function delMetadataDatabases(ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
@@ -397,6 +410,256 @@ async function getMetadataDatabasesById(ctx) {
}
}
}
+
+//打标元数据
+async function postTagMetadata(ctx) {
+ const transaction = await ctx.fs.dc.orm.transaction();
+ try {
+ const { tags, database, file, restapi } = ctx.request.body;
+ const models = ctx.fs.dc.models;
+ if (tags.length && (database || file || restapi)) {
+ if (database) {
+ await models.TagDatabase.destroy({ where: { database: database }, transaction });
+ const data = tags.map(tag => { return { tagId: tag, database: database } });
+ if (data.length)
+ await models.TagDatabase.bulkCreate(data, { transaction });
+ }
+ if (file) {
+ await models.TagFile.destroy({ where: { file: file }, transaction });
+ const data = tags.map(tag => { return { tagId: tag, file: file } });
+ if (data.length)
+ await models.TagFile.bulkCreate(data, { transaction });
+ }
+ if (restapi) {
+ await models.TagRestapi.destroy({ where: { restapi: restapi }, transaction });
+ const data = tags.map(tag => { return { tagId: tag, restapi: restapi } });
+ if (data.length)
+ await models.TagRestapi.bulkCreate(data, { transaction });
+ }
+ await transaction.commit();
+ ctx.status = 204;
+ } else {
+ ctx.body = { message: '参数不全,请重新配置' }
+ ctx.status = 400;
+ }
+ } catch (error) {
+ await transaction.rollback();
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {
+ "message": "打标元数据失败"
+ }
+ }
+}
+
+//获取元数据已打标数据
+async function getTagMetadata(ctx) {
+ try {
+ const models = ctx.fs.dc.models;
+ const { id } = ctx.params;
+ const { type } = ctx.query;
+ let rslt = [];
+ if (type === 'database') {
+ rslt = await models.Tag.findAll({
+ where: { '$tagDatabases.database$': id },
+ include: [{
+ model: models.TagDatabase,
+ }],
+ order: [['id', 'asc']],
+ });
+ }
+ if (type === 'file') {
+ rslt = await models.Tag.findAll({
+ where: { '$tagFiles.file$': id },
+ include: [{
+ model: models.TagFile,
+ }],
+ order: [['id', 'asc']],
+ });
+ }
+ if (type === 'restapi') {
+ rslt = await models.Tag.findAll({
+ where: { '$tagRestapis.restapi$': id },
+ include: [{
+ model: models.TagRestapi,
+ }],
+ order: [['id', 'asc']],
+ });
+ }
+ ctx.status = 200;
+ ctx.body = rslt;
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {
+ "message": "获取元数据已打标数据失败"
+ }
+ }
+}
+//申请资源
+async function postMetadataResourceApplications(ctx) {
+ try {
+ const { resourceName, applyBy } = ctx.request.body;
+ if (!resourceName || !applyBy) {
+ ctx.status = 400;
+ ctx.body = { message: '参数不全,请重新申请资源' }
+ } else {
+ const models = ctx.fs.dc.models;
+ const postOne = await models.ResourceConsumption.findOne({
+ where: { applyBy: applyBy, resourceName: resourceName }
+ });
+ if (postOne) {
+ ctx.status = 400;
+ ctx.body = { message: '该用户已申请过该元数据资源' }
+ } else {
+ await models.ResourceConsumption.create({ applyAt: moment(), approveState: '审批中', ...ctx.request.body });
+ ctx.body = { message: '申请资源成功' }
+ ctx.status = 200;
+ }
+ }
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {
+ "message": "申请资源失败"
+ }
+ }
+}
+
+//获取元数据资源申请记录
+async function getMetadataResourceApplications(ctx) {
+ try {
+ const models = ctx.fs.dc.models;
+ const { resourceNames, type } = ctx.query;
+ let rslt = [];
+ if (resourceNames && type) {
+ rslt = await models.ResourceConsumption.findAll({
+ where: { resourceName: { $in: resourceNames.split(',') }, resourceType: type }
+ });
+ }
+ ctx.status = 200;
+ ctx.body = rslt;
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {
+ "message": "获取元数据资源申请记录失败"
+ }
+ }
+}
+
+//新建文件元数据
+async function postMetadataFiles(ctx) {
+ try {
+ const { name, catalog, type, fileName } = ctx.request.body;
+ const models = ctx.fs.dc.models;
+ const postOne = await models.MetadataFile.findOne({
+ where: { name: name, catalog: catalog }
+ });
+ if (postOne) {
+ ctx.status = 400;
+ ctx.body = { message: '该资源目录下元数据名称已存在' }
+ } else {
+ if (!name || !catalog || !type || !fileName) {
+ ctx.body = { message: '参数不全,请重新配置' }
+ ctx.status = 400;
+ } else {
+ await models.MetadataFile.create({ createAt: moment(), ...ctx.request.body });
+ ctx.body = { message: '新建元数据成功' }
+ ctx.status = 200;
+ }
+ }
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {
+ "message": "新建元数据失败"
+ }
+ }
+}
+
+//修改文件元数据
+async function putMetadataFiles(ctx) {
+ try {
+ const { id } = ctx.params;
+ const { updateFileName } = ctx.query;
+ const { catalog, name } = ctx.request.body;
+ const models = ctx.fs.dc.models;
+ let metadataFileInfo = await models.MetadataFile.findOne({ where: { id } });
+ if (metadataFileInfo) {
+ if (updateFileName) {//编辑时,将文件删除,后又取消,则更新文件名为null
+ await models.MetadataFile.update({ updateAt: moment(), fileName: null }, { where: { id: id } });
+ ctx.status = 204;
+ } else {
+ const putOne = await models.MetadataFile.findOne({ where: { id: { $not: id }, catalog: catalog, name: name } });
+ if (putOne) {
+ ctx.status = 400;
+ ctx.body = { message: '该元数据名称已存在' }
+ } else {
+ await models.MetadataFile.update({ updateAt: moment(), ...ctx.request.body }, { where: { id: id } });
+ ctx.status = 200;
+ ctx.body = { message: '修改元数据成功' }
+ }
+ }
+ } else {
+ ctx.status = 400;
+ ctx.body = { message: '该元数据不存在' }
+ }
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {
+ "message": "修改元数据失败"
+ }
+ }
+}
+//删除文件元数据
+async function delMetadataFiles(ctx) {
+ const transaction = await ctx.fs.dc.orm.transaction();
+ try {
+ const models = ctx.fs.dc.models;
+ const { id } = ctx.params;
+ let metadataFileInfo = await models.MetadataFile.findOne({ where: { id } });
+ if (metadataFileInfo) {
+ let deletable = true;
+ let tagFileInfo = await models.TagFile.findOne({ where: { file: id } });
+ if (tagFileInfo) {
+ ctx.status = 400;
+ ctx.body = { message: '该元数据已被打标' }
+ deletable = false;
+ } else {
+ let resourceConsumptionInfo = await models.ResourceConsumption.findOne({
+ where: {
+ resourceName: metadataFileInfo.name,
+ resourceType: '文件'
+ }
+ });
+ if (resourceConsumptionInfo) {
+ ctx.status = 400;
+ ctx.body = { message: '该元数据存在资源申请' }
+ deletable = false;
+ }
+ }
+ if (deletable) {
+ await models.MetadataFile.destroy({
+ where: { id: id },
+ transaction
+ })
+ await transaction.commit();
+ ctx.status = 200;
+ ctx.body = { message: '删除元数据成功' }
+ }
+ } else {
+ ctx.status = 400;
+ ctx.body = { message: '该元数据不存在' }
+ }
+ } catch (error) {
+ await transaction.rollback();
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = { message: '删除元数据失败' }
+ }
+}
module.exports = {
getResourceCatalog,
postResourceCatalog,
@@ -406,8 +669,15 @@ module.exports = {
getMetadataFiles,
getMetadataRestapis,
getMetadataModels,
- postMeatadataDatabases,
- putMeatadataDatabases,
- delMeatadataDatabases,
- getMetadataDatabasesById
+ postMetadataDatabases,
+ putMetadataDatabases,
+ delMetadataDatabases,
+ getMetadataDatabasesById,
+ postTagMetadata,
+ getTagMetadata,
+ postMetadataResourceApplications,
+ getMetadataResourceApplications,
+ postMetadataFiles,
+ putMetadataFiles,
+ delMetadataFiles
}
\ No newline at end of file
diff --git a/api/app/lib/models/metadata_file.js b/api/app/lib/models/metadata_file.js
index 7e3af9f..a99adbb 100644
--- a/api/app/lib/models/metadata_file.js
+++ b/api/app/lib/models/metadata_file.js
@@ -113,6 +113,15 @@ module.exports = dc => {
primaryKey: false,
field: "catalogKey",
autoIncrement: false
+ },
+ fileName: {
+ type: DataTypes.STRING,
+ allowNull: true,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "file_name",
+ autoIncrement: false
}
}, {
tableName: "t_metadata_file",
diff --git a/api/app/lib/routes/latestMetadata/index.js b/api/app/lib/routes/latestMetadata/index.js
index 6f089c9..50e4d4c 100644
--- a/api/app/lib/routes/latestMetadata/index.js
+++ b/api/app/lib/routes/latestMetadata/index.js
@@ -6,13 +6,13 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/resource-catalog'] = { content: '获取资源目录', visible: false };
router.get('/resource-catalog', latestMetadata.getResourceCatalog);
- app.fs.api.logAttr['POST /resource-catalog'] = { content: '新建资源目录', visible: true };
+ app.fs.api.logAttr['POST/resource-catalog'] = { content: '新建资源目录', visible: true };
router.post('/resource-catalog', latestMetadata.postResourceCatalog);
- app.fs.api.logAttr['PUT /resource-catalog/:id'] = { content: '修改资源目录', visible: true };
+ app.fs.api.logAttr['PUT/resource-catalog/:id'] = { content: '修改资源目录', visible: true };
router.put('/resource-catalog/:id', latestMetadata.putResourceCatalog);
- app.fs.api.logAttr['DEL /resource-catalog/:id'] = { content: '删除资源目录', visible: true };
+ app.fs.api.logAttr['DEL/resource-catalog/:id'] = { content: '删除资源目录', visible: true };
router.delete('/resource-catalog/:id', latestMetadata.delResourceCatalog);
app.fs.api.logAttr['GET/metadata/databases'] = { content: '获取库表元数据列表', visible: false };
@@ -27,15 +27,36 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/metadata/models'] = { content: '获取元数据模型', visible: false };
router.get('/metadata/models', latestMetadata.getMetadataModels);
- app.fs.api.logAttr['POST /meatadata/databases'] = { content: '新建库表元数据', visible: true };
- router.post('/meatadata/databases', latestMetadata.postMeatadataDatabases);
+ app.fs.api.logAttr['POST/metadata/databases'] = { content: '新建库表元数据', visible: true };
+ router.post('/metadata/databases', latestMetadata.postMetadataDatabases);
- app.fs.api.logAttr['PUT /meatadata/databases/:id'] = { content: '修改库表元数据', visible: true };
- router.put('/meatadata/databases/:id', latestMetadata.putMeatadataDatabases);
+ app.fs.api.logAttr['PUT/metadata/databases/:id'] = { content: '修改库表元数据', visible: true };
+ router.put('/metadata/databases/:id', latestMetadata.putMetadataDatabases);
- app.fs.api.logAttr['DEL /meatadata/databases/:id'] = { content: '删除库表元数据', visible: true };
- router.delete('/meatadata/databases/:id', latestMetadata.delMeatadataDatabases);
+ app.fs.api.logAttr['DEL/metadata/databases/:id'] = { content: '删除库表元数据', visible: true };
+ router.delete('/metadata/databases/:id', latestMetadata.delMetadataDatabases);
app.fs.api.logAttr['GET/metadata/databases/:id'] = { content: '获取库表元数据基本信息', visible: false };
router.get('/metadata/databases/:id', latestMetadata.getMetadataDatabasesById);
+
+ app.fs.api.logAttr['POST/tag/metadata'] = { content: '打标元数据', visible: true };
+ router.post('/tag/metadata', latestMetadata.postTagMetadata);
+
+ app.fs.api.logAttr['GET/tag/metadata/:id'] = { content: '获取元数据已打标数据', visible: true };
+ router.get('/tag/metadata/:id', latestMetadata.getTagMetadata);
+
+ app.fs.api.logAttr['POST/resource-consumption/applications'] = { content: '申请资源', visible: true };
+ router.post('/resource-consumption/applications', latestMetadata.postMetadataResourceApplications);
+
+ app.fs.api.logAttr['GET/resource-consumption/applications'] = { content: '获取元数据资源申请记录', visible: true };
+ router.get('/resource-consumption/applications', latestMetadata.getMetadataResourceApplications);
+
+ app.fs.api.logAttr['POST/metadata/files'] = { content: '新建文件元数据', visible: true };
+ router.post('/metadata/files', latestMetadata.postMetadataFiles);
+
+ app.fs.api.logAttr['PUT/metadata/files/:id'] = { content: '修改文件元数据', visible: true };
+ router.put('/metadata/files/:id', latestMetadata.putMetadataFiles);
+
+ app.fs.api.logAttr['DEL/metadata/files/:id'] = { content: '删除文件元数据', visible: true };
+ router.delete('/metadata/files/:id', latestMetadata.delMetadataFiles);
};
\ No newline at end of file
diff --git a/scripts/0.0.4/01_alter_t_metadata_database&t_resource_consumption.sql b/scripts/0.0.4/01_alter_t_metadata_database&t_resource_consumption.sql
index c7d49be..407ce42 100644
--- a/scripts/0.0.4/01_alter_t_metadata_database&t_resource_consumption.sql
+++ b/scripts/0.0.4/01_alter_t_metadata_database&t_resource_consumption.sql
@@ -8,6 +8,8 @@ alter table t_metadata_database
add "catalogKey" varchar(255) not null;
alter table t_metadata_file
add "catalogKey" varchar(255) not null;
+alter table t_metadata_file
+ add file_name varchar(255);
alter table t_metadata_restapi
add "catalogKey" varchar(255) not null;
diff --git a/web/client/src/components/UploadLocal/index.js b/web/client/src/components/UploadLocal/index.js
new file mode 100644
index 0000000..a275c81
--- /dev/null
+++ b/web/client/src/components/UploadLocal/index.js
@@ -0,0 +1,339 @@
+'use strict';
+
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { Spin, Upload, message, Modal, Card, Button } from 'antd';
+import moment from 'moment';
+import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons';
+import { RouteRequest } from '@peace/utils';
+import { RouteTable } from '$utils'
+const { confirm } = Modal;
+class Uploads extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ fileUploading: false,
+ fileList: [],
+ curPreviewPic: '',
+ delPicIng: false,
+ removeFilesList: []
+ };
+ }
+ dealName = (uploaded) => {
+ let realName = uploaded.split('/')[2]
+ let x1 = realName.split('.')
+ let x2 = x1[0].split('_')
+ let showName = `${x2[0]}.${x1[1]}`
+ return showName
+ }
+
+ // setFileList = (value) => {
+ // let defaultFileList = [];
+ // defaultFileList = value.map((u, index) => {
+ // let fileUrl = `${this.ApiRoot}/${u.url}`;
+ // return {
+ // uid: -index - 1,
+ // name: this.dealName(u.url),
+ // status: 'done',
+ // storageUrl: u.url,
+ // url: fileUrl
+ // };
+ // });
+ // onChange(defaultFileList)
+ // this.setState({
+ // fileList: defaultFileList
+ // });
+ // };
+
+ componentDidMount() {
+ const { value } = this.props;
+ if (value) {
+ // this.setState(value);
+ this.setState({ fileList: value })
+
+ }
+ }
+
+ componentWillReceiveProps(np) {
+ const { dispatch, value: thisEditData, onChange } = this.props;
+ const { value: nextEditData } = np;
+
+ const setFileList = () => {
+ let defaultFileList = [];
+ defaultFileList = nextEditData.map((u, index) => {
+ let fileUrl = u.filename;
+ return {
+ uid: -index - 1,
+ name: u.name,
+ status: 'done',
+ storageUrl: u.filename,
+ url: fileUrl,
+ size: u.size || -1
+ };
+ });
+ this.setState({
+ fileList: defaultFileList
+ });
+ };
+
+ if (nextEditData && nextEditData.length) {
+ if (!thisEditData || !this.state.fileList.length) {
+ setFileList();
+ } else if (nextEditData.length != thisEditData.length) {
+ setFileList();
+ } else {
+ let repeat = true;
+ for (let i = 0; i < thisEditData.length; i++) {
+ if (thisEditData[i] != nextEditData[i]) {
+ repeat = false;
+ break;
+ }
+ }
+ if (!repeat) {
+ setFileList();
+ }
+ }
+ }
+ // else{
+ // this.setState({
+ // fileList:[],
+ // })
+ // }
+ }
+ //删除文件
+ deleteFile(file) {
+ if (file.url) {
+ RouteRequest.delete(RouteTable.cleanUpUploadTrash, { url: file.url });
+ };
+ }
+ handleOk = (that, file, fileList, curPreviewPic, removeFilesList) => {
+ let nextFileList = [];
+ fileList.map((f, i) => {
+ if (f.uid != file.uid) {
+ nextFileList.push(f);
+ }
+ });
+ that.deleteFile(file);
+ let nextRemoveFiles = removeFilesList.concat([file.storageUrl]);
+ if (curPreviewPic == file.url) {
+ that.setState({
+ curPreviewPic: ''
+ });
+ }
+ that.props.onChange(nextFileList);
+ that.setState({
+ fileList: nextFileList,
+ removeFilesList: nextRemoveFiles
+ });
+ }
+ render() {
+ const UploadPath = {
+ project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'png', 'jpg', 'rar', 'zip'],
+ report: ['doc', 'docx', 'xls', 'xlsx', 'pdf'],
+ data: ['txt', 'xls', 'xlsx'],
+ image: ['png', 'jpg', 'svg', 'jpeg'],
+ three: ['js'],
+ video: ['mp4']
+ };
+ /**
+ * uploadType 【string】 主要区别文件上传路径 以及类型 以 web/routes/attachment/index.js 中 UploadPath 的 key 值为准;默认 project;
+ * disabled 【boolean】 上传是否可用
+ * maxFilesNum 【number】 最大上传数量
+ * fileTypes 【array[string]】 可允许上传的文件类型;
+ * maxFileSize 【number】 单个文件最大大小 M
+ * listType 【antd】 upload 组件的属性
+ * onChange 【function】 文件数量变化时候回调 返回文件
+ * value 【array[obj]】 编辑数据 [{url:'xxx', [size:999]}]
+ * onStateChange 【function】 文件状态改变回调函数 上传中 return { uploading:true/false }
+ */
+ const {
+ uploadType,
+ disabled,
+ maxFilesNum,
+ fileTypes,
+ maxFileSize,
+ listType,
+ onChange,
+ value,
+ showUploadList,
+ onStateChange,
+ addNew
+ } = this.props;
+ const { fileList, curPreviewPic, delPicIng, removeFilesList } = this.state;
+ const that = this;
+ let uploadType_ = uploadType || 'project';
+ let maxFilesNum_ = maxFilesNum || 1;
+ let defaultFileTypes = fileTypes || UploadPath[uploadType_];
+ const uploadProps = {
+ name: 'checkFile_',
+ multiple: false,
+ showUploadList: showUploadList || true,
+ action: "/_upload/new?type=project",
+ listType: listType || 'text',
+ disabled: disabled,
+ beforeUpload: (file) => {
+ if (fileList.length >= maxFilesNum_) {
+ message.warning(`最多选择${maxFilesNum_}个文件上传`);
+ return false;
+ }
+ if (file.name.length > 60) {
+ message.warning(`文件名过长(大于60字符),请修改后上传`);
+ return false;
+ }
+ const extNames = file.name.split('.');
+ var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/;
+ // if (!reg.exec(file.name)) {
+ // message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`);
+ // return false;
+ // }
+ let isDAE = false;
+ if (extNames.length > 0) {
+ let fileType = extNames[extNames.length - 1].toLowerCase();
+ isDAE = defaultFileTypes.some((f) => f == fileType);
+ }
+ if (!isDAE) {
+ message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`);
+ return false;
+ }
+ const isLt = file.size / 1024 / 1024 < (maxFileSize || 3);
+ if (!isLt) {
+ message.error(`文件必须小于${maxFileSize || 3}MB!`);
+ return false;
+ }
+ this.setState({
+ fileUploading: true
+ });
+ if (onStateChange) {
+ onStateChange({ uploading: true });
+ }
+ },
+ onChange(info) {
+ const status = info.file.status;
+ if (status === 'uploading') {
+ that.setState({
+ fileList: info.fileList
+ });
+ }
+ if (status === 'done') {
+ let { filename } = info.file.response;
+ let size = info.file.size;
+ let nextFileList = fileList;
+ nextFileList[nextFileList.length - 1] = {
+ uid: -moment().unix(),
+ name: info.file.name,
+ status: 'done',
+ storageUrl: filename,
+ url: filename,
+ size: size
+ };
+ onChange(nextFileList);
+ that.setState({
+ fileUploading: false,
+ fileList: nextFileList
+ });
+ if (onStateChange) {
+ onStateChange({ uploading: false });
+ }
+ } else if (status === 'error') {
+ that.setState({
+ fileUploading: false
+ });
+ message.error(`${info.file.name} 上传失败,请重试`);
+ if (onStateChange) {
+ onStateChange({ uploading: false });
+ }
+ }
+ },
+ onRemove(file) {
+ // if (confirm('请确认是否删除此文件?删除后将不可恢复!') === true) {
+ if (addNew) {
+ that.handleOk(that, file, fileList, curPreviewPic, removeFilesList);
+ } else {
+ confirm({
+ title: '请确认是否删除此文件?删除后将不可恢复!',
+ onOk() {
+ that.handleOk(that, file, fileList, curPreviewPic, removeFilesList);
+ },
+ onCancel() { },
+ });
+ }
+ },
+ onPreview(file) {
+ let filePostfix = file.url.split('.').pop();
+ filePostfix = filePostfix.toLowerCase();
+ if (UploadPath.image.some((img) => img == filePostfix)) {
+ that.setState({
+ curPreviewPic: file.url
+ });
+ } else {
+ message.warn('仅支持图片预览');
+ }
+ }
+ };
+
+ let fileList_ = fileList
+ // .map(f => {
+ // if (f.storageUrl) {
+ // let realName = f.storageUrl.split('/').pop()
+ // if (f.name != realName) {
+ // f.name = realName
+ // }
+ // }
+ // return f
+ // })
+
+ return (
+
+
+
+ {
+ disabled ? (
+ ''
+ ) :
+ listType == 'picture-card' ?
+ (
+ fileList.length >= maxFilesNum_ ? null : (
+
+ )
+ ) : (
+
+ )
+ }
+
+ {
+ curPreviewPic ? (
+
+
+ 文件预览
+ { this.setState({ curPreviewPic: '' }); }}
+ >
+
+
+
+
+
+ ) : ''
+ }
+
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ const { auth } = state
+ return {
+ user: auth.user
+ };
+}
+
+export default connect(mapStateToProps)(Uploads);
diff --git a/web/client/src/components/index.js b/web/client/src/components/index.js
index c20bafa..80d0a3e 100644
--- a/web/client/src/components/index.js
+++ b/web/client/src/components/index.js
@@ -6,11 +6,13 @@ import Uploads from './Uploads';
import NoResource from './no-resource';
import ExportAndImport from './export';
import ButtonGroup from './buttonGroup';
+import UploadLocal from './UploadLocal';
export {
Upload,
Uploads,
NoResource,
ExportAndImport,
- ButtonGroup
+ ButtonGroup,
+ UploadLocal
};
diff --git a/web/client/src/sections/metadataManagement/actions/metadata.js b/web/client/src/sections/metadataManagement/actions/metadata.js
index c4f1ae0..e522737 100644
--- a/web/client/src/sections/metadataManagement/actions/metadata.js
+++ b/web/client/src/sections/metadataManagement/actions/metadata.js
@@ -100,37 +100,37 @@ export function getMetadataModels(params) {
});
}
-export function postMeatadataDatabases(data) {
+export function postMetadataDatabases(data) {
return dispatch => basicAction({
type: 'post',
data: data,
dispatch: dispatch,
actionType: 'POST_METADATA_DATABASES',
- url: ApiTable.postMeatadataDatabases,
+ url: ApiTable.postMetadataDatabases,
msg: { option: '新建元数据' },
reducer: {}
});
}
-export function putMeatadataDatabases(id, data) {
+export function putMetadataDatabases(id, data) {
return dispatch => basicAction({
type: 'put',
data: data,
dispatch,
actionType: 'PUT_METADATA_DATABASES',
- url: ApiTable.putMeatadataDatabases.replace('{id}', id),
+ url: ApiTable.putMetadataDatabases.replace('{id}', id),
msg: {
option: '修改元数据',
}
});
}
-export function delMeatadataDatabases(id) {
+export function delMetadataDatabases(id) {
return dispatch => basicAction({
type: 'del',
dispatch,
actionType: 'DELETE_METADATA_DATABASES',
- url: ApiTable.delMeatadataDatabases.replace('{id}', id),
+ url: ApiTable.delMetadataDatabases.replace('{id}', id),
msg: {
option: '删除元数据',
}
@@ -146,4 +146,88 @@ export function getMetadataDatabasesById(id) {
msg: { error: '获取元数据基本信息失败' },
reducer: { name: 'metadataDatabasesInfo' }
});
-}
\ No newline at end of file
+}
+
+
+export function postTagMetadata(data) {
+ return dispatch => basicAction({
+ type: 'post',
+ data: data,
+ dispatch: dispatch,
+ actionType: 'POST_TAG_METADATA',
+ url: ApiTable.postTagMetadata,
+ msg: { option: '打标元数据' },
+ reducer: {}
+ });
+}
+export function getTagMetadata(id, type) {
+ return dispatch => basicAction({
+ type: 'get',
+ dispatch: dispatch,
+ actionType: 'GET_TAG_METADATA',
+ url: ApiTable.getTagMetadata.replace('{id}', id) + `?type=${type}`,
+ msg: { error: '获取元数据已打标数据失败' },
+ reducer: { name: 'tagMetadata' }
+ });
+}
+
+export function postMetadataResourceApplications(data) {
+ return dispatch => basicAction({
+ type: 'post',
+ data: data,
+ dispatch: dispatch,
+ actionType: 'POST_METADATA_RESOURCE_APPLICATIONS',
+ url: ApiTable.postMetadataResourceApplications,
+ msg: { option: '申请资源' },
+ reducer: {}
+ });
+}
+
+export function getMetadataResourceApplications(params) {
+ return dispatch => basicAction({
+ type: 'get',
+ dispatch: dispatch,
+ query: params,
+ actionType: 'GET_TAG_METADATA',
+ url: ApiTable.getMetadataResourceApplications,
+ msg: { error: '获取元数据资源申请记录失败' },
+ reducer: { name: 'metadataResourceApplications' }
+ });
+}
+
+export function postMetadataFiles(data) {
+ return dispatch => basicAction({
+ type: 'post',
+ data: data,
+ dispatch: dispatch,
+ actionType: 'POST_METADATA_FILES',
+ url: ApiTable.postMetadataFiles,
+ msg: { option: '新建元数据' },
+ reducer: {}
+ });
+}
+
+export function putMetadataFiles(id, data, updateFileName) {
+ return dispatch => basicAction({
+ type: 'put',
+ data: data,
+ dispatch,
+ actionType: 'PUT_METADATA_FILES',
+ url: ApiTable.putMetadataFiles.replace('{id}', id) + `?updateFileName=${updateFileName || ''}`,
+ msg: {
+ option: updateFileName ? '' : '修改元数据',
+ }
+ });
+}
+
+export function delMetadataFiles(id) {
+ return dispatch => basicAction({
+ type: 'del',
+ dispatch,
+ actionType: 'DELETE_METADATA_FILES',
+ url: ApiTable.delMetadataFiles.replace('{id}', id),
+ msg: {
+ option: '删除元数据',
+ }
+ });
+}
diff --git a/web/client/src/sections/metadataManagement/components/metadataDatabaseModal.js b/web/client/src/sections/metadataManagement/components/metadataDatabaseModal.js
index e6dcad4..d005501 100644
--- a/web/client/src/sections/metadataManagement/components/metadataDatabaseModal.js
+++ b/web/client/src/sections/metadataManagement/components/metadataDatabaseModal.js
@@ -137,8 +137,8 @@ const MetadataDatabaseModal = (props) => {
-
+ rules={[{ max: 255, message: `详情不超过255个字符` }]}>
+
{renderModelItems()}
diff --git a/web/client/src/sections/metadataManagement/components/metadataFileModal.js b/web/client/src/sections/metadataManagement/components/metadataFileModal.js
new file mode 100644
index 0000000..78c6417
--- /dev/null
+++ b/web/client/src/sections/metadataManagement/components/metadataFileModal.js
@@ -0,0 +1,174 @@
+import React, { useEffect, useState } from 'react';
+import { Modal, Input, Form, Select, InputNumber, Tooltip, Tag, message } from 'antd';
+import { UploadLocal } from '$components';
+const { TextArea } = Input;
+const MetadataFileModal = (props) => {
+ const { onConfirm, onCancel, editData, metadataModels } = props;
+ const [form] = Form.useForm();
+ const [editUrl, setEditUrl] = useState([]);
+ useEffect(() => {
+ }, []);
+ const handleOk = () => {
+ form.validateFields().then(values => {
+ if (onConfirm) {
+ let dataSave = JSON.parse(JSON.stringify(values));
+ dataSave.attributesParam = {};
+ metadataModels.map(m => {
+ dataSave.attributesParam[m.attributeCode] = values[m.attributeCode];
+ delete dataSave[m.attributeCode];
+ })
+ onConfirm(dataSave);
+ }
+ })
+ }
+ const validatorNull = (rule, value, getFieldValue, validateFields, label) => {
+ if (!value || !value.trim().length) {
+ return Promise.reject(new Error(`${label}不可空字符串`));
+ }
+ return Promise.resolve();
+ }
+ const renderModelItems = () => {
+ const items = metadataModels.filter(mm => mm.modelType === '文件').map(m => {
+ if (m.control === '文本框') {
+ const rules = [{ required: !m.nullable, message: '' }]
+ if (!m.nullable) {
+ rules.push(({ getFieldValue, validateFields }) => ({
+ validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields, m.attributeName) }
+ }))
+ rules.push({ max: m.length, message: `${m.attributeName}不超过${m.length}个字符` })
+ }
+ return 10 ?
+ {m.attributeName.substring(0, 10) + '...'}
+ : m.attributeName}
+ name={m.attributeCode}
+ rules={rules}>
+
+
+ } else if (m.control === '数字输入框') {
+ const rules = [{ required: !m.nullable, message: `${m.attributeName}不可空` }]
+ let maxValue = '';
+ let length = m.length;
+ if (length) {
+ while (length > 0) {
+ maxValue += '9'
+ length--;
+ }
+ }
+ return 10 ?
+ {m.attributeName.substring(0, 10) + '...'}
+ : m.attributeName}
+ name={m.attributeCode}
+ rules={rules}>
+
+
+ } else {
+ return 10 ?
+ {m.attributeName.substring(0, 10) + '...'}
+ : m.attributeName}
+ name={m.attributeCode}
+ rules={[{ required: !m.nullable, message: `${m.attributeName}不可空` }]}>
+
+
+ }
+ })
+ return items;
+ }
+
+ const vsjunct = (params) => {
+ if (params.length) {
+ let appendix = []
+ for (let p of params) {
+ appendix.push({
+ fName: p.name,
+ size: p.size,
+ fileSize: p.size,
+ storageUrl: p.storageUrl,//必须有storageUrl
+ })
+ }
+ setEditUrl(appendix)
+ } else {
+ setEditUrl([])
+ }
+ }
+ const handleCancel = () => {
+ if (editData.add) {
+ if (form.getFieldValue('files') && form.getFieldValue('files').length) {
+ onCancel(form.getFieldValue('files')[0]);
+ } else {
+ onCancel(null);
+ }
+ } else {
+ if (!(form.getFieldValue('files') && form.getFieldValue('files').length)) {
+ message.warning('文件已被删除,可重新编辑上传');
+ onCancel(null, editData.record.id);
+ } else {
+ if (!editData.record.files.length) {
+ message.warning('文件需确定提交,才进行入库存储');
+ onCancel(form.getFieldValue('files')[0]);
+ } else {
+ onCancel(null);
+ }
+ }
+ }
+ }
+ return (
+ handleOk(null)}
+ onCancel={() => handleCancel()}>
+ ({
+ validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields, '文件名称') }
+ })]}>
+
+
+
+
+
+
+
+
+
+ 文件大小不超过40MB,开放资源包含多个文件,建议将文件进行压缩,形成压缩包再上传
+ 支持的文件格式:jpg,png,gif,txt,doc,docx,pdf,xsl,xlsx,zip,rar
+
+ {renderModelItems()}
+
+
+ )
+}
+export default MetadataFileModal;
\ No newline at end of file
diff --git a/web/client/src/sections/metadataManagement/components/metadataResourceModal.js b/web/client/src/sections/metadataManagement/components/metadataResourceModal.js
new file mode 100644
index 0000000..a6ff43f
--- /dev/null
+++ b/web/client/src/sections/metadataManagement/components/metadataResourceModal.js
@@ -0,0 +1,53 @@
+import React, { useEffect, useState } from 'react';
+import { Modal, Input, Form } from 'antd';
+const { TextArea } = Input;
+const MetadataResourceModal = (props) => {
+ const { onConfirm, onCancel, editData } = props;
+ const [form] = Form.useForm();
+ useEffect(() => {
+ }, []);
+ const handleOk = () => {
+ form.validateFields().then(values => {
+ if (onConfirm) {
+ onConfirm({ ...editData.record, ...values });
+ }
+ })
+ }
+ const validatorNull = (rule, value, getFieldValue, validateFields, label) => {
+ if (!value || !value.trim().length) {
+ return Promise.reject(new Error(`${label}不可空字符串`));
+ }
+ return Promise.resolve();
+ }
+ return (
+ handleOk(null)}
+ onCancel={onCancel}>
+
+
+
+
+
+
+ ({
+ validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields, '需求描述') }
+ })]}>
+
+
+
+
+ )
+}
+export default MetadataResourceModal;
\ No newline at end of file
diff --git a/web/client/src/sections/metadataManagement/components/metadataTagModal.js b/web/client/src/sections/metadataManagement/components/metadataTagModal.js
new file mode 100644
index 0000000..7741862
--- /dev/null
+++ b/web/client/src/sections/metadataManagement/components/metadataTagModal.js
@@ -0,0 +1,79 @@
+import React, { useEffect, useState } from 'react';
+import { Modal, Form, Select, } from 'antd';
+const MetadataDatabaseTagModal = (props) => {
+ const { onConfirm, onCancel, editData, tagList } = props;
+ const [form] = Form.useForm();
+ const [tagSet, setTagSet] = useState(editData.record.tagSet || []);
+ useEffect(() => {
+ }, []);
+ const handleOk = () => {
+ form.validateFields().then(values => {
+ if (onConfirm) {
+ onConfirm({ ...values });
+ }
+ })
+ }
+ const renderTagItems = () => {
+ let tags = [];
+ if (tagSet.length) {
+ tagList.map(t => {
+ if (tagSet.includes(t.tagSetId)) {
+ t.tags.map(t => {
+ tags.push({t.name})
+ })
+ }
+ });
+ }
+ return tags;
+ }
+ return (
+ handleOk(null)}
+ onCancel={onCancel}>
+
+
+
+
+
+
+
+
+ )
+}
+export default MetadataDatabaseTagModal;
\ No newline at end of file
diff --git a/web/client/src/sections/metadataManagement/containers/databasesTable.js b/web/client/src/sections/metadataManagement/containers/databasesTable.js
index 2431e4e..b9e315c 100644
--- a/web/client/src/sections/metadataManagement/containers/databasesTable.js
+++ b/web/client/src/sections/metadataManagement/containers/databasesTable.js
@@ -6,10 +6,12 @@ import moment from 'moment';
import FileSaver from 'file-saver';
import MetadataDatabaseModal from '../components/metadataDatabaseModal';
import { ModelTypes } from '../constants/index';
+import MetadataTagModal from '../components/metadataTagModal';
+import MetadataResourceModal from '../components/metadataResourceModal';
const DatabaseTable = (props) => {
const { user, dispatch, actions, clientHeight, resourceCatalogId, resourceCatalogKey,
- resourceCatalogPath, isRequesting, metadataModels, setView } = props;
+ resourceCatalogPath, isRequesting, metadataModels, setView, tagList, metadataResourceApplications } = props;
const { metadataManagement } = actions;
const SortValues = { 'ascend': 'asc', 'descend': 'desc' };
const [tableData, setTableData] = useState([]);
@@ -22,8 +24,13 @@ const DatabaseTable = (props) => {
const [selectedRows, setSelectedRows] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [editData, setEditData] = useState({});
+ const [tagModalVisible, setTagModalVisible] = useState(false);
+ const [editTagData, setEditTagData] = useState({});
+ const [resourceModalVisible, setResourceModalVisible] = useState(false);
+ const [editResourceData, setEditResourceData] = useState({});
useEffect(() => {
+ dispatch(metadataManagement.getTagList());
setCreateAtSort('descend');
initData({ limit, offset: currentPage - 1, orderDirection: SortValues[createAtSort] });
}, [resourceCatalogId]);
@@ -33,14 +40,23 @@ const DatabaseTable = (props) => {
if (res.success) {
setTableData(res.payload.data.rows);
setTableDataCount(res.payload.data.count);
+ let resourceNames = [];
+ res.payload.data.rows.map(r => {
+ if (r.type === '表') {
+ resourceNames.push(r.name);
+ }
+ })
+ if (resourceNames.length)
+ dispatch(metadataManagement.getMetadataResourceApplications({ resourceNames: resourceNames.join(','), type: '库表' }))
}
+
})
}
const onView = (record) => {
setView({ path: '/' + resourceCatalogPath.join('/'), ...record });
}
const onEdit = (record) => {
- dispatch(metadataManagement.getMetadataModels({ modelTypes: ModelTypes })).then(res => {
+ dispatch(metadataManagement.getMetadataModels({ modelTypes: ModelTypes.join(',') })).then(res => {
if (res.success) {
setEditData({ title: '修改库表元数据', record: { path: '/' + resourceCatalogPath.join('/'), ...record, ...record.attributesParam } });
setModalVisible(true);
@@ -48,15 +64,44 @@ const DatabaseTable = (props) => {
})
}
const confirmDelete = (id) => {
- dispatch(metadataManagement.delMeatadataDatabases(id)).then(res => {
+ dispatch(metadataManagement.delMetadataDatabases(id)).then(res => {
if (res.success) {
onSearch(); setModalVisible(false);
}
});
}
- const marking = (id) => { }
- const applyResources = (id) => { }
+ const marking = (id) => {
+ dispatch(metadataManagement.getTagMetadata(id, 'database')).then(res => {
+ if (res.success) {
+ const obj = { tagSet: [], tags: [], id: id };
+ if (res.payload.data.length) {
+ obj.tagSet = res.payload.data.map(d => d.tagSet);
+ obj.tags = res.payload.data.map(d => d.id);
+ }
+ setEditTagData({ record: obj });
+ setTagModalVisible(true);
+ }
+ })
+ }
+ const onConfirmTag = (values) => {
+ dispatch(metadataManagement.postTagMetadata({ database: editTagData.record.id, ...values })).then(res => {
+ if (res.success) {
+ onSearch(); setTagModalVisible(false);
+ }
+ });
+ }
+ const applyResources = (record) => {
+ setEditResourceData({ record: { resourceName: record.name, applyBy: user.id, applyByName: user.name, resourceType: '库表' } });
+ setResourceModalVisible(true);
+ }
+ const onConfirmResource = (values) => {
+ dispatch(metadataManagement.postMetadataResourceApplications(values)).then(res => {
+ if (res.success) {
+ onSearch(); setResourceModalVisible(false);
+ }
+ });
+ }
const onTableChange = (pagination, filters, sorter) => {
let limit = Number.parseInt(pagination.pageSize);
let offset = Number.parseInt(pagination.current) - 1;
@@ -123,6 +168,8 @@ const DatabaseTable = (props) => {
dataIndex: 'action',
width: '8%',
render: (text, record) => {
+ let resourceApplicationsRecords = metadataResourceApplications.filter(ra =>
+ ra.applyBy == user.id && ra.resourceName === record.name);
return
onView(record)}>查看
onEdit(record)}>编辑
@@ -131,7 +178,9 @@ const DatabaseTable = (props) => {
onConfirm={() => confirmDelete(record.id)}
> 删除
{record.type === '表' ? marking(record.id)}>打标 : null}
- {record.type === '表' ? applyResources(record.id)}>申请资源 : null}
+ {record.type === '表' ? resourceApplicationsRecords.length === 0 ?
+ applyResources(record)}>申请资源 :
+ 申请资源 : null}
}
}];
@@ -187,12 +236,12 @@ const DatabaseTable = (props) => {
let obj = {}
if (editData.add) {
obj = { createBy: user.id, catalog: resourceCatalogId, catalogKey: resourceCatalogKey, ...values }
- dispatch(metadataManagement.postMeatadataDatabases(obj)).then(() => {
+ dispatch(metadataManagement.postMetadataDatabases(obj)).then(() => {
onSearch(); setModalVisible(false);
});
} else {
obj = { catalog: resourceCatalogId, catalogKey: resourceCatalogKey, ...values }
- dispatch(metadataManagement.putMeatadataDatabases(editData.record.id, obj)).then(res => {
+ dispatch(metadataManagement.putMetadataDatabases(editData.record.id, obj)).then(res => {
if (res.success) {
onSearch(); setModalVisible(false);
}
@@ -202,7 +251,7 @@ const DatabaseTable = (props) => {
return