mirror of
https://github.com/nocobase/nocobase.git
synced 2026-10-29 18:16:18 +08:00
fix: exporting empty values in nested associations (#6845)
This commit is contained in:
@@ -125,6 +125,7 @@ export interface CommonFindOptions extends Transactionable {
|
||||
sort?: Sort;
|
||||
context?: any;
|
||||
tree?: boolean;
|
||||
hooks?: boolean;
|
||||
}
|
||||
|
||||
export type FindOneOptions = Omit<FindOptions, 'limit'> & {
|
||||
|
||||
+99
@@ -440,6 +440,105 @@ describe('export to xlsx with preset', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should export with attachment url field and associations', async () => {
|
||||
const Post = app.db.collection({
|
||||
name: 'posts',
|
||||
fields: [
|
||||
{ type: 'string', name: 'title' },
|
||||
{
|
||||
name: 'attachment1',
|
||||
type: 'belongsTo',
|
||||
interface: 'm2o',
|
||||
onDelete: 'SET NULL',
|
||||
targetKey: 'id',
|
||||
target: 'attachments',
|
||||
uiSchema: {
|
||||
'x-component-props': {
|
||||
accept: 'image/*',
|
||||
multiple: false,
|
||||
},
|
||||
'x-component': 'AssociationField',
|
||||
title: 'attachment1',
|
||||
},
|
||||
storage: 'local',
|
||||
foreignKey: 'f_63ni7i8z7cx',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const Tag = app.db.collection({
|
||||
name: 'tags',
|
||||
fields: [
|
||||
{ type: 'string', name: 'name' },
|
||||
{
|
||||
name: 'post',
|
||||
type: 'belongsTo',
|
||||
target: 'posts',
|
||||
targetKey: 'id',
|
||||
foreignKey: 'aaa',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await app.db.sync();
|
||||
await Post.repository.create({
|
||||
values: {
|
||||
title: 'p1',
|
||||
attachment1: {
|
||||
title: 'nocobase-logo1',
|
||||
filename: '682e5ad037dd02a0fe4800a3e91c283b.png',
|
||||
extname: '.png',
|
||||
mimetype: 'image/png',
|
||||
url: '',
|
||||
storageId: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await Tag.repository.create({
|
||||
values: [
|
||||
{
|
||||
name: 'tag1',
|
||||
post: {
|
||||
id: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const exporter = new XlsxExporter({
|
||||
collectionManager: app.mainDataSource.collectionManager,
|
||||
collection: Tag,
|
||||
chunkSize: 10,
|
||||
columns: [
|
||||
{ dataIndex: ['name'], defaultTitle: 'Name' },
|
||||
{
|
||||
dataIndex: ['post', 'attachment1', 'url'],
|
||||
defaultTitle: 'URL',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const wb = await exporter.run();
|
||||
const xlsxFilePath = path.resolve(__dirname, `t_${uid()}.xlsx`);
|
||||
try {
|
||||
XLSX.writeFile(wb, xlsxFilePath);
|
||||
|
||||
// read xlsx file
|
||||
const workbook = XLSX.readFile(xlsxFilePath);
|
||||
const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const sheetData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });
|
||||
|
||||
const header = sheetData[0];
|
||||
expect(header).toEqual(['Name', 'URL']);
|
||||
|
||||
const result = sheetData[1];
|
||||
expect(result[1]).toEqual('/storage/uploads/682e5ad037dd02a0fe4800a3e91c283b.png');
|
||||
} finally {
|
||||
fs.unlinkSync(xlsxFilePath);
|
||||
}
|
||||
});
|
||||
|
||||
it('should export with china region field', async () => {
|
||||
const Post = app.db.collection({
|
||||
name: 'posts',
|
||||
|
||||
+11
-9
@@ -82,19 +82,21 @@ abstract class BaseExporter<T extends ExportOptions = ExportOptions> extends Eve
|
||||
}
|
||||
|
||||
protected getAppendOptionsFromFields(ctx?) {
|
||||
const fields = this.options.fields.map((x) => x[0]);
|
||||
const hasPermissionFields = _.isEmpty(ctx?.permission?.can?.params)
|
||||
? fields
|
||||
: _.intersection(ctx?.permission?.can?.params?.appends || [], fields);
|
||||
return hasPermissionFields
|
||||
.map((field) => {
|
||||
const fieldInstance = this.options.collection.getField(field);
|
||||
return this.options.fields
|
||||
.filter((fieldPath) => {
|
||||
const field = fieldPath[0];
|
||||
const hasPermission =
|
||||
_.isEmpty(ctx?.permission?.can?.params) || (ctx?.permission?.can?.params?.appends || []).includes(field);
|
||||
return hasPermission;
|
||||
})
|
||||
.map((fieldPath) => {
|
||||
const fieldInstance = this.options.collection.getField(fieldPath[0]);
|
||||
if (!fieldInstance) {
|
||||
throw new Error(`Field "${field}" not found: , please check the fields configuration.`);
|
||||
throw new Error(`Field "${fieldPath[0]}" not found: , please check the fields configuration.`);
|
||||
}
|
||||
|
||||
if (fieldInstance.isRelationField()) {
|
||||
return field;
|
||||
return fieldPath.join('.');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -24,6 +24,7 @@ import StorageTypeLocal from './storages/local';
|
||||
import StorageTypeS3 from './storages/s3';
|
||||
import StorageTypeTxCos from './storages/tx-cos';
|
||||
import { encodeURL } from './utils';
|
||||
import _ from 'lodash';
|
||||
|
||||
export type * from './storages';
|
||||
|
||||
@@ -312,7 +313,7 @@ export class PluginFileManagerServer extends Plugin {
|
||||
|
||||
this.app.db.interfaceManager.registerInterfaceType('attachment', AttachmentInterface);
|
||||
|
||||
this.db.on('afterFind', async (instances) => {
|
||||
this.db.on('afterFind', async (instances, options) => {
|
||||
if (!instances) {
|
||||
return;
|
||||
}
|
||||
@@ -321,7 +322,27 @@ export class PluginFileManagerServer extends Plugin {
|
||||
if (name) {
|
||||
const collection = this.db.getCollection(name);
|
||||
if (collection?.name === 'attachments' || collection?.options?.template === 'file') {
|
||||
let recordMap = null;
|
||||
if (!options?.attributes?.includes('storageId')) {
|
||||
// 导出时,不会查出来 storageId,这里兼容重新取一下
|
||||
const ids = records.map((x) => x.id).filter(Boolean);
|
||||
const fullRecords = await collection.repository.find({
|
||||
where: { id: ids },
|
||||
transaction: options.transaction,
|
||||
hooks: false,
|
||||
});
|
||||
recordMap = _.keyBy(fullRecords, (x) => x.id);
|
||||
}
|
||||
for (const record of records) {
|
||||
if (recordMap) {
|
||||
const fullRecord = recordMap[record.id];
|
||||
if (fullRecord) {
|
||||
record.set('storageId', fullRecord.storageId);
|
||||
record.set('title', fullRecord.title);
|
||||
record.set('filename', fullRecord.filename);
|
||||
record.set('extname', fullRecord.extname);
|
||||
}
|
||||
}
|
||||
const url = await this.getFileURL(record);
|
||||
const previewUrl = await this.getFileURL(record, true);
|
||||
record.set('url', url);
|
||||
|
||||
Reference in New Issue
Block a user