Merge branch 'main' into next

This commit is contained in:
GitHub Actions Bot 2024-09-24 02:19:00 +00:00
commit 9a69a8b823
3 changed files with 164 additions and 26 deletions

View File

@ -3,7 +3,10 @@
FILES=$(find packages/pro-plugins/@nocobase -name .npmignore) FILES=$(find packages/pro-plugins/@nocobase -name .npmignore)
if [ "$1" == "ignore-src" ]; then if [ "$1" == "ignore-src" ]; then
CONTENT="/node_modules" CONTENT="/node_modules
/docker
/docs
"
else else
CONTENT="/node_modules CONTENT="/node_modules
/docker /docker

View File

@ -1045,4 +1045,149 @@ describe('export to xlsx', () => {
fs.unlinkSync(xlsxFilePath); fs.unlinkSync(xlsxFilePath);
} }
}); });
it('should export with different ui schema in associations', async () => {
const User = app.db.collection({
name: 'users',
fields: [
{ type: 'string', name: 'name' },
{ type: 'integer', name: 'age' },
{
type: 'hasMany',
name: 'posts',
target: 'posts',
},
{
type: 'belongsToMany',
name: 'groups',
target: 'groups',
through: 'usersGroups',
},
{
name: 'createdAt',
type: 'date',
interface: 'createdAt',
field: 'createdAt',
uiSchema: {
type: 'datetime',
title: '{{t("Created at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
'x-read-pretty': true,
},
},
],
});
const Post = app.db.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'belongsTo', name: 'user', target: 'users' },
],
});
const Group = app.db.collection({
name: 'groups',
fields: [
{ type: 'string', name: 'name' },
{
type: 'integer',
name: 'testInterface',
interface: 'testInterface',
title: 'Associations interface 测试',
uiSchema: { test: 'testValue' },
},
],
});
class TestInterface extends BaseInterface {
toString(value, ctx) {
return `${this.options.uiSchema.test}.${value}`;
}
}
app.db.interfaceManager.registerInterfaceType('testInterface', TestInterface);
await app.db.sync();
const [group1, group2, group3] = await Group.repository.create({
values: [
{ name: 'group1', testInterface: 1 },
{ name: 'group2', testInterface: 2 },
{ name: 'group3', testInterface: 3 },
],
});
const values = Array.from({ length: 22 }).map((_, index) => {
return {
name: `user${index}`,
age: index % 100,
groups: [
{
id: group1.get('id'),
},
{
id: group2.get('id'),
},
{
id: group3.get('id'),
},
],
posts: Array.from({ length: 3 }).map((_, postIndex) => {
return {
title: `post${postIndex}`,
};
}),
};
});
await User.repository.create({
values,
});
const exporter = new XlsxExporter({
collectionManager: app.mainDataSource.collectionManager,
collection: User,
chunkSize: 10,
columns: [
{ dataIndex: ['name'], defaultTitle: 'Name' },
{
dataIndex: ['groups', 'testInterface'],
defaultTitle: 'Test Field',
},
],
});
const wb = await exporter.run();
const xlsxFilePath = path.resolve(__dirname, `t_${uid()}.xlsx`);
try {
await new Promise((resolve, reject) => {
XLSX.writeFileAsync(
xlsxFilePath,
wb,
{
type: 'array',
},
() => {
resolve(123);
},
);
});
// 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', 'Associations interface 测试']);
const firstUser = sheetData[1];
expect(firstUser).toEqual(['user0', 'testValue.1,testValue.2,testValue.3']);
} finally {
fs.unlinkSync(xlsxFilePath);
}
});
}); });

View File

@ -152,7 +152,7 @@ class XlsxExporter {
if (dataIndex.length > 1) { if (dataIndex.length > 1) {
let targetCollection: ICollection; let targetCollection: ICollection;
for (let i = 0; i < dataIndex.length - 1; i++) { for (let i = 0; i < dataIndex.length; i++) {
const isLast = i === dataIndex.length - 1; const isLast = i === dataIndex.length - 1;
if (isLast) { if (isLast) {
@ -185,42 +185,32 @@ class XlsxExporter {
return value; return value;
} }
private getFieldRenderer(field?: IField, ctx?): (value) => any {
const InterfaceClass = this.options.collectionManager.getFieldInterface(field?.options?.interface);
if (!InterfaceClass) {
return this.renderRawValue;
}
const fieldInternface = new InterfaceClass(field?.options);
return (value) => fieldInternface.toString(value, ctx);
}
private renderCellValue(rowData: IModel, column: ExportColumn, ctx?) { private renderCellValue(rowData: IModel, column: ExportColumn, ctx?) {
const { dataIndex } = column; const { dataIndex } = column;
rowData = rowData.toJSON(); rowData = rowData.toJSON();
const value = rowData[dataIndex[0]]; const value = rowData[dataIndex[0]];
const field = this.findFieldByDataIndex(dataIndex);
const render = this.getFieldRenderer(field, ctx);
if (dataIndex.length > 1) { if (dataIndex.length > 1) {
const deepValue = deepGet(rowData, dataIndex); const deepValue = deepGet(rowData, dataIndex);
if (Array.isArray(deepValue)) { if (Array.isArray(deepValue)) {
return deepValue.join(','); return deepValue.map(render).join(',');
} }
return deepValue; return render(deepValue);
} }
return render(value);
const field = this.findFieldByDataIndex(dataIndex);
if (!field) {
return this.renderRawValue(value);
}
const fieldOptions = field.options;
const interfaceName = fieldOptions['interface'];
if (!interfaceName) {
return this.renderRawValue(value);
}
const InterfaceClass = this.options.collectionManager.getFieldInterface(interfaceName);
if (!InterfaceClass) {
return this.renderRawValue(value);
}
const interfaceInstance = new InterfaceClass(fieldOptions);
return interfaceInstance.toString(value, ctx);
} }
} }