mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:20:11 +00:00
chore: convert devMode to standard spec format using metadataTransducer
fix: https://github.com/alibaba/lowcode-engine/issues/283
This commit is contained in:
parent
0c6bca339a
commit
3d176cb534
@ -16,7 +16,7 @@ import {
|
|||||||
import { deprecate } from '@alilc/lowcode-utils';
|
import { deprecate } from '@alilc/lowcode-utils';
|
||||||
import { computed, engineConfig } from '@alilc/lowcode-editor-core';
|
import { computed, engineConfig } from '@alilc/lowcode-editor-core';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
import { componentDefaults, legacyIssues } from './transducers';
|
||||||
import { isNode, Node, ParentalNode } from './document';
|
import { isNode, Node, ParentalNode } from './document';
|
||||||
import { Designer } from './designer';
|
import { Designer } from './designer';
|
||||||
import { intlNode } from './locale';
|
import { intlNode } from './locale';
|
||||||
@ -397,47 +397,6 @@ export function getRegisteredMetadataTransducers(): MetadataTransducer[] {
|
|||||||
return metadataTransducers;
|
return metadataTransducers;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMetadataTransducer((metadata) => {
|
|
||||||
const { configure, componentName } = metadata;
|
|
||||||
const { component = {} } = configure;
|
|
||||||
if (!component.nestingRule) {
|
|
||||||
let m;
|
|
||||||
// uri match xx.Group set subcontrolling: true, childWhiteList
|
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
if ((m = /^(.+)\.Group$/.exec(componentName))) {
|
|
||||||
// component.subControlling = true;
|
|
||||||
if (!component.nestingRule) {
|
|
||||||
component.nestingRule = {
|
|
||||||
childWhitelist: [`${m[1]}`],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
} else if ((m = /^(.+)\.Node$/.exec(componentName))) {
|
|
||||||
// uri match xx.Node set selfControlled: false, parentWhiteList
|
|
||||||
// component.selfControlled = false;
|
|
||||||
component.nestingRule = {
|
|
||||||
parentWhitelist: [`${m[1]}`, componentName],
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
} else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) {
|
|
||||||
// uri match .Item .Node .Option set parentWhiteList
|
|
||||||
component.nestingRule = {
|
|
||||||
parentWhitelist: [`${m[1]}`],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if (component.isModal == null && /Dialog/.test(componentName)) {
|
|
||||||
// component.isModal = true;
|
|
||||||
// }
|
|
||||||
return {
|
|
||||||
...metadata,
|
|
||||||
configure: {
|
|
||||||
...configure,
|
|
||||||
component,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const builtinComponentActions: ComponentAction[] = [
|
const builtinComponentActions: ComponentAction[] = [
|
||||||
{
|
{
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
@ -544,3 +503,6 @@ export function modifyBuiltinComponentAction(
|
|||||||
handle(builtinAction);
|
handle(builtinAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerMetadataTransducer(legacyIssues, 2, 'legacy-issues'); // should use a high level priority, eg: 2
|
||||||
|
registerMetadataTransducer(componentDefaults, 100, 'component-defaults');
|
||||||
|
|||||||
@ -521,7 +521,7 @@ export class Designer {
|
|||||||
const designer = this;
|
const designer = this;
|
||||||
designer._componentMetasMap.forEach((config, key) => {
|
designer._componentMetasMap.forEach((config, key) => {
|
||||||
const metaData = config.getMetadata();
|
const metaData = config.getMetadata();
|
||||||
if (metaData.devMode === 'lowcode') {
|
if (metaData.devMode === 'lowCode') {
|
||||||
maps[key] = metaData.schema;
|
maps[key] = metaData.schema;
|
||||||
} else {
|
} else {
|
||||||
const view = metaData.configure.advanced?.view;
|
const view = metaData.configure.advanced?.view;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export interface ComponentMap {
|
|||||||
destructuring?: boolean;
|
destructuring?: boolean;
|
||||||
exportName?: string;
|
exportName?: string;
|
||||||
subName?: string;
|
subName?: string;
|
||||||
devMode?: 'lowcode' | 'procode';
|
devMode?: 'lowCode' | 'proCode';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocumentModel {
|
export class DocumentModel {
|
||||||
@ -667,7 +667,7 @@ export class DocumentModel {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
componentsMap.push({
|
componentsMap.push({
|
||||||
devMode: 'lowcode',
|
devMode: 'lowCode',
|
||||||
componentName,
|
componentName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1189,7 +1189,7 @@ export function isRootNode(node: Node): node is RootNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isLowCodeComponent(node: Node): boolean {
|
export function isLowCodeComponent(node: Node): boolean {
|
||||||
return node.componentMeta?.getMetadata().devMode === 'lowcode';
|
return node.componentMeta?.getMetadata().devMode === 'lowCode';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getZLevelTop(child: Node, zLevel: number): Node | null {
|
export function getZLevelTop(child: Node, zLevel: number): Node | null {
|
||||||
|
|||||||
48
packages/designer/src/transducers/index.ts
Normal file
48
packages/designer/src/transducers/index.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { TransformedComponentMetadata as Metadata } from '@alilc/lowcode-types';
|
||||||
|
|
||||||
|
export function legacyIssues(metadata: Metadata): Metadata {
|
||||||
|
const { devMode } = metadata;
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
devMode: devMode?.replace(/(low|pro)code/, '$1Code') as Metadata['devMode'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function componentDefaults(metadata: Metadata): Metadata {
|
||||||
|
const { configure, componentName } = metadata;
|
||||||
|
const { component = {} } = configure;
|
||||||
|
if (!component.nestingRule) {
|
||||||
|
let m;
|
||||||
|
// uri match xx.Group set subcontrolling: true, childWhiteList
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
if ((m = /^(.+)\.Group$/.exec(componentName))) {
|
||||||
|
// component.subControlling = true;
|
||||||
|
component.nestingRule = {
|
||||||
|
childWhitelist: [`${m[1]}`],
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
} else if ((m = /^(.+)\.Node$/.exec(componentName))) {
|
||||||
|
// uri match xx.Node set selfControlled: false, parentWhiteList
|
||||||
|
// component.selfControlled = false;
|
||||||
|
component.nestingRule = {
|
||||||
|
parentWhitelist: [`${m[1]}`, componentName],
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
} else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) {
|
||||||
|
// uri match .Item .Node .Option set parentWhiteList
|
||||||
|
component.nestingRule = {
|
||||||
|
parentWhitelist: [`${m[1]}`],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (component.isModal == null && /Dialog/.test(componentName)) {
|
||||||
|
// component.isModal = true;
|
||||||
|
// }
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
...configure,
|
||||||
|
component,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -5,59 +5,59 @@ Object {
|
|||||||
"componentsMap": Array [
|
"componentsMap": Array [
|
||||||
Object {
|
Object {
|
||||||
"componentName": "PageHeader",
|
"componentName": "PageHeader",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "RootHeader",
|
"componentName": "RootHeader",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "TextField",
|
"componentName": "TextField",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "Column",
|
"componentName": "Column",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "SelectField",
|
"componentName": "SelectField",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "ColumnsLayout",
|
"componentName": "ColumnsLayout",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "CardContent",
|
"componentName": "CardContent",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "Card",
|
"componentName": "Card",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "Button",
|
"componentName": "Button",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "Div",
|
"componentName": "Div",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "Form",
|
"componentName": "Form",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "RootContent",
|
"componentName": "RootContent",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "RootFooter",
|
"componentName": "RootFooter",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"componentName": "Page",
|
"componentName": "Page",
|
||||||
"devMode": "lowcode",
|
"devMode": "lowCode",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"componentsTree": Array [
|
"componentsTree": Array [
|
||||||
|
|||||||
@ -175,7 +175,7 @@ describe('document-model 测试', () => {
|
|||||||
{ componentName: 'Other', package: '@ali/vc-other' }
|
{ componentName: 'Other', package: '@ali/vc-other' }
|
||||||
);
|
);
|
||||||
expect(comps.find(comp => comp.componentName === 'Page')).toEqual(
|
expect(comps.find(comp => comp.componentName === 'Page')).toEqual(
|
||||||
{ componentName: 'Page', devMode: 'lowcode' }
|
{ componentName: 'Page', devMode: 'lowCode' }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'Button',
|
componentName: 'Button',
|
||||||
npm: {
|
npm: {
|
||||||
@ -6,7 +7,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '按钮',
|
title: '按钮',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -304,4 +305,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'Dialog',
|
componentName: 'Dialog',
|
||||||
npm: {
|
npm: {
|
||||||
@ -6,7 +7,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -273,4 +274,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'Div',
|
componentName: 'Div',
|
||||||
npm: {
|
npm: {
|
||||||
@ -6,7 +7,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -279,4 +280,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'Form',
|
componentName: 'Form',
|
||||||
npm: {
|
npm: {
|
||||||
@ -5,7 +6,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '表单',
|
title: '表单',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -275,4 +276,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'Other',
|
componentName: 'Other',
|
||||||
npm: {
|
npm: {
|
||||||
@ -5,7 +6,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -275,4 +276,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'Page',
|
componentName: 'Page',
|
||||||
npm: {
|
npm: {
|
||||||
@ -5,7 +6,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -275,4 +276,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'RootContent',
|
componentName: 'RootContent',
|
||||||
npm: {
|
npm: {
|
||||||
@ -5,7 +6,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -275,4 +276,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'RootFooter',
|
componentName: 'RootFooter',
|
||||||
npm: {
|
npm: {
|
||||||
@ -5,7 +6,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -275,4 +276,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ComponentMetadata } from "@alilc/lowcode-types";
|
||||||
export default {
|
export default {
|
||||||
componentName: 'RootHeader',
|
componentName: 'RootHeader',
|
||||||
npm: {
|
npm: {
|
||||||
@ -5,7 +6,7 @@ export default {
|
|||||||
},
|
},
|
||||||
title: '容器',
|
title: '容器',
|
||||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||||
devMode: 'procode',
|
devMode: 'proCode',
|
||||||
tags: ['布局'],
|
tags: ['布局'],
|
||||||
configure: {
|
configure: {
|
||||||
props: [
|
props: [
|
||||||
@ -275,4 +276,4 @@ export default {
|
|||||||
],
|
],
|
||||||
autoruns: [],
|
autoruns: [],
|
||||||
},
|
},
|
||||||
};
|
} as ComponentMetadata;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// @ts-ignore
|
|
||||||
import '../../fixtures/window';
|
import '../../fixtures/window';
|
||||||
import { Node } from '../../../src/document/node/node';
|
import { Node } from '../../../src/document/node/node';
|
||||||
import { Designer } from '../../../src/designer/designer';
|
import { Designer } from '../../../src/designer/designer';
|
||||||
@ -18,7 +17,7 @@ jest.mock('../../../src/designer/designer', () => {
|
|||||||
|
|
||||||
let designer = null;
|
let designer = null;
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
designer = new Designer({});
|
designer = new Designer({} as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('组件元数据处理', () => {
|
describe('组件元数据处理', () => {
|
||||||
@ -66,3 +65,15 @@ describe('组件元数据处理', () => {
|
|||||||
expect(meta.availableActions[4].name).toBe('new');
|
expect(meta.availableActions[4].name).toBe('new');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('组件元数据transducers', () => {
|
||||||
|
it('legacyIssues', () => {
|
||||||
|
const legacyMeta: any = {
|
||||||
|
...divMeta,
|
||||||
|
devMode: 'procode'
|
||||||
|
}
|
||||||
|
const meta = new ComponentMeta(designer, legacyMeta);
|
||||||
|
const metadata = meta.getMetadata();
|
||||||
|
expect(metadata.devMode).toBe('proCode');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -379,7 +379,7 @@ export interface ComponentMetadata {
|
|||||||
/**
|
/**
|
||||||
* 组件研发模式
|
* 组件研发模式
|
||||||
*/
|
*/
|
||||||
devMode?: 'procode' | 'lowcode';
|
devMode?: 'proCode' | 'lowCode';
|
||||||
/**
|
/**
|
||||||
* npm 源引入完整描述对象
|
* npm 源引入完整描述对象
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user