feat: plugin export (#479)
* feat: init export plugin * feat: add client export * fix: fix the word spell * feat: export plugin done * feat: init export plugin * feat: add client export * fix: fix the word spell * feat: export plugin done * ci: change plugin-export version * refactor: renders add ctx params * fix: fix select and multipleSelect export * fix: array convert string * refactor: move SchemaInitializerPluginProvider * fix: build error * fix: change umijs config * fix: update SchemaInitializerPluginProvider * fix: import server * fix: fix some bug * fix: fix some bug * refactor: export plugin refactor * refactor: create all export fields by default * fix: fix export plugin bug * fix(plugin-collection-manager): uiSchema toJSON * fix: update yarn.lock * fix: fix init fields bug * refactor: enum params pass by client * fix: fix export table header title * refactor: refactor dataIndex * fix: fix dataIndex maybe complex object * fix: add checkboxGroup in export plugin * fix: add checkbox and i18n * feat: improve code Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
e69257e346
commit
d831a9b889
@ -3,7 +3,8 @@
|
|||||||
"version": "0.7.0-alpha.83",
|
"version": "0.7.0-alpha.83",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nocobase/client": "0.7.0-alpha.83"
|
"@nocobase/client": "0.7.0-alpha.83",
|
||||||
|
"@nocobase/plugin-export": "0.7.0-alpha.83"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
WorkflowShortcut
|
WorkflowShortcut
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { AuditLogsProvider } from '@nocobase/plugin-audit-logs/client';
|
import { AuditLogsProvider } from '@nocobase/plugin-audit-logs/client';
|
||||||
|
import { ExportPluginProvider } from '@nocobase/plugin-export/client';
|
||||||
import { notification } from 'antd';
|
import { notification } from 'antd';
|
||||||
import 'antd/dist/antd.css';
|
import 'antd/dist/antd.css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -105,6 +106,7 @@ const providers = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
ExportPluginProvider,
|
||||||
AuditLogsProvider,
|
AuditLogsProvider,
|
||||||
BlockSchemaComponentProvider,
|
BlockSchemaComponentProvider,
|
||||||
AntdSchemaComponentProvider,
|
AntdSchemaComponentProvider,
|
||||||
|
@ -18,22 +18,6 @@
|
|||||||
},
|
},
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["mock/**/*", "src/**/*", "config/**/*", ".umirc.ts", "typings.d.ts"],
|
||||||
"mock/**/*",
|
"exclude": ["node_modules", "lib", "es", "dist", "typings", "**/__test__", "test", "docs", "tests"]
|
||||||
"src/**/*",
|
|
||||||
"config/**/*",
|
|
||||||
".umirc.ts",
|
|
||||||
"typings.d.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"lib",
|
|
||||||
"es",
|
|
||||||
"dist",
|
|
||||||
"typings",
|
|
||||||
"**/__test__",
|
|
||||||
"test",
|
|
||||||
"docs",
|
|
||||||
"tests"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,6 @@ export default [
|
|||||||
'@nocobase/plugin-china-region',
|
'@nocobase/plugin-china-region',
|
||||||
'@nocobase/plugin-workflow',
|
'@nocobase/plugin-workflow',
|
||||||
'@nocobase/plugin-client',
|
'@nocobase/plugin-client',
|
||||||
|
'@nocobase/plugin-export',
|
||||||
'@nocobase/plugin-audit-logs',
|
'@nocobase/plugin-audit-logs',
|
||||||
] as PluginsConfigurations;
|
] as PluginsConfigurations;
|
||||||
|
@ -57,7 +57,6 @@ async function listWithNonPaged(ctx: Context) {
|
|||||||
|
|
||||||
export async function list(ctx: Context, next) {
|
export async function list(ctx: Context, next) {
|
||||||
const { paginate } = ctx.action.params;
|
const { paginate } = ctx.action.params;
|
||||||
|
|
||||||
if (paginate === false || paginate === 'false') {
|
if (paginate === false || paginate === 'false') {
|
||||||
await listWithNonPaged(ctx);
|
await listWithNonPaged(ctx);
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,7 +16,7 @@ export const useBlockResource = () => {
|
|||||||
return useContext(BlockResourceContext);
|
return useContext(BlockResourceContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface UseReousrceProps {
|
interface UseResourceProps {
|
||||||
resource: any;
|
resource: any;
|
||||||
association?: any;
|
association?: any;
|
||||||
useSourceId?: any;
|
useSourceId?: any;
|
||||||
@ -33,7 +33,7 @@ const useAssociation = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const useReousrce = (props: UseReousrceProps) => {
|
const useResource = (props: UseResourceProps) => {
|
||||||
const { block, resource, useSourceId } = props;
|
const { block, resource, useSourceId } = props;
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
@ -121,7 +121,7 @@ export const useBlockRequestContext = () => {
|
|||||||
|
|
||||||
export const BlockProvider = (props) => {
|
export const BlockProvider = (props) => {
|
||||||
const { collection, association } = props;
|
const { collection, association } = props;
|
||||||
const resource = useReousrce(props);
|
const resource = useResource(props);
|
||||||
return (
|
return (
|
||||||
<MaybeCollectionProvider collection={collection}>
|
<MaybeCollectionProvider collection={collection}>
|
||||||
<BlockAssociationContext.Provider value={association}>
|
<BlockAssociationContext.Provider value={association}>
|
||||||
|
@ -4,6 +4,17 @@ import { CollectionManagerContext } from '../context';
|
|||||||
|
|
||||||
export const useCollectionManager = () => {
|
export const useCollectionManager = () => {
|
||||||
const { refreshCM, service, interfaces, collections } = useContext(CollectionManagerContext);
|
const { refreshCM, service, interfaces, collections } = useContext(CollectionManagerContext);
|
||||||
|
const getCollectionField = (name: string) => {
|
||||||
|
const [collectionName, fieldName] = name.split('.');
|
||||||
|
if (!fieldName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const collection = collections?.find((collection) => collection.name === collectionName);
|
||||||
|
if (!collection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return collection?.fields?.find((field) => field.name === fieldName);
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
service,
|
service,
|
||||||
interfaces,
|
interfaces,
|
||||||
@ -22,16 +33,19 @@ export const useCollectionManager = () => {
|
|||||||
const collection = collections?.find((collection) => collection.name === name);
|
const collection = collections?.find((collection) => collection.name === name);
|
||||||
return collection?.fields || [];
|
return collection?.fields || [];
|
||||||
},
|
},
|
||||||
getCollectionField(name: string) {
|
getCollectionField,
|
||||||
const [collectionName, fieldName] = name.split('.');
|
getCollectionJoinField(name: string) {
|
||||||
if (!fieldName) {
|
const [collectionName, ...fieldNames] = name.split('.');
|
||||||
|
if (!fieldNames?.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const collection = collections?.find((collection) => collection.name === collectionName);
|
let cName = collectionName;
|
||||||
if (!collection) {
|
return fieldNames.reduce((result, curFieldName) => {
|
||||||
return;
|
const collectionField = getCollectionField(`${cName}.${curFieldName}`);
|
||||||
}
|
cName = collectionField.target;
|
||||||
return collection?.fields?.find((field) => field.name === fieldName);
|
|
||||||
|
return collectionField;
|
||||||
|
}, null);
|
||||||
},
|
},
|
||||||
getInterface(name: string) {
|
getInterface(name: string) {
|
||||||
return interfaces[name] ? clone(interfaces[name]) : null;
|
return interfaces[name] ? clone(interfaces[name]) : null;
|
||||||
|
@ -26,4 +26,3 @@ export * from './slate';
|
|||||||
export * from './system-settings';
|
export * from './system-settings';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
export * from './workflow';
|
export * from './workflow';
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ export default {
|
|||||||
"Title": "Title",
|
"Title": "Title",
|
||||||
"Select view": "Select view",
|
"Select view": "Select view",
|
||||||
"Reset": "Reset",
|
"Reset": "Reset",
|
||||||
"Export fields": "Export fields",
|
"Exportable fields": "Exportable fields",
|
||||||
"Saved successfully": "Saved successfully",
|
"Saved successfully": "Saved successfully",
|
||||||
"Nickname": "Nickname",
|
"Nickname": "Nickname",
|
||||||
"Sign in": "Sign in",
|
"Sign in": "Sign in",
|
||||||
@ -532,8 +532,7 @@ export default {
|
|||||||
"Request success": "Request success",
|
"Request success": "Request success",
|
||||||
"Invalid JSON format": "Invalid JSON format",
|
"Invalid JSON format": "Invalid JSON format",
|
||||||
"After successful request": "After successful request",
|
"After successful request": "After successful request",
|
||||||
"Add exported field": "Add exported field",
|
"Add exportable field": "Add exportable field",
|
||||||
"Exported table header name": "Exported table header name",
|
|
||||||
"Audit logs": "Audit logs",
|
"Audit logs": "Audit logs",
|
||||||
"Record ID": "Record ID",
|
"Record ID": "Record ID",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
|
@ -262,7 +262,7 @@ export default {
|
|||||||
"Title": "标题",
|
"Title": "标题",
|
||||||
"Select view": "切换视图",
|
"Select view": "切换视图",
|
||||||
"Reset": "重置",
|
"Reset": "重置",
|
||||||
"Export fields": "导出字段",
|
"Exportable fields": "可导出字段",
|
||||||
"Saved successfully": "保存成功",
|
"Saved successfully": "保存成功",
|
||||||
"Nickname": "昵称",
|
"Nickname": "昵称",
|
||||||
"Sign in": "登录",
|
"Sign in": "登录",
|
||||||
@ -584,6 +584,8 @@ export default {
|
|||||||
'Request success': '请求成功',
|
'Request success': '请求成功',
|
||||||
'Invalid JSON format': '非法JSON格式',
|
'Invalid JSON format': '非法JSON格式',
|
||||||
'After successful request': '请求成功之后',
|
'After successful request': '请求成功之后',
|
||||||
|
'Add exportable field': '添加可导出字段',
|
||||||
|
// 'Custom column title': '自定义列标题',
|
||||||
"Audit logs": "操作记录",
|
"Audit logs": "操作记录",
|
||||||
"Record ID": "数据 ID",
|
"Record ID": "数据 ID",
|
||||||
"User": "用户",
|
"User": "用户",
|
||||||
|
@ -73,7 +73,7 @@ export const Cascader = connect(
|
|||||||
fieldNames={fieldNames}
|
fieldNames={fieldNames}
|
||||||
displayRender={displayRender}
|
displayRender={displayRender}
|
||||||
onChange={(value, selectedOptions) => {
|
onChange={(value, selectedOptions) => {
|
||||||
if (labelInValue) {
|
if (value && labelInValue) {
|
||||||
onChange(selectedOptions.map((option) => omit(option, [fieldNames.children])));
|
onChange(selectedOptions.map((option) => omit(option, [fieldNames.children])));
|
||||||
} else {
|
} else {
|
||||||
onChange(value);
|
onChange(value);
|
||||||
|
@ -15,7 +15,6 @@ export interface SchemaInitializerProviderProps {
|
|||||||
|
|
||||||
export const useSchemaInitializer = (name: string) => {
|
export const useSchemaInitializer = (name: string) => {
|
||||||
const initializers = useContext(SchemaInitializerContext);
|
const initializers = useContext(SchemaInitializerContext);
|
||||||
|
|
||||||
const render = (component?: any, props?: any) => {
|
const render = (component?: any, props?: any) => {
|
||||||
return component && React.createElement(component, props);
|
return component && React.createElement(component, props);
|
||||||
};
|
};
|
||||||
@ -46,6 +45,8 @@ export const useSchemaInitializer = (name: string) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SchemaInitializerPluginContext = createContext(null);
|
||||||
|
|
||||||
export const SchemaInitializerProvider: React.FC<SchemaInitializerProviderProps> = (props) => {
|
export const SchemaInitializerProvider: React.FC<SchemaInitializerProviderProps> = (props) => {
|
||||||
const { initializers, components, children } = props;
|
const { initializers, components, children } = props;
|
||||||
return (
|
return (
|
||||||
|
@ -476,10 +476,12 @@ SchemaSettings.ActionModalItem = React.memo((props: any) => {
|
|||||||
const { dn } = useSchemaSettings();
|
const { dn } = useSchemaSettings();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
|
|
||||||
const form = useMemo(
|
const form = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createForm({
|
createForm({
|
||||||
initialValues: cloneDeep(initialValues),
|
initialValues: cloneDeep(initialValues),
|
||||||
|
values: cloneDeep(initialValues),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -508,7 +510,7 @@ SchemaSettings.ActionModalItem = React.memo((props: any) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SchemaSettings.Item {...others} onClick={openAssignedFieldValueHandler}>
|
<SchemaSettings.Item {...others} onClick={openAssignedFieldValueHandler} onKeyDown={(e) => e.stopPropagation()}>
|
||||||
{props.children || props.title}
|
{props.children || props.title}
|
||||||
</SchemaSettings.Item>
|
</SchemaSettings.Item>
|
||||||
{createPortal(
|
{createPortal(
|
||||||
|
12
packages/core/devtools/umiConfig.d.ts
vendored
12
packages/core/devtools/umiConfig.d.ts
vendored
@ -1,18 +1,20 @@
|
|||||||
export declare function getUmiConfig(): {
|
export declare function getUmiConfig(): {
|
||||||
define: {
|
define: {
|
||||||
'process.env.API_BASE_URL': string;
|
'process.env.API_BASE_URL': string;
|
||||||
};
|
};
|
||||||
proxy: {
|
proxy: {
|
||||||
[x: string]: {
|
[x: string]:
|
||||||
|
| {
|
||||||
target: string;
|
target: string;
|
||||||
changeOrigin: boolean;
|
changeOrigin: boolean;
|
||||||
} | {
|
}
|
||||||
|
| {
|
||||||
target: string;
|
target: string;
|
||||||
changeOrigin: boolean;
|
changeOrigin: boolean;
|
||||||
pathRewrite: {
|
pathRewrite: {
|
||||||
[x: string]: string;
|
[x: string]: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Resourcer from './resourcer';
|
|
||||||
import Middleware, { MiddlewareType } from './middleware';
|
|
||||||
import Action, { ActionName, ActionType } from './action';
|
import Action, { ActionName, ActionType } from './action';
|
||||||
|
import Middleware, { MiddlewareType } from './middleware';
|
||||||
|
import Resourcer from './resourcer';
|
||||||
|
|
||||||
export type ResourceType = 'single' | 'hasOne' | 'hasMany' | 'belongsTo' | 'belongsToMany';
|
export type ResourceType = 'single' | 'hasOne' | 'hasMany' | 'belongsTo' | 'belongsToMany';
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default } from './plugin';
|
export { default } from './server';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { mockServer } from '@nocobase/test';
|
import { mockServer } from '@nocobase/test';
|
||||||
import CollectionManagerPlugin from '../plugin';
|
import CollectionManagerPlugin from '../server';
|
||||||
|
|
||||||
describe('collections repository', () => {
|
describe('collections repository', () => {
|
||||||
it('case 1', async () => {
|
it('case 1', async () => {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export { default } from './plugin';
|
|
||||||
export * from './repositories';
|
export * from './repositories';
|
||||||
|
export { default } from './server';
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export class FieldModel extends MagicAttributeModel {
|
|||||||
const uiSchema = await UISchema.findByPk(options.uiSchemaUid, {
|
const uiSchema = await UISchema.findByPk(options.uiSchemaUid, {
|
||||||
transaction: loadOptions.transaction,
|
transaction: loadOptions.transaction,
|
||||||
});
|
});
|
||||||
return collection.setField(name, { ...options, uiSchema });
|
return collection.setField(name, { ...options, uiSchema: uiSchema.get() });
|
||||||
} else {
|
} else {
|
||||||
return collection.setField(name, options);
|
return collection.setField(name, options);
|
||||||
}
|
}
|
||||||
|
2
packages/plugins/export/client.d.ts
vendored
Normal file
2
packages/plugins/export/client.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
export * from './lib/client';
|
12
packages/plugins/export/client.js
Normal file
12
packages/plugins/export/client.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
var _useExportClient = require("./lib/client");
|
||||||
|
|
||||||
|
Object.keys(_useExportClient).forEach(function (key) {
|
||||||
|
if (key === "default" || key === "__esModule") return;
|
||||||
|
if (key in exports && exports[key] === _useExportClient[key]) return;
|
||||||
|
Object.defineProperty(exports, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get: function get() {
|
||||||
|
return _useExportClient[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
18
packages/plugins/export/package.json
Normal file
18
packages/plugins/export/package.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "@nocobase/plugin-export",
|
||||||
|
"version": "0.7.0-alpha.83",
|
||||||
|
"main": "lib/server/index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"node-xlsx": "^0.16.1",
|
||||||
|
"@nocobase/client": "0.7.0-alpha.83",
|
||||||
|
"@nocobase/server": "0.7.0-alpha.83"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node-xlsx": "^0.15.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@nocobase/server": "*",
|
||||||
|
"@nocobase/client": "*",
|
||||||
|
"@nocobase/test": "*"
|
||||||
|
}
|
||||||
|
}
|
2
packages/plugins/export/server.d.ts
vendored
Normal file
2
packages/plugins/export/server.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
export * from './lib/server';
|
12
packages/plugins/export/server.js
Normal file
12
packages/plugins/export/server.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
var _useExportServer = require("./lib/server");
|
||||||
|
|
||||||
|
Object.keys(_useExportServer).forEach(function (key) {
|
||||||
|
if (key === "default" || key === "__esModule") return;
|
||||||
|
if (key in exports && exports[key] === _useExportServer[key]) return;
|
||||||
|
Object.defineProperty(exports, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get: function get() {
|
||||||
|
return _useExportServer[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,94 @@
|
|||||||
|
import { Schema, useFieldSchema } from '@formily/react';
|
||||||
|
import { merge } from '@formily/shared';
|
||||||
|
import { SchemaInitializer, useCollection, useCompile, useDesignable } from '@nocobase/client';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import React from 'react';
|
||||||
|
import { useFields } from './useFields';
|
||||||
|
|
||||||
|
const findSchema = (schema: Schema, key: string, action: string) => {
|
||||||
|
return schema.reduceProperties((buf, s) => {
|
||||||
|
if (s[key] === action) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
const c = findSchema(s, key, action);
|
||||||
|
if (c) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const removeSchema = (schema, cb) => {
|
||||||
|
return cb(schema);
|
||||||
|
};
|
||||||
|
export const useCurrentSchema = (action: string, key: string, find = findSchema, rm = removeSchema) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { remove } = useDesignable();
|
||||||
|
const schema = find(fieldSchema, key, action);
|
||||||
|
return {
|
||||||
|
schema,
|
||||||
|
exists: !!schema,
|
||||||
|
remove() {
|
||||||
|
schema && rm(schema, remove);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const initExportSettings = (fields) => {
|
||||||
|
const exportSettings = [];
|
||||||
|
const generateDataIndex = (di, preFields, fNodes: any[]) => {
|
||||||
|
let child = cloneDeep(preFields);
|
||||||
|
fNodes.reduce((buf, cur) => {
|
||||||
|
if (cur.children) {
|
||||||
|
const childDI = [];
|
||||||
|
preFields.dataIndex.push(cur.name);
|
||||||
|
generateDataIndex(childDI, cloneDeep(preFields), cur.children);
|
||||||
|
preFields.dataIndex.pop();
|
||||||
|
di.push(...childDI);
|
||||||
|
} else {
|
||||||
|
child.dataIndex.push(cur.name);
|
||||||
|
di.push(child);
|
||||||
|
child = cloneDeep(preFields);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
generateDataIndex(exportSettings, { dataIndex: [] }, fields);
|
||||||
|
return exportSettings;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ExportActionInitializer = (props) => {
|
||||||
|
const { item, insert } = props;
|
||||||
|
const { exists, remove } = useCurrentSchema('export', 'x-action', item.find, item.remove);
|
||||||
|
const compile = useCompile();
|
||||||
|
const { name } = useCollection();
|
||||||
|
const fields = useFields(name);
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Export") }}',
|
||||||
|
'x-action': 'export',
|
||||||
|
'x-action-settings': {
|
||||||
|
exportSettings: [],
|
||||||
|
},
|
||||||
|
'x-designer': 'ExportDesigner',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
icon: 'clouddownloadoutlined',
|
||||||
|
useProps: '{{ useExportAction }}',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.SwitchItem
|
||||||
|
checked={exists}
|
||||||
|
title={item.title}
|
||||||
|
onClick={() => {
|
||||||
|
if (exists) {
|
||||||
|
return remove();
|
||||||
|
}
|
||||||
|
schema['x-action-settings']['exportSettings'] = initExportSettings(fields);
|
||||||
|
const s = merge(schema || {}, item.schema || {});
|
||||||
|
item?.schemaInitialize?.(s);
|
||||||
|
insert(s);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
122
packages/plugins/export/src/client/ExportDesigner.tsx
Normal file
122
packages/plugins/export/src/client/ExportDesigner.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import { ArrayItems } from '@formily/antd';
|
||||||
|
import type { ISchema } from '@formily/react';
|
||||||
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { GeneralSchemaDesigner, SchemaSettings, useDesignable } from '@nocobase/client';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useShared } from './useShared';
|
||||||
|
|
||||||
|
export const ExportDesigner = () => {
|
||||||
|
const field = useField();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
const [schema, setSchema] = useState<ISchema>();
|
||||||
|
const { schema: pageSchema } = useShared();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSchema(pageSchema);
|
||||||
|
}, [field.address, fieldSchema?.['x-action-settings']?.['exportSettings']]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GeneralSchemaDesigner disableInitializer>
|
||||||
|
<SchemaSettings.ModalItem
|
||||||
|
title={t('Edit button')}
|
||||||
|
schema={
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
title: t('Edit button'),
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
title: t('Button title'),
|
||||||
|
default: fieldSchema.title,
|
||||||
|
'x-component-props': {},
|
||||||
|
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
title: t('Button icon'),
|
||||||
|
default: fieldSchema?.['x-component-props']?.icon,
|
||||||
|
'x-component-props': {},
|
||||||
|
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Radio.Group',
|
||||||
|
title: t('Button background color'),
|
||||||
|
default: fieldSchema?.['x-component-props']?.danger
|
||||||
|
? 'danger'
|
||||||
|
: fieldSchema?.['x-component-props']?.type === 'primary'
|
||||||
|
? 'primary'
|
||||||
|
: 'default',
|
||||||
|
enum: [
|
||||||
|
{ value: 'default', label: '{{t("Default")}}' },
|
||||||
|
{ value: 'primary', label: '{{t("Highlight")}}' },
|
||||||
|
{ value: 'danger', label: '{{t("Danger red")}}' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ISchema
|
||||||
|
}
|
||||||
|
onSubmit={({ title, icon, type }) => {
|
||||||
|
if (title) {
|
||||||
|
fieldSchema.title = title;
|
||||||
|
field.title = title;
|
||||||
|
field.componentProps.icon = icon;
|
||||||
|
field.componentProps.danger = type === 'danger';
|
||||||
|
field.componentProps.type = type;
|
||||||
|
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
||||||
|
fieldSchema['x-component-props'].icon = icon;
|
||||||
|
fieldSchema['x-component-props'].danger = type === 'danger';
|
||||||
|
fieldSchema['x-component-props'].type = type;
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
title,
|
||||||
|
'x-component-props': {
|
||||||
|
...fieldSchema['x-component-props'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dn.refresh();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SchemaSettings.ActionModalItem
|
||||||
|
title={t('Exportable fields')}
|
||||||
|
schema={schema}
|
||||||
|
initialValues={{ exportSettings: fieldSchema?.['x-action-settings']?.exportSettings }}
|
||||||
|
components={{ ArrayItems }}
|
||||||
|
onSubmit={({ exportSettings }) => {
|
||||||
|
fieldSchema['x-action-settings']['exportSettings'] = exportSettings
|
||||||
|
?.filter((fieldItem) => fieldItem?.dataIndex?.length)
|
||||||
|
.map((item) => ({
|
||||||
|
dataIndex: item.dataIndex.map((di) => di.name ?? di),
|
||||||
|
title: item.title,
|
||||||
|
}));
|
||||||
|
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-action-settings': fieldSchema['x-action-settings'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dn.refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SchemaSettings.Divider />
|
||||||
|
<SchemaSettings.Remove
|
||||||
|
removeParentsIfNoChildren
|
||||||
|
breakRemoveOn={(s) => {
|
||||||
|
return s['x-component'] === 'Space' || s['x-component'].endsWith('ActionBar');
|
||||||
|
}}
|
||||||
|
confirm={{
|
||||||
|
title: t('Delete action'),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</GeneralSchemaDesigner>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,23 @@
|
|||||||
|
import { SchemaInitializerContext } from '@nocobase/client';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
export const ExportInitializerProvider = (props: any) => {
|
||||||
|
const initializes = useContext(SchemaInitializerContext);
|
||||||
|
const hasExportAction = initializes.TableActionInitializers.items[0].children.some(
|
||||||
|
(initialize) => initialize.component === 'ExportActionInitializer',
|
||||||
|
);
|
||||||
|
!hasExportAction &&
|
||||||
|
initializes.TableActionInitializers.items[0].children.push({
|
||||||
|
type: 'item',
|
||||||
|
title: "{{t('Export')}}",
|
||||||
|
component: 'ExportActionInitializer',
|
||||||
|
schema: {
|
||||||
|
'x-align': 'right',
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
|
'x-acl-action-props': {
|
||||||
|
skipScopeCheck: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return props.children;
|
||||||
|
};
|
11
packages/plugins/export/src/client/ExportPluginProvider.tsx
Normal file
11
packages/plugins/export/src/client/ExportPluginProvider.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SchemaComponentOptions } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { ExportActionInitializer, ExportDesigner, ExportInitializerProvider, useExportAction } from './';
|
||||||
|
|
||||||
|
export const ExportPluginProvider = (props: any) => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentOptions components={{ ExportActionInitializer, ExportDesigner }} scope={{ useExportAction }}>
|
||||||
|
<ExportInitializerProvider>{props.children}</ExportInitializerProvider>
|
||||||
|
</SchemaComponentOptions>
|
||||||
|
);
|
||||||
|
};
|
6
packages/plugins/export/src/client/index.ts
Normal file
6
packages/plugins/export/src/client/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './ExportActionInitializer';
|
||||||
|
export * from './ExportDesigner';
|
||||||
|
export * from './ExportInitializerProvider';
|
||||||
|
export * from './ExportPluginProvider';
|
||||||
|
export * from './useExportAction';
|
||||||
|
|
56
packages/plugins/export/src/client/useExportAction.ts
Normal file
56
packages/plugins/export/src/client/useExportAction.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
useAPIClient,
|
||||||
|
useBlockRequestContext,
|
||||||
|
useCollection,
|
||||||
|
useCollectionManager,
|
||||||
|
useCompile,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export const useExportAction = () => {
|
||||||
|
const { service } = useBlockRequestContext();
|
||||||
|
const apiClient = useAPIClient();
|
||||||
|
const actionSchema = useFieldSchema();
|
||||||
|
const compile = useCompile();
|
||||||
|
const { getCollectionJoinField } = useCollectionManager();
|
||||||
|
const { name, title, getField } = useCollection();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return {
|
||||||
|
async onClick() {
|
||||||
|
const { exportSettings } = actionSchema?.['x-action-settings'] ?? {};
|
||||||
|
exportSettings.forEach((es) => {
|
||||||
|
const { uiSchema } = getCollectionJoinField(`${name}.${es.dataIndex.join('.')}`) ?? {};
|
||||||
|
es.enum = uiSchema?.enum?.map((e) => ({ value: e.value, label: e.label }));
|
||||||
|
if (!es.enum && uiSchema.type === 'boolean') {
|
||||||
|
es.enum = [
|
||||||
|
{ value: true, label: t('Yes') },
|
||||||
|
{ value: false, label: t('No') },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
es.defaultTitle = uiSchema?.title;
|
||||||
|
});
|
||||||
|
const { data } = await apiClient.request({
|
||||||
|
url: `/${name}:exportXlsx`,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
params: {
|
||||||
|
title: compile(title),
|
||||||
|
columns: JSON.stringify(compile(exportSettings)),
|
||||||
|
appends: service.params[0]?.appends?.join(),
|
||||||
|
filter: JSON.stringify(service.params[0]?.filter),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let blob = new Blob([data], { type: 'application/x-xls' });
|
||||||
|
const a = document.createElement('a');
|
||||||
|
const blobUrl = window.URL.createObjectURL(blob);
|
||||||
|
a.download = `${compile(title)}.xlsx`;
|
||||||
|
a.href = blobUrl;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
document.body.removeChild(a);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
58
packages/plugins/export/src/client/useFields.ts
Normal file
58
packages/plugins/export/src/client/useFields.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { useCollectionManager } from '@nocobase/client';
|
||||||
|
|
||||||
|
export const useFields = (collectionName: string) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const nonfilterable = fieldSchema?.['x-component-props']?.nonfilterable || [];
|
||||||
|
const { getCollectionFields, getInterface } = useCollectionManager();
|
||||||
|
const fields = getCollectionFields(collectionName);
|
||||||
|
const field2option = (field, depth) => {
|
||||||
|
if (nonfilterable.length && depth === 1 && nonfilterable.includes(field.name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!field.interface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fieldInterface = getInterface(field.interface);
|
||||||
|
if (!fieldInterface.filterable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { nested, children, operators } = fieldInterface.filterable;
|
||||||
|
const option = {
|
||||||
|
name: field.name,
|
||||||
|
title: field?.uiSchema?.title || field.name,
|
||||||
|
schema: field?.uiSchema,
|
||||||
|
operators:
|
||||||
|
operators?.filter?.((operator) => {
|
||||||
|
return !operator?.visible || operator.visible(field);
|
||||||
|
}) || [],
|
||||||
|
};
|
||||||
|
if (field.target && depth > 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (depth > 2) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
if (children?.length) {
|
||||||
|
option['children'] = children;
|
||||||
|
}
|
||||||
|
if (nested) {
|
||||||
|
const targetFields = getCollectionFields(field.target);
|
||||||
|
const options = getOptions(targetFields, depth + 1).filter(Boolean);
|
||||||
|
option['children'] = option['children'] || [];
|
||||||
|
option['children'].push(...options);
|
||||||
|
}
|
||||||
|
return option;
|
||||||
|
};
|
||||||
|
const getOptions = (fields, depth) => {
|
||||||
|
const options = [];
|
||||||
|
fields.forEach((field) => {
|
||||||
|
const option = field2option(field, depth);
|
||||||
|
if (option) {
|
||||||
|
options.push(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
return getOptions(fields, 1);
|
||||||
|
};
|
92
packages/plugins/export/src/client/useShared.ts
Normal file
92
packages/plugins/export/src/client/useShared.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { useCollection } from '@nocobase/client';
|
||||||
|
import { useFields } from './useFields';
|
||||||
|
|
||||||
|
export const useShared = () => {
|
||||||
|
const { name } = useCollection();
|
||||||
|
const fields = useFields(name);
|
||||||
|
return {
|
||||||
|
schema: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
properties: {
|
||||||
|
exportSettings: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'ArrayItems',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
space: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
className: css`
|
||||||
|
width: 100%;
|
||||||
|
& .ant-space-item:nth-child(2),
|
||||||
|
& .ant-space-item:nth-child(3) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayItems.SortHandle',
|
||||||
|
},
|
||||||
|
dataIndex: {
|
||||||
|
type: 'array',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Cascader',
|
||||||
|
required: true,
|
||||||
|
enum: fields,
|
||||||
|
'x-component-props': {
|
||||||
|
fieldNames: {
|
||||||
|
label: 'title',
|
||||||
|
value: 'name',
|
||||||
|
children: 'children',
|
||||||
|
},
|
||||||
|
// labelInValue: true,
|
||||||
|
changeOnSelect: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
placeholder: '{{ t("Custom column title") }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayItems.Remove',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Add exportable field") }}',
|
||||||
|
'x-component': 'ArrayItems.Addition',
|
||||||
|
'x-component-props': {
|
||||||
|
className: css`
|
||||||
|
border-color: rgb(241, 139, 98);
|
||||||
|
color: rgb(241, 139, 98);
|
||||||
|
&.ant-btn-dashed:hover {
|
||||||
|
border-color: rgb(241, 139, 98);
|
||||||
|
color: rgb(241, 139, 98);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
1
packages/plugins/export/src/index.ts
Normal file
1
packages/plugins/export/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './server';
|
0
packages/plugins/export/src/server/actions/.gitkeep
Normal file
0
packages/plugins/export/src/server/actions/.gitkeep
Normal file
51
packages/plugins/export/src/server/actions/export-xlsx.ts
Normal file
51
packages/plugins/export/src/server/actions/export-xlsx.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Context, Next } from '@nocobase/actions';
|
||||||
|
import { Collection, Repository } from '@nocobase/database';
|
||||||
|
import xlsx from 'node-xlsx';
|
||||||
|
import render from '../renders';
|
||||||
|
|
||||||
|
export async function exportXlsx(ctx: Context, next: Next) {
|
||||||
|
let { title, columns, associatedName, associatedIndex, resourceName, filter, fields, appends, except } =
|
||||||
|
ctx.action.params;
|
||||||
|
if (typeof columns === 'string') {
|
||||||
|
columns = JSON.parse(columns);
|
||||||
|
}
|
||||||
|
columns = columns?.filter((col) => col?.dataIndex?.length > 0);
|
||||||
|
|
||||||
|
let repository: Repository;
|
||||||
|
let collection: Collection;
|
||||||
|
|
||||||
|
if (associatedName && associatedIndex) {
|
||||||
|
const associated = ctx.db.getCollection(associatedName);
|
||||||
|
const resourceField = associated.getField(resourceName);
|
||||||
|
collection = ctx.db.getCollection(resourceField.target);
|
||||||
|
repository = associated.repository.relation(resourceName).of(associatedIndex) as any;
|
||||||
|
} else {
|
||||||
|
collection = ctx.db.getCollection(resourceName);
|
||||||
|
repository = collection.repository;
|
||||||
|
}
|
||||||
|
const data = await repository.find({
|
||||||
|
filter,
|
||||||
|
fields,
|
||||||
|
appends,
|
||||||
|
except,
|
||||||
|
});
|
||||||
|
const collectionFields = columns.map((col) => collection.fields.get(col.dataIndex[0]));
|
||||||
|
const { rows, ranges } = await render({ columns, fields: collectionFields, data }, ctx);
|
||||||
|
ctx.body = xlsx.build([
|
||||||
|
{
|
||||||
|
name: title,
|
||||||
|
data: rows,
|
||||||
|
options: {
|
||||||
|
'!merges': ranges,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.set({
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
// to avoid "invalid character" error in header (RFC)
|
||||||
|
'Content-Disposition': `attachment; filename=${encodeURI(title)}.xlsx`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await next();
|
||||||
|
}
|
1
packages/plugins/export/src/server/actions/index.ts
Normal file
1
packages/plugins/export/src/server/actions/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './export-xlsx';
|
26
packages/plugins/export/src/server/index.ts
Normal file
26
packages/plugins/export/src/server/index.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { InstallOptions, Plugin } from '@nocobase/server';
|
||||||
|
import { exportXlsx } from './actions';
|
||||||
|
|
||||||
|
export class ExportPlugin extends Plugin {
|
||||||
|
getName(): string {
|
||||||
|
return this.getPackageName(__dirname);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeLoad() {}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
// Visit: http://localhost:13000/api/xxx:exportXlsx
|
||||||
|
this.app.resourcer.registerActionHandler('exportXlsx', exportXlsx);
|
||||||
|
// this.app.resource({
|
||||||
|
// name: 'export',
|
||||||
|
// actions: {
|
||||||
|
// xlsx: exportXlsx,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
this.app.acl.allow('*', 'exportXlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
async install(options: InstallOptions) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExportPlugin;
|
0
packages/plugins/export/src/server/models/.gitkeep
Normal file
0
packages/plugins/export/src/server/models/.gitkeep
Normal file
173
packages/plugins/export/src/server/renders/index.ts
Normal file
173
packages/plugins/export/src/server/renders/index.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import * as renders from './renders';
|
||||||
|
|
||||||
|
function getInterfaceRender(name: string): Function {
|
||||||
|
return renders[name] || renders._;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHeader(params, ctx) {
|
||||||
|
const { columns, fields, headers = [], rowIndex = 0 } = params;
|
||||||
|
|
||||||
|
let { colIndex = 0 } = params;
|
||||||
|
|
||||||
|
if (!headers[rowIndex]) {
|
||||||
|
headers.push([]);
|
||||||
|
}
|
||||||
|
const row = headers[rowIndex];
|
||||||
|
fields.forEach((field, i) => {
|
||||||
|
const nextColIndex = colIndex + i;
|
||||||
|
row.push({
|
||||||
|
column: columns[i],
|
||||||
|
field,
|
||||||
|
rowIndex,
|
||||||
|
colIndex: nextColIndex,
|
||||||
|
});
|
||||||
|
// if (field.interface === 'subTable') {
|
||||||
|
// const subTable = ctx.db.getTable(field.target);
|
||||||
|
// const subFields = subTable.getOptions().fields.filter((field) => Boolean(field.__index));
|
||||||
|
// renderHeader(
|
||||||
|
// {
|
||||||
|
// fields: subFields,
|
||||||
|
// headers,
|
||||||
|
// rowIndex: rowIndex + 1,
|
||||||
|
// colIndex: nextColIndex,
|
||||||
|
// },
|
||||||
|
// ctx,
|
||||||
|
// );
|
||||||
|
// colIndex += subFields.length;
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(params, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderRows({ columns, fields, data }, ctx) {
|
||||||
|
return await data.reduce(async (preResult, row) => {
|
||||||
|
const result = await preResult;
|
||||||
|
const thisRow = [];
|
||||||
|
const rowIndex = 0;
|
||||||
|
let colOffset = 0;
|
||||||
|
for (let i = 0, iLen = fields.length; i < iLen; i++) {
|
||||||
|
const field = fields[i];
|
||||||
|
|
||||||
|
if (!thisRow[rowIndex]) {
|
||||||
|
thisRow.push([]);
|
||||||
|
}
|
||||||
|
const cells = thisRow[rowIndex];
|
||||||
|
if (field.options.interface !== 'subTable') {
|
||||||
|
const render = getInterfaceRender(field.options.interface);
|
||||||
|
const value = await render(field, row, ctx, columns[i]);
|
||||||
|
cells.push({
|
||||||
|
value,
|
||||||
|
rowIndex: result.length + rowIndex,
|
||||||
|
colIndex: i + colOffset,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const subTable = ctx.db.getTable(field.target);
|
||||||
|
const subFields = subTable.getOptions().fields.filter((item) => Boolean(item.__index));
|
||||||
|
//TODO: must provide sub-table columns
|
||||||
|
const subTableColumns = [];
|
||||||
|
const subRows = await renderRows(
|
||||||
|
{ columns: subTableColumns, fields: subFields, data: row.get(field.name) || [] },
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
|
||||||
|
// const { rows: subRowGroups } = subTableRows;
|
||||||
|
subRows.forEach((cells, j) => {
|
||||||
|
const subRowIndex = rowIndex + j;
|
||||||
|
if (!thisRow[subRowIndex]) {
|
||||||
|
thisRow.push([]);
|
||||||
|
}
|
||||||
|
const subCells = thisRow[subRowIndex];
|
||||||
|
subCells.push(
|
||||||
|
...cells.map((cell) => ({
|
||||||
|
...cell,
|
||||||
|
rowIndex: result.length + subRowIndex,
|
||||||
|
colIndex: cell.colIndex + i,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
colOffset += subFields.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thisRow.forEach((cells) => {
|
||||||
|
cells.forEach((cell) => {
|
||||||
|
const relRowIndex = cell.rowIndex - result.length;
|
||||||
|
Object.assign(cell, {
|
||||||
|
rowSpan:
|
||||||
|
relRowIndex >= thisRow.length - 1 ||
|
||||||
|
thisRow[relRowIndex + 1].find((item) => item.colIndex === cell.colIndex)
|
||||||
|
? 1
|
||||||
|
: thisRow.length - relRowIndex,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.concat(thisRow);
|
||||||
|
}, Promise.resolve([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function ({ columns, fields, data }, ctx) {
|
||||||
|
const headers = [];
|
||||||
|
renderHeader({ columns, fields, headers }, ctx);
|
||||||
|
const ranges = [];
|
||||||
|
// 计算全表最大的列索引(由于无论如何最大列都是单个单元格,所以等价于长度)
|
||||||
|
const maxColIndex = Math.max(...headers.map((row) => row[row.length - 1].colIndex));
|
||||||
|
// 遍历所有单元格,计算需要合并的坐标范围
|
||||||
|
headers.forEach((row, rowIndex) => {
|
||||||
|
row.forEach((cell, cellIndex) => {
|
||||||
|
// 跨行合并的行数为
|
||||||
|
cell.rowSpan =
|
||||||
|
cell.rowIndex >= headers.length - 1 ||
|
||||||
|
headers[cell.rowIndex + 1].find((item) => item.colIndex === cell.colIndex)
|
||||||
|
? 1
|
||||||
|
: headers.length - cell.rowIndex;
|
||||||
|
|
||||||
|
const nextCell = headers
|
||||||
|
.slice(0, rowIndex + 1)
|
||||||
|
.map((r) => r.find((item) => item.colIndex > cell.colIndex))
|
||||||
|
.filter((c) => Boolean(c))
|
||||||
|
.reduce((min, c) => (min && Math.min(min.colIndex, c.colIndex) === min.colIndex ? min : c), null);
|
||||||
|
cell.colSpan = nextCell ? nextCell.colIndex - cell.colIndex : maxColIndex - cell.colIndex + 1;
|
||||||
|
|
||||||
|
if (cell.rowSpan > 1 || cell.colSpan > 1) {
|
||||||
|
ranges.push({
|
||||||
|
s: { c: cell.colIndex, r: cell.rowIndex },
|
||||||
|
e: { c: cell.colIndex + cell.colSpan - 1, r: cell.rowIndex + cell.rowSpan - 1 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = (await renderRows({ columns, fields, data }, ctx)).map((row) => {
|
||||||
|
const cells = Array(maxColIndex).fill(null);
|
||||||
|
row.forEach((cell) => {
|
||||||
|
cells.splice(cell.colIndex, 1, cell.value);
|
||||||
|
if (cell.rowSpan > 1) {
|
||||||
|
ranges.push({
|
||||||
|
s: { c: cell.colIndex, r: cell.rowIndex + headers.length },
|
||||||
|
e: { c: cell.colIndex, r: cell.rowIndex + cell.rowSpan - 1 + headers.length },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return cells;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
rows: [
|
||||||
|
...headers.map((row) => {
|
||||||
|
// 补齐无数据单元格,以供合并
|
||||||
|
const cells = Array(maxColIndex).fill(null);
|
||||||
|
row.forEach((cell) =>
|
||||||
|
cells.splice(
|
||||||
|
cell.colIndex,
|
||||||
|
1,
|
||||||
|
cell.column.title ?? cell.column.defaultTitle ?? cell.column.dataIndex[cell.column.dataIndex.length - 1],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return cells;
|
||||||
|
}),
|
||||||
|
...rows,
|
||||||
|
],
|
||||||
|
ranges,
|
||||||
|
};
|
||||||
|
}
|
98
packages/plugins/export/src/server/renders/renders.ts
Normal file
98
packages/plugins/export/src/server/renders/renders.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
export async function _(field, row, ctx, column?: any) {
|
||||||
|
if (column?.dataIndex.length > 1) {
|
||||||
|
return column.dataIndex.reduce((result, col) => {
|
||||||
|
return result?.[col];
|
||||||
|
}, row);
|
||||||
|
} else {
|
||||||
|
return row.get(field.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function datetime(field, row, ctx) {
|
||||||
|
const value = row.get(field.name);
|
||||||
|
return moment(value).format(field.showTime ? `${field.dateFormat} ${field.timeFormat}` : field.dateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function percent(field, row, ctx) {
|
||||||
|
const value = row.get(field.name);
|
||||||
|
return value && `${value}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function boolean(field, row, ctx, column?: any) {
|
||||||
|
const value = row.get(field.name);
|
||||||
|
let { enum: enumData } = column ?? {};
|
||||||
|
if (enumData?.length > 0) {
|
||||||
|
const option = enumData.find((item) => item.value === value);
|
||||||
|
return option?.label;
|
||||||
|
} else {
|
||||||
|
// FIXME: i18n
|
||||||
|
return value ? '是' : value === null || value === undefined ? '' : '否';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checkbox = boolean;
|
||||||
|
|
||||||
|
export async function select(field, row, ctx, column?: any) {
|
||||||
|
const value = row.get(field.name);
|
||||||
|
let { enum: enumData } = column ?? {};
|
||||||
|
if (!enumData) {
|
||||||
|
const repository = ctx.db.getCollection('uiSchemas').repository;
|
||||||
|
const model = await repository.findById(field.options.uiSchemaUid);
|
||||||
|
enumData = model.get('enum');
|
||||||
|
}
|
||||||
|
const option = enumData.find((item) => item.value === value);
|
||||||
|
return option?.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function multipleSelect(field, row, ctx, column?: any) {
|
||||||
|
const values = row.get(field.name);
|
||||||
|
let { enum: enumData } = column ?? {};
|
||||||
|
if (!enumData) {
|
||||||
|
const repository = ctx.db.getCollection('uiSchemas').repository;
|
||||||
|
const model = await repository.findById(field.options.uiSchemaUid);
|
||||||
|
enumData = model.get('enum');
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
?.map((value) => {
|
||||||
|
const option = enumData.find((item) => item.value === value);
|
||||||
|
return option?.label;
|
||||||
|
})
|
||||||
|
?.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const radio = select;
|
||||||
|
|
||||||
|
export const radioGroup = select;
|
||||||
|
|
||||||
|
export const checkboxes = multipleSelect;
|
||||||
|
|
||||||
|
export const checkboxGroup = multipleSelect;
|
||||||
|
|
||||||
|
export async function subTable(field, row, ctx) {
|
||||||
|
// TODO: need title field to be defined
|
||||||
|
return (row.get(field.name) || []).map((item) => item[field.sourceKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function linkTo(field, row, ctx, column?: any) {
|
||||||
|
return (row.get(field.name) || []).map((item) => {
|
||||||
|
return column.dataIndex.reduce((buf, cur) => {
|
||||||
|
buf = item[cur];
|
||||||
|
return buf;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// return (row.get(field.name) || []).map((item) => item[field.labelField]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function attachment(field, row, ctx) {
|
||||||
|
return (row.get(field.name) || []).map((item) => item[field.url]).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function chinaRegion(field, row, ctx) {
|
||||||
|
const value = row.get(field.name);
|
||||||
|
const values = (Array.isArray(value) ? value : [value]).sort((a, b) =>
|
||||||
|
a.level !== b.level ? a.level - b.level : a.sort - b.sort,
|
||||||
|
);
|
||||||
|
return values.map((item) => item.name).join('/');
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
export { default } from './plugin';
|
export { default } from './server';
|
||||||
|
@ -1,105 +1 @@
|
|||||||
import { skip } from '@nocobase/acl';
|
export { default } from './server';
|
||||||
import { MagicAttributeModel } from '@nocobase/database';
|
|
||||||
import { Plugin } from '@nocobase/server';
|
|
||||||
import { resolve } from 'path';
|
|
||||||
import { getAccessible } from './actions/getAccessible';
|
|
||||||
|
|
||||||
export class UiRoutesStoragePlugin extends Plugin {
|
|
||||||
getName(): string {
|
|
||||||
return this.getPackageName(__dirname);
|
|
||||||
}
|
|
||||||
|
|
||||||
async install() {
|
|
||||||
const repository = this.app.db.getRepository('uiRoutes');
|
|
||||||
const routes = [
|
|
||||||
{
|
|
||||||
type: 'redirect',
|
|
||||||
from: '/',
|
|
||||||
to: '/admin',
|
|
||||||
exact: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'route',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Menu',
|
|
||||||
'x-designer': 'Menu.Designer',
|
|
||||||
'x-initializer': 'MenuItemInitializers',
|
|
||||||
'x-component-props': {
|
|
||||||
mode: 'mix',
|
|
||||||
theme: 'dark',
|
|
||||||
// defaultSelectedUid: 'u8',
|
|
||||||
onSelect: '{{ onSelect }}',
|
|
||||||
sideMenuRefScopeKey: 'sideMenuRef',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
path: '/admin/:name(.+)?',
|
|
||||||
component: 'AdminLayout',
|
|
||||||
title: 'NocoBase Admin',
|
|
||||||
routes: [
|
|
||||||
// test...
|
|
||||||
// {
|
|
||||||
// type: 'route',
|
|
||||||
// path: '/admin/workflows/:id',
|
|
||||||
// component: 'WorkflowPage',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'route',
|
|
||||||
// path: '/admin/block-templates/:key',
|
|
||||||
// component: 'BlockTemplateDetails',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'route',
|
|
||||||
// path: '/admin/block-templates',
|
|
||||||
// component: 'BlockTemplatePage',
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
type: 'route',
|
|
||||||
path: '/admin/:name(.+)?',
|
|
||||||
component: 'RouteSchemaComponent',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'route',
|
|
||||||
component: 'AuthLayout',
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
type: 'route',
|
|
||||||
path: '/signin',
|
|
||||||
component: 'SigninPage',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'route',
|
|
||||||
path: '/signup',
|
|
||||||
component: 'SignupPage',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (const values of routes) {
|
|
||||||
await repository.create({
|
|
||||||
values,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async load() {
|
|
||||||
this.app.resourcer.registerActionHandler('uiRoutes:getAccessible', getAccessible);
|
|
||||||
this.app.db.registerModels({ MagicAttributeModel });
|
|
||||||
|
|
||||||
await this.app.db.import({
|
|
||||||
directory: resolve(__dirname, 'collections'),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.app.acl.use(
|
|
||||||
skip({
|
|
||||||
resourceName: 'uiRoutes',
|
|
||||||
actionName: 'getAccessible',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UiRoutesStoragePlugin;
|
|
||||||
|
105
packages/plugins/ui-routes-storage/src/server.ts
Normal file
105
packages/plugins/ui-routes-storage/src/server.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { skip } from '@nocobase/acl';
|
||||||
|
import { MagicAttributeModel } from '@nocobase/database';
|
||||||
|
import { Plugin } from '@nocobase/server';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { getAccessible } from './actions/getAccessible';
|
||||||
|
|
||||||
|
export class UiRoutesStoragePlugin extends Plugin {
|
||||||
|
getName(): string {
|
||||||
|
return this.getPackageName(__dirname);
|
||||||
|
}
|
||||||
|
|
||||||
|
async install() {
|
||||||
|
const repository = this.app.db.getRepository('uiRoutes');
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
type: 'redirect',
|
||||||
|
from: '/',
|
||||||
|
to: '/admin',
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'route',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Menu',
|
||||||
|
'x-designer': 'Menu.Designer',
|
||||||
|
'x-initializer': 'MenuItemInitializers',
|
||||||
|
'x-component-props': {
|
||||||
|
mode: 'mix',
|
||||||
|
theme: 'dark',
|
||||||
|
// defaultSelectedUid: 'u8',
|
||||||
|
onSelect: '{{ onSelect }}',
|
||||||
|
sideMenuRefScopeKey: 'sideMenuRef',
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
path: '/admin/:name(.+)?',
|
||||||
|
component: 'AdminLayout',
|
||||||
|
title: 'NocoBase Admin',
|
||||||
|
routes: [
|
||||||
|
// test...
|
||||||
|
// {
|
||||||
|
// type: 'route',
|
||||||
|
// path: '/admin/workflows/:id',
|
||||||
|
// component: 'WorkflowPage',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: 'route',
|
||||||
|
// path: '/admin/block-templates/:key',
|
||||||
|
// component: 'BlockTemplateDetails',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: 'route',
|
||||||
|
// path: '/admin/block-templates',
|
||||||
|
// component: 'BlockTemplatePage',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
type: 'route',
|
||||||
|
path: '/admin/:name(.+)?',
|
||||||
|
component: 'RouteSchemaComponent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'route',
|
||||||
|
component: 'AuthLayout',
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
type: 'route',
|
||||||
|
path: '/signin',
|
||||||
|
component: 'SigninPage',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'route',
|
||||||
|
path: '/signup',
|
||||||
|
component: 'SignupPage',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
for (const values of routes) {
|
||||||
|
await repository.create({
|
||||||
|
values,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.app.resourcer.registerActionHandler('uiRoutes:getAccessible', getAccessible);
|
||||||
|
this.app.db.registerModels({ MagicAttributeModel });
|
||||||
|
|
||||||
|
await this.app.db.import({
|
||||||
|
directory: resolve(__dirname, 'collections'),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.app.acl.use(
|
||||||
|
skip({
|
||||||
|
resourceName: 'uiRoutes',
|
||||||
|
actionName: 'getAccessible',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UiRoutesStoragePlugin;
|
@ -1,6 +1,7 @@
|
|||||||
export * from './constants';
|
|
||||||
export * from './calculators';
|
export * from './calculators';
|
||||||
export * from './triggers';
|
export * from './constants';
|
||||||
export * from './instructions';
|
export * from './instructions';
|
||||||
|
|
||||||
export { default } from './server';
|
export { default } from './server';
|
||||||
|
export * from './triggers';
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"@nocobase/plugin-ui-schema-storage": "0.7.0-alpha.83",
|
"@nocobase/plugin-ui-schema-storage": "0.7.0-alpha.83",
|
||||||
"@nocobase/plugin-users": "0.7.0-alpha.83",
|
"@nocobase/plugin-users": "0.7.0-alpha.83",
|
||||||
"@nocobase/plugin-workflow": "0.7.0-alpha.83",
|
"@nocobase/plugin-workflow": "0.7.0-alpha.83",
|
||||||
|
"@nocobase/plugin-export": "0.7.0-alpha.83",
|
||||||
"@nocobase/server": "0.7.0-alpha.83"
|
"@nocobase/server": "0.7.0-alpha.83"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
106
yarn.lock
106
yarn.lock
@ -2544,6 +2544,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.14.6", "@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3":
|
||||||
|
version "7.18.3"
|
||||||
|
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
|
||||||
|
integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.15.4", "@babel/runtime@^7.9.2":
|
"@babel/runtime@^7.15.4", "@babel/runtime@^7.9.2":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
|
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
|
||||||
@ -2551,13 +2558,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3":
|
|
||||||
version "7.18.3"
|
|
||||||
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
|
|
||||||
integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.13.4"
|
|
||||||
|
|
||||||
"@babel/template@^7.10.4", "@babel/template@^7.16.7", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
|
"@babel/template@^7.10.4", "@babel/template@^7.16.7", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
|
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
|
||||||
@ -5170,6 +5170,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/express" "*"
|
"@types/express" "*"
|
||||||
|
|
||||||
|
"@types/node-xlsx@^0.15.1":
|
||||||
|
version "0.15.3"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/node-xlsx/-/node-xlsx-0.15.3.tgz#675e5f91868b6c76c0bd3234fb84e4b55b807b1c"
|
||||||
|
integrity sha512-6UAa+t9eR3AZzRanZWGNUV8MoXwVpXQHMqCMdK8PNPwTy+G3kYHP+2KkTI+39vXETNb6krRijL+zWzmFrlfLjw==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "16.11.7"
|
version "16.11.7"
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42"
|
resolved "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42"
|
||||||
@ -5871,6 +5876,19 @@ address@>=0.0.1, address@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
|
resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
|
||||||
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
|
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
|
||||||
|
|
||||||
|
adler-32@~1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25"
|
||||||
|
integrity sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==
|
||||||
|
dependencies:
|
||||||
|
exit-on-epipe "~1.0.1"
|
||||||
|
printj "~1.1.0"
|
||||||
|
|
||||||
|
adler-32@~1.3.0:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz#1dbf0b36dda0012189a32b3679061932df1821e2"
|
||||||
|
integrity sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==
|
||||||
|
|
||||||
agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2:
|
agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||||
@ -7187,7 +7205,7 @@ buffer-equal@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
|
resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
|
||||||
integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74=
|
integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74=
|
||||||
|
|
||||||
buffer-from@1.x, buffer-from@^1.0.0:
|
buffer-from@1.x, buffer-from@^1.0.0, buffer-from@^1.1.1:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||||
@ -7466,6 +7484,14 @@ center-align@^0.1.1:
|
|||||||
align-text "^0.1.3"
|
align-text "^0.1.3"
|
||||||
lazy-cache "^1.0.3"
|
lazy-cache "^1.0.3"
|
||||||
|
|
||||||
|
cfb@^1.1.4:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz#94e687628c700e5155436dac05f74e08df23bc44"
|
||||||
|
integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==
|
||||||
|
dependencies:
|
||||||
|
adler-32 "~1.3.0"
|
||||||
|
crc-32 "~1.2.0"
|
||||||
|
|
||||||
chalk@2.4.1:
|
chalk@2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.npmmirror.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
|
resolved "https://registry.npmmirror.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
|
||||||
@ -7849,6 +7875,11 @@ code-point-at@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||||
|
|
||||||
|
codepage@~1.15.0:
|
||||||
|
version "1.15.0"
|
||||||
|
resolved "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz#2e00519024b39424ec66eeb3ec07227e692618ab"
|
||||||
|
integrity sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==
|
||||||
|
|
||||||
collect-v8-coverage@^1.0.0:
|
collect-v8-coverage@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
|
resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
|
||||||
@ -8388,6 +8419,11 @@ cosmiconfig@^7, cosmiconfig@^7.0.0:
|
|||||||
path-type "^4.0.0"
|
path-type "^4.0.0"
|
||||||
yaml "^1.10.0"
|
yaml "^1.10.0"
|
||||||
|
|
||||||
|
crc-32@~1.2.0:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
|
||||||
|
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
|
||||||
|
|
||||||
create-ecdh@^4.0.0:
|
create-ecdh@^4.0.0:
|
||||||
version "4.0.4"
|
version "4.0.4"
|
||||||
resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
|
resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
|
||||||
@ -10202,6 +10238,11 @@ exenv@^1.2.0:
|
|||||||
resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
||||||
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
|
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
|
||||||
|
|
||||||
|
exit-on-epipe@~1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
|
||||||
|
integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
|
||||||
|
|
||||||
exit@^0.1.2:
|
exit@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
|
resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
|
||||||
@ -10676,6 +10717,11 @@ formstream@^1.1.0:
|
|||||||
mime "^2.5.2"
|
mime "^2.5.2"
|
||||||
pause-stream "~0.0.11"
|
pause-stream "~0.0.11"
|
||||||
|
|
||||||
|
frac@~1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b"
|
||||||
|
integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
|
||||||
|
|
||||||
fraction.js@^4.2.0:
|
fraction.js@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
||||||
@ -15697,6 +15743,15 @@ node-releases@^2.0.3:
|
|||||||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476"
|
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476"
|
||||||
integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
|
integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
|
||||||
|
|
||||||
|
node-xlsx@^0.16.1:
|
||||||
|
version "0.16.2"
|
||||||
|
resolved "https://registry.npmmirror.com/node-xlsx/-/node-xlsx-0.16.2.tgz#40f580187eae0e032cac96e958e97cb6ceca09f6"
|
||||||
|
integrity sha512-ZT3Y4Zg2BFC2UWdp9B/6x3GqrFL0Bf0cXKy9IyhcwlKbcDAf5GuPAPSqrWFQK68NIpfTNA1Kr/NNjpwYxUgHTA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.14.6"
|
||||||
|
buffer-from "^1.1.1"
|
||||||
|
xlsx "^0.17.0"
|
||||||
|
|
||||||
nodemailer-mock@^1.5.11:
|
nodemailer-mock@^1.5.11:
|
||||||
version "1.5.11"
|
version "1.5.11"
|
||||||
resolved "https://registry.npmjs.org/nodemailer-mock/-/nodemailer-mock-1.5.11.tgz#1bb6b9af44e9007380191d32e33555ea136a819f"
|
resolved "https://registry.npmjs.org/nodemailer-mock/-/nodemailer-mock-1.5.11.tgz#1bb6b9af44e9007380191d32e33555ea136a819f"
|
||||||
@ -17785,6 +17840,11 @@ pretty-quick@^3.1.0:
|
|||||||
mri "^1.1.5"
|
mri "^1.1.5"
|
||||||
multimatch "^4.0.0"
|
multimatch "^4.0.0"
|
||||||
|
|
||||||
|
printj@~1.1.0:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
|
||||||
|
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
|
||||||
|
|
||||||
prism-react-renderer@^1.1.1:
|
prism-react-renderer@^1.1.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89"
|
resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89"
|
||||||
@ -20599,6 +20659,13 @@ sqlstring@^2.3.2:
|
|||||||
resolved "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514"
|
resolved "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514"
|
||||||
integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==
|
integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==
|
||||||
|
|
||||||
|
ssf@~0.11.2:
|
||||||
|
version "0.11.2"
|
||||||
|
resolved "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c"
|
||||||
|
integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
|
||||||
|
dependencies:
|
||||||
|
frac "~1.1.2"
|
||||||
|
|
||||||
sshpk@^1.7.0:
|
sshpk@^1.7.0:
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||||
@ -22779,11 +22846,21 @@ wkx@^0.5.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
wmf@~1.0.1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da"
|
||||||
|
integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
|
||||||
|
|
||||||
word-wrap@^1.2.3, word-wrap@~1.2.3:
|
word-wrap@^1.2.3, word-wrap@~1.2.3:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||||
|
|
||||||
|
word@~0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
|
||||||
|
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
||||||
|
|
||||||
wordwrap@0.0.2:
|
wordwrap@0.0.2:
|
||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
|
resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
|
||||||
@ -22914,6 +22991,19 @@ xdg-basedir@^3.0.0:
|
|||||||
resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||||
|
|
||||||
|
xlsx@^0.17.0:
|
||||||
|
version "0.17.5"
|
||||||
|
resolved "https://registry.npmmirror.com/xlsx/-/xlsx-0.17.5.tgz#78b788fcfc0773d126cdcd7ea069cb7527c1ce81"
|
||||||
|
integrity sha512-lXNU0TuYsvElzvtI6O7WIVb9Zar1XYw7Xb3VAx2wn8N/n0whBYrCnHMxtFyIiUU1Wjf09WzmLALDfBO5PqTb1g==
|
||||||
|
dependencies:
|
||||||
|
adler-32 "~1.2.0"
|
||||||
|
cfb "^1.1.4"
|
||||||
|
codepage "~1.15.0"
|
||||||
|
crc-32 "~1.2.0"
|
||||||
|
ssf "~0.11.2"
|
||||||
|
wmf "~1.0.1"
|
||||||
|
word "~0.3.0"
|
||||||
|
|
||||||
xml-name-validator@^3.0.0:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
|
Loading…
Reference in New Issue
Block a user