(null);
- // @ts-ignore
+ const graphRef = useRef(null);
const { children, assets, id, services, config, locales } = props;
const { language = 'zh-CN', ...localeMessages } = locales || {};
@@ -41,6 +86,7 @@ const GISDK = (props: Props) => {
nodes: [],
edges: [],
},
+ HAS_GRAPH: false,
source: { nodes: [], edges: [] } as GIGraphData,
layout: {},
components: [] as GIComponentConfig[],
@@ -61,11 +107,59 @@ const GISDK = (props: Props) => {
GISDK_ID,
});
- React.useEffect(() => {
- updateState(draft => {
- draft.config = props.config;
- });
- }, [props.config]);
+ const { data, layout, components, initializer, theme, transform, GICC_LAYOUT, HAS_GRAPH, graph } = state;
+
+ // const handleGraphInit = graph => {
+ // updateState(draft => {
+ // draft.graph = graph;
+ // draft.HAS_GRAPH = true;
+ // });
+ // };
+
+ useEffect(() => {
+ // init...
+ if (!HAS_INIT) {
+ console.log('%c GISDK INTI ....', 'color:rgba(255,87,34,0.8)', graphRef);
+ HAS_INIT = true;
+ const { assets, config, services } = props;
+
+ const { GICC_LAYOUT, INITIALIZER, componentsCfg } = getComponentsCfg(config.components, config.pageLayout);
+ const transform = getTransformer(config.nodes, config.edges, ElementAssets);
+
+ updateState(draft => {
+ /** initializer */
+ if (INITIALIZER.id !== draft.initializer?.id) {
+ draft.initializer = INITIALIZER;
+ }
+ /** components */
+ draft.config.components = componentsCfg;
+ draft.components = componentsCfg;
+ /** layout */
+ draft.config.layout = config.layout;
+ draft.layout = config.layout.props || {};
+ draft.layoutCache = false;
+ /** styling */
+ draft.transform = transform;
+ draft.config.nodes = config.nodes;
+ draft.config.edges = config.edges;
+ if (draft.data.nodes.length !== 0) {
+ const preData = original(draft.data);
+ // 当节点和边的Schema配置变化的时候,默认是重置视觉映射;
+ const newData = transform(preData, true);
+ //@ts-ignore
+ draft.data = newData;
+ }
+
+ draft.GICC_LAYOUT = GICC_LAYOUT;
+ /** props */
+ draft.config = config;
+ draft.servives = services;
+ /** flag */
+ draft.graph = graphRef.current;
+ draft.HAS_GRAPH = true;
+ });
+ }
+ }, []);
const {
layout: layoutCfg,
@@ -77,28 +171,15 @@ const GISDK = (props: Props) => {
/** 根据注册的图元素,生成Transform函数 */
React.useEffect(() => {
- let GICC_LAYOUT = { id: 'EmptyLayout', props: {} };
- let INITIALIZER;
- if (pageLayout) GICC_LAYOUT = pageLayout;
- componentsCfg.forEach(item => {
- if (pageLayout) {
- if (item.id === pageLayout.id) {
- GICC_LAYOUT = item;
- return;
- }
- } else if (item.type === 'GICC_LAYOUT') {
- GICC_LAYOUT = item;
- return;
- }
- if (item.type === 'INITIALIZER' || item.props.GI_INITIALIZER) {
- INITIALIZER = item;
- return;
- }
- });
+ if (!HAS_GRAPH) {
+ return;
+ }
+ console.log('%c GISDK COMPONENTS ....', 'color:rgba(255,87,34,0.8)');
+ const { GICC_LAYOUT, INITIALIZER, componentsCfg: ComponentCfg } = getComponentsCfg(componentsCfg, pageLayout);
updateState(draft => {
- draft.config.components = componentsCfg;
- draft.components = componentsCfg;
+ draft.config.components = ComponentCfg;
+ draft.components = ComponentCfg;
if (INITIALIZER.id !== draft.initializer?.id) {
//@ts-ignore
draft.initializer = INITIALIZER;
@@ -107,74 +188,16 @@ const GISDK = (props: Props) => {
//@ts-ignore
draft.GICC_LAYOUT = GICC_LAYOUT;
});
- }, [componentsCfg, pageLayout]);
+ }, [componentsCfg, pageLayout, HAS_GRAPH]);
React.useEffect(() => {
- if (!layoutCfg) {
+ if (!layoutCfg || !HAS_GRAPH) {
return;
}
+ console.log('%c GISDK LAYOUT ....', 'color:rgba(255,87,34,0.8)');
stopForceSimulation();
- // @ts-ignore
const { type, ...options } = layoutCfg.props || {};
- //@ts-ignore
let otherOptions = {};
- if (options && options.defSpringLenCfg) {
- //@ts-ignore
- otherOptions = {
- defSpringLen: utils.getDefSpringLenFunction(options.defSpringLenCfg),
- };
- }
- // 资金力导布局定制
- if (layoutCfg.id === 'FundForce') {
- otherOptions = {
- defSideCoe: utils.getDefSideCoeFunction(options.income, options.outcome, options.isLog, options.multiple),
- };
- }
-
- if (layoutCfg.id === 'Force2') {
- const {
- advanceWeight,
- edgeWeightField,
- nodeWeightField,
- nodeWeightFieldFromEdge,
- nodeWeightFromType,
- directed,
- directedFromType,
- directedInWeightField,
- directedOutWeightField,
- directedIsLog,
- directedMultiple,
- directedAmountFromEdge,
- } = options;
- (otherOptions as any).defSideCoe = 'unset';
- if (advanceWeight) {
- if (edgeWeightField) {
- (otherOptions as any).edgeStrength = utils.getEdgeWeightedStrength(options);
- }
- if (
- (nodeWeightFromType === 'node' && nodeWeightField) ||
- (nodeWeightFromType === 'edge' && nodeWeightFieldFromEdge)
- ) {
- (otherOptions as any).nodeStrength = utils.getNodeWeightedStrength(options);
- }
- if (directed) {
- if (directedFromType === 'node') {
- (otherOptions as any).defSideCoe = utils.getDefSideCoeFunction(
- directedInWeightField,
- directedOutWeightField,
- directedIsLog,
- directedMultiple,
- );
- } else if (directedAmountFromEdge) {
- (otherOptions as any).defSideCoe = utils.getDefSideCoeFromEdgeFunction(
- directedAmountFromEdge,
- directedIsLog,
- directedMultiple,
- );
- }
- }
- }
- }
updateState(draft => {
draft.layout = {
@@ -187,31 +210,15 @@ const GISDK = (props: Props) => {
draft.config.layout = layoutCfg;
draft.layoutCache = false;
});
- }, [layoutCfg]);
+ }, [layoutCfg, HAS_GRAPH]);
/** 增加多元素 */
React.useEffect(() => {
- if (!nodesCfg || !edgesCfg || nodesCfg.length === 0 || edgesCfg.length === 0) {
+ if (!nodesCfg || !edgesCfg || nodesCfg.length === 0 || edgesCfg.length === 0 || !HAS_GRAPH) {
return;
}
-
- /**
- *
- * @param data 源数据
- * @param reset 是否重置:按照 Node/Edge Schema来视觉映射
- * @returns
- */
- const transform = (data, reset?: boolean) => {
- const nodes = utils.transDataByConfig('nodes', data, { nodes: nodesCfg, edges: edgesCfg }, ElementAssets, reset);
- const edges = utils.transDataByConfig('edges', data, { nodes: nodesCfg, edges: edgesCfg }, ElementAssets, reset);
- const { combos, tableResult } = data;
- return {
- nodes,
- edges,
- combos,
- tableResult,
- };
- };
+ console.log('%c GISDK STYLE ....', 'color:rgba(255,87,34,0.8)');
+ const transform = getTransformer(nodesCfg, edgesCfg, ElementAssets);
updateState(draft => {
if (draft.data.nodes.length !== 0) {
@@ -221,15 +228,11 @@ const GISDK = (props: Props) => {
//@ts-ignore
draft.data = newData;
}
-
draft.transform = transform;
draft.config.nodes = nodesCfg;
draft.config.edges = edgesCfg;
});
- }, [nodesCfg, edgesCfg]);
-
- // @ts-ignore
- const { data, layout, components, initializer, theme, transform, GICC_LAYOUT } = state;
+ }, [nodesCfg, edgesCfg, HAS_GRAPH]);
// console.log('%c G6VP Render...', 'color:red', state.layout);
const sourceDataMap = useMemo(() => {
@@ -250,47 +253,46 @@ const GISDK = (props: Props) => {
}, [state.source]);
const stopForceSimulation = () => {
- if (graphinRef.current) {
- const { layout, graph } = graphinRef.current;
- const { instance } = layout;
- if (instance) {
- const { type, simulation } = instance;
- if (type === 'graphin-force') {
- simulation.stop();
- return;
- }
- }
- const layoutController = graph.get('layoutController');
- const layoutMethod = layoutController.layoutMethods?.[0];
- if (layoutMethod?.type === 'force2') {
- layoutMethod.stop();
- }
- }
+ // if (graphinRef.current) {
+ // const { layout, graph } = graphinRef.current;
+ // const { instance } = layout;
+ // if (instance) {
+ // const { type, simulation } = instance;
+ // if (type === 'graphin-force') {
+ // simulation.stop();
+ // return;
+ // }
+ // }
+ // const layoutController = graph.get('layoutController');
+ // const layoutMethod = layoutController.layoutMethods?.[0];
+ // if (layoutMethod?.type === 'force2') {
+ // layoutMethod.stop();
+ // }
+ // }
};
const restartForceSimulation = (nodes = []) => {
- if (graphinRef.current) {
- const { layout: graphLayout, graph } = graphinRef.current;
- const { instance } = graphLayout;
- if (instance) {
- const { type, simulation } = instance;
- if (type === 'graphin-force') {
- simulation.restart(nodes, graph);
- return;
- }
- }
- const layoutController = graph.get('layoutController');
- const layoutMethod = layoutController.layoutMethods?.[0];
- if (layoutMethod?.type === 'force2') {
- graph.updateLayout({ animate: true, disableTriggerLayout: false });
- updateState(draft => {
- draft.layout.animate = true;
- });
- }
- }
+ // if (graphinRef.current) {
+ // const { layout: graphLayout, graph } = graphinRef.current;
+ // const { instance } = graphLayout;
+ // if (instance) {
+ // const { type, simulation } = instance;
+ // if (type === 'graphin-force') {
+ // simulation.restart(nodes, graph);
+ // return;
+ // }
+ // }
+ // const layoutController = graph.get('layoutController');
+ // const layoutMethod = layoutController.layoutMethods?.[0];
+ // if (layoutMethod?.type === 'force2') {
+ // graph.updateLayout({ animate: true, disableTriggerLayout: false });
+ // updateState(draft => {
+ // draft.layout.animate = true;
+ // });
+ // }
+ // }
};
- console.log('graphinRef.current', graphinRef.current);
- const HAS_GRAPH = graphinRef.current?.graph && !graphinRef.current.graph.destroyed;
+ console.log('%c GISDK RENDER....', 'color:rgba(255,87,34,1)', HAS_GRAPH, state.initialized);
const ContextValue = {
...state,
GISDK_ID,
@@ -298,10 +300,8 @@ const GISDK = (props: Props) => {
assets,
sourceDataMap,
HAS_GRAPH,
- graph: graphinRef.current?.graph,
- theme: graphinRef.current?.theme,
- apis: graphinRef.current?.apis,
- layoutInstance: graphinRef.current?.layout,
+ graph: graph,
+
updateContext: updateState,
updateData: res => {
updateState(draft => {
@@ -360,52 +360,26 @@ const GISDK = (props: Props) => {
const { renderComponents, InitializerComponent, InitializerProps, GICC_LAYOUT_COMPONENT, GICC_LAYOUT_PROPS } =
getComponents({ ...state, HAS_GRAPH }, config.components, ComponentAssets);
- const graphData = useMemo(() => {
- const nodeMap = {};
- const edgeMap = {};
- const edges: any[] = [];
- const nodes: any[] = [];
- data.nodes?.forEach(node => {
- if (!nodeMap[node.id]) {
- nodes.push(node);
- nodeMap[node.id] = node;
- }
- });
-
- data.edges.forEach(edge => {
- if (nodeMap[edge.source] && nodeMap[edge.target] && !edgeMap[edge.id]) {
- edges?.push(edge);
- edgeMap[edge.id] = edge;
- }
- });
- const { combos } = data;
- return {
- nodes,
- edges,
- combos,
- };
- }, [data]);
-
return (
+ {/* @ts-ignore */}
{/* @ts-ignore */}
+
- <>
-
- {HAS_GRAPH && }
- {HAS_GRAPH && state.initialized && renderComponents()}
- {HAS_GRAPH && state.initialized && children}
- >
+
+ {HAS_GRAPH && }
+ {HAS_GRAPH && state.initialized && renderComponents()}
+ {HAS_GRAPH && state.initialized && children}
diff --git a/packages/gi-sdk/src/SetupUseGraphinHook.tsx b/packages/gi-sdk/src/SetupUseGraphinHook.tsx
deleted file mode 100644
index 435ef0f2b..000000000
--- a/packages/gi-sdk/src/SetupUseGraphinHook.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { GraphinContext } from '@antv/graphin';
-
-import React from 'react';
-
-const SetupUseGraphinHook = props => {
- const { updateContext } = props;
- const { graph, apis, layout, theme } = React.useContext(GraphinContext);
-
- React.useEffect(() => {
- const stopForceSimulation = () => {
- const { instance } = layout;
- if (instance) {
- const { type, simulation } = instance;
- if (type === 'graphin-force') {
- simulation.stop();
- }
- }
- };
- const restartForceSimulation = (nodes = []) => {
- const { instance } = layout;
- if (instance) {
- const { type, simulation } = instance;
- if (type === 'graphin-force') {
- simulation.restart(nodes, graph);
- }
- }
- };
-
- updateContext(draft => {
- draft.graph = graph;
- draft.theme = theme;
- draft.apis = apis;
- //@ts-ignore
- draft.layoutInstance = layout;
- draft.isContextReady = true;
- draft.stopForceSimulation = stopForceSimulation;
- draft.restartForceSimulation = restartForceSimulation;
- });
- return () => {
- // console.warn('卸载组件....', graph && graph.destroyed);
- };
- }, [layout, graph]);
-
- return null;
-};
-
-export default SetupUseGraphinHook;
diff --git a/packages/gi-sdk/src/components/EngineServer/LoadGraph.tsx b/packages/gi-sdk/src/components/EngineServer/LoadGraph.tsx
index 57616e5dc..68b060f49 100644
--- a/packages/gi-sdk/src/components/EngineServer/LoadGraph.tsx
+++ b/packages/gi-sdk/src/components/EngineServer/LoadGraph.tsx
@@ -2,11 +2,12 @@ import Graphin from '@antv/graphin';
import { Button, Col, Form, Input, Row, Select, Statistic } from 'antd';
import * as React from 'react';
import { useImmer } from 'use-immer';
-import { GraphSchemaData, utils } from '../../index';
+import $i18n from '../../i18n';
+import { GraphSchemaData } from '../../index';
+import * as utils from '../../process';
import CollapseCard from '../CollapseCard';
import type { GraphDBConfig } from './index';
import { getEngineForm, setEngineForm } from './utils';
-import $i18n from '../../i18n';
const { getSchemaGraph } = utils;
diff --git a/packages/gi-sdk/src/components/Initializer/Component.tsx b/packages/gi-sdk/src/components/Initializer/Component.tsx
new file mode 100644
index 000000000..40079b8fc
--- /dev/null
+++ b/packages/gi-sdk/src/components/Initializer/Component.tsx
@@ -0,0 +1,176 @@
+import { notification } from 'antd';
+import React, { memo } from 'react';
+import $i18n from '../../i18n';
+import { GIConfig, useContext } from '../../index';
+import * as utils from '../../process';
+
+const { isPosition, isStyles } = utils;
+
+export type GIService = any;
+export interface IProps {
+ serviceId: string;
+ schemaServiceId: string;
+ aggregate: boolean;
+ transByFieldMapping: boolean;
+}
+
+const Initializer: React.FunctionComponent = props => {
+ const context = useContext();
+ const { serviceId, schemaServiceId, aggregate, transByFieldMapping } = props;
+ const { services, updateContext, transform, largeGraphLimit } = context;
+
+ React.useEffect(() => {
+ // const { service: initialService } = services.find(s => s.id === serviceId) as GIService;
+ // const { service: schemaService } = (services.find(s => s.id === schemaServiceId) as GIService) || {
+ // service: () => Promise.resolve(null),
+ // };
+
+ let initialService = services.find(s => s.id === serviceId) as GIService;
+ let schemaService = services.find(s => s.id === schemaServiceId) as GIService;
+
+ if (!initialService) {
+ notification.error({
+ message: $i18n.get({ id: 'basic.components.Initializer.Component.CanvasRenderingFailed', dm: '画布渲染失败' }),
+ description: $i18n.get(
+ {
+ id: 'basic.components.Initializer.Component.TheServiceidServiceIsMissing',
+ dm: '缺少 {serviceId} 服务,请检查相关资产是否加载成功',
+ },
+ { serviceId: serviceId },
+ ),
+ });
+ initialService = {
+ service: () => {
+ return new Promise(resolve => {
+ resolve({
+ nodes: [],
+ edges: [],
+ });
+ });
+ },
+ };
+ }
+ if (!schemaService) {
+ notification.error({
+ message: $i18n.get({
+ id: 'basic.components.Initializer.Component.FailedToObtainGraphModel',
+ dm: '图模型获取失败',
+ }),
+ description: $i18n.get(
+ {
+ id: 'basic.components.Initializer.Component.TheServiceidServiceIsMissing',
+ dm: '缺少 {serviceId} 服务,请检查相关资产是否加载成功',
+ },
+ { serviceId: serviceId },
+ ),
+ });
+ schemaService = {
+ service: () => {
+ return new Promise(resolve => {
+ resolve({
+ nodes: [],
+ edges: [],
+ });
+ });
+ },
+ };
+ }
+ updateContext(draft => {
+ draft.isLoading = true;
+ });
+
+ Promise.all([schemaService.service(), initialService.service()]).then(
+ ([schemaData, graphData = { nodes: [], edges: [] }]) => {
+ let schema = schemaData;
+ let data = graphData;
+
+ if (transByFieldMapping) {
+ const { schemaData: _schemaData, data: _data } = utils.transDataBySchemaMeta(graphData, schemaData);
+ schema = _schemaData;
+ data = _data;
+ }
+ const { nodes } = data;
+
+ if (nodes.length > largeGraphLimit) {
+ notification.warn({
+ message: $i18n.get({
+ id: 'basic.components.Initializer.Component.TheAmountOfDataLoaded',
+ dm: '加载的数据量过大',
+ }),
+ description: $i18n.get({
+ id: 'basic.components.Initializer.Component.WeRecommendThatYouAggregate',
+ dm: '建议聚合数据,默认切换到网格布局。您也可以在「资产中心」中加载「大图组件」启用 3D 渲染',
+ }),
+ });
+ }
+ updateContext(draft => {
+ /** 判断是否保存样式和位置 */
+ const position = isPosition(nodes);
+ const style = isStyles(nodes);
+ /** 取消布局缓存 */
+ draft.initialized = true;
+ draft.layoutCache = false;
+
+ /** 如果接口有 schema,就更新 schemaData */
+ if (schema) {
+ draft.schemaData = schema as any;
+ }
+ /** 只有当 config 中没有 nodes 和 edges 的时候,才会用 schema 生成一个默认样式 */
+
+ if (schema && (draft.config.nodes?.length === 0 || draft.config.edges?.length === 0)) {
+ const schemaStyle = utils.generatorStyleConfigBySchema(schema) as GIConfig;
+ draft.config.nodes = schemaStyle.nodes;
+ draft.config.edges = schemaStyle.edges;
+ }
+ /** 如果有布局信息 */
+ if (position) {
+ draft.layout.type = 'preset';
+ }
+ /** 如果有样式数据 */
+ if (style) {
+ draft.data = data;
+ draft.source = data;
+ draft.isLoading = false;
+ return;
+ }
+ /** 如果是大图模式 */
+ if (nodes.length > largeGraphLimit) {
+ const newData = transform(data, true);
+ draft.largeGraphMode = true;
+ draft.largeGraphData = newData;
+ draft.source = newData;
+ draft.data = {
+ nodes: [],
+ edges: [],
+ };
+ draft.isLoading = false;
+ return;
+ }
+ /** 如果是聚合模式 */
+ if (aggregate) {
+ const newData = transform(data, true);
+ draft.rawData = { ...data };
+ draft.source = newData;
+ draft.largeGraphMode = false;
+ draft.largeGraphData = undefined;
+ draft.data = transform(utils.aggregateEdges(data), true);
+ draft.isLoading = false;
+ return;
+ }
+ /** 默认是普通模式 */
+ const newData = transform(data, true);
+ draft.rawData = { ...data };
+ draft.data = newData;
+ draft.source = newData;
+ draft.largeGraphMode = false;
+ draft.largeGraphData = undefined;
+ draft.isLoading = false;
+ });
+ },
+ );
+ }, [largeGraphLimit, aggregate, transByFieldMapping]);
+
+ return null;
+};
+
+export default memo(Initializer);
diff --git a/packages/gi-sdk/src/components/Initializer/index.tsx b/packages/gi-sdk/src/components/Initializer/index.tsx
new file mode 100644
index 000000000..390cc7dca
--- /dev/null
+++ b/packages/gi-sdk/src/components/Initializer/index.tsx
@@ -0,0 +1,9 @@
+import Component from './Component';
+import info from './info';
+import registerMeta from './registerMeta';
+
+export default {
+ info,
+ component: Component,
+ registerMeta,
+};
diff --git a/packages/gi-sdk/src/components/Initializer/info.ts b/packages/gi-sdk/src/components/Initializer/info.ts
new file mode 100644
index 000000000..fb3d565b9
--- /dev/null
+++ b/packages/gi-sdk/src/components/Initializer/info.ts
@@ -0,0 +1,17 @@
+import $i18n from '../../i18n';
+const info = {
+ id: 'Initializer',
+ name: $i18n.get({ id: 'basic.components.Initializer.info.Initializer', dm: '初始化器' }),
+ desc: $i18n.get({
+ id: 'basic.components.Initializer.info.RequiredInitializeQueryGraphData',
+ dm: '必选!初始化查询图数据与图模型',
+ }),
+ // icon: 'icon-export',
+ cover: 'http://xxxx.jpg',
+ category: 'system-interaction',
+ type: 'INITIALIZER',
+ // 申明需要实现的服务名
+ services: ['GI_SERVICE_INTIAL_GRAPH', 'GI_SERVICE_SCHEMA'],
+ docs: 'https://www.yuque.com/antv/gi/eedyuy',
+};
+export default info;
diff --git a/packages/gi-sdk/src/components/Initializer/registerMeta.ts b/packages/gi-sdk/src/components/Initializer/registerMeta.ts
new file mode 100644
index 000000000..fce82b19b
--- /dev/null
+++ b/packages/gi-sdk/src/components/Initializer/registerMeta.ts
@@ -0,0 +1,62 @@
+import $i18n from '../../i18n';
+import { utils } from '../../index';
+import info from './info';
+
+export default context => {
+ const { services, engineId } = context;
+ const { options: initializerServiceOptions, defaultValue: defaultInitializerService } =
+ utils.getServiceOptionsByEngineId(services, info.services[0], engineId);
+ const { options: schemaServiceOptions, defaultValue: defaultschemaService } = utils.getServiceOptionsByEngineId(
+ services,
+ info.services[1],
+ engineId,
+ );
+ return {
+ serviceId: {
+ title: $i18n.get({ id: 'basic.components.Initializer.registerMeta.InitializeAQuery', dm: '初始化查询' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Select',
+ 'x-component-props': {
+ options: initializerServiceOptions,
+ },
+ default: defaultInitializerService,
+ },
+ schemaServiceId: {
+ title: $i18n.get({ id: 'basic.components.Initializer.registerMeta.QueryGraphModel', dm: '查询图模型' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Select',
+ 'x-component-props': {
+ options: schemaServiceOptions,
+ },
+ default: defaultschemaService,
+ },
+
+ // 注意⚠️:GI_INITIALIZER 是必须的属性字段,千万不要漏掉
+ GI_INITIALIZER: {
+ title: $i18n.get({ id: 'basic.components.Initializer.registerMeta.DefaultStartup', dm: '默认启动' }),
+ type: 'boolean',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ 'x-component-props': {
+ disabled: true,
+ },
+ default: true,
+ },
+ aggregate: {
+ title: $i18n.get({ id: 'basic.components.Initializer.registerMeta.SummaryEdge', dm: '汇总边' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: false,
+ },
+ transByFieldMapping: {
+ title: $i18n.get({ id: 'basic.components.Initializer.registerMeta.EnableFieldMapping', dm: '开启字段映射' }),
+ type: 'boolean',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: false,
+ },
+ };
+};
diff --git a/packages/gi-sdk/src/components/SimpleEdge/index.tsx b/packages/gi-sdk/src/components/SimpleEdge/index.tsx
new file mode 100644
index 000000000..d409ccd47
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleEdge/index.tsx
@@ -0,0 +1,23 @@
+import registerMeta from './registerMeta';
+import registerTransform from './registerTransform';
+import $i18n from '../../i18n';
+const registerShape = Graphin => {
+ // graphinEdge 已经在内部注册成功
+};
+/** index.md 中解析得到默认值,也可用户手动修改 */
+const info = {
+ id: 'SimpleEdge',
+ category: 'edge',
+ name: $i18n.get({ id: 'basic.elements.SimpleEdge.OfficialSide', dm: '官方边' }),
+ desc: 'SimpleEdge',
+ cover: 'http://xxxx.jpg',
+ type: 'EDGE',
+ docs: 'https://www.yuque.com/antv/gi/ce260nnvfvagqszi',
+};
+
+export default {
+ info,
+ registerShape,
+ registerMeta,
+ registerTransform,
+};
diff --git a/packages/gi-sdk/src/components/SimpleEdge/registerMeta.tsx b/packages/gi-sdk/src/components/SimpleEdge/registerMeta.tsx
new file mode 100644
index 000000000..370c17dd7
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleEdge/registerMeta.tsx
@@ -0,0 +1,327 @@
+import $i18n from '../../i18n';
+import { defaultConfig } from './registerTransform';
+const { advanced, color, size } = defaultConfig;
+const { keyshape, label, animate } = advanced;
+
+const registerMeta = context => {
+ const { keys, schemaData } = context;
+
+ const schema = {
+ type: 'object',
+ properties: {
+ color: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Color', dm: '颜色' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: color,
+ },
+ size: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Size', dm: '大小' }),
+ type: 'number',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ default: size,
+ },
+ label: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Text', dm: '文本' }),
+ type: 'string',
+ // enum: keys.map(c => {
+ // return {
+ // label: `${c.id} (${c.type})`,
+ // value: c.id,
+ // };
+ // }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'GroupSelect',
+ 'x-component-props': {
+ mode: 'multiple',
+ schemaData: schemaData.edges,
+ },
+ },
+ advancedPanel: {
+ type: 'void',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse',
+ 'x-component-props': {
+ className: 'gi-assets-elements-advance-panel',
+ ghost: true,
+ },
+ properties: {
+ advanced: {
+ type: 'object',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.AdvancedConfiguration', dm: '高级配置' }),
+ key: 'advanced-panel',
+ },
+ properties: {
+ panel: {
+ type: 'void',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse',
+ 'x-component-props': {
+ className: 'gi-assets-elements-panel',
+ style: {},
+ ghost: true,
+ },
+ properties: {
+ keyshape: {
+ type: 'object',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Shape', dm: '形状' }),
+ key: 'icon-panel',
+ },
+ properties: {
+ hasArrow: {
+ type: 'boolean',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Arrow', dm: '箭头' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: true,
+ },
+ customPoly: {
+ type: 'boolean',
+ title: $i18n.get({
+ id: 'basic.elements.SimpleEdge.registerMeta.DefineRadians',
+ dm: '定义弧度',
+ }),
+ default: keyshape.customPoly,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ 'x-reactions': [
+ {
+ target: 'advanced.keyshape.poly',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.icon.fill',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.icon.size',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ ],
+ },
+ poly: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Radian', dm: '弧度' }),
+ type: 'number',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ default: keyshape.poly,
+ },
+
+ lineDash: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.DottedLine', dm: '虚线' }),
+ type: 'array',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Offset',
+ 'x-component-props': {
+ min: -100,
+ max: 100,
+ },
+ default: keyshape.lineDash,
+ },
+ opacity: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Transparency', dm: '透明度' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ default: keyshape.opacity,
+ },
+ },
+ },
+ label: {
+ type: 'object',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Label', dm: '标签' }),
+ key: 'keyshape-panel',
+ },
+ properties: {
+ visible: {
+ type: 'boolean',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Visible', dm: '显隐' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: label.visible,
+ },
+ fontSize: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Size', dm: '大小' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ default: label.fontSize,
+ },
+ offset: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Offset', dm: '偏移' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Offset',
+ 'x-component-props': {
+ min: -100,
+ max: 100,
+ },
+ default: label.offset,
+ },
+ fill: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Color', dm: '颜色' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: label.fill,
+ },
+ backgroundEnable: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Background', dm: '背景' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: label.backgroundEnable,
+ },
+ backgroundFill: {
+ type: 'string',
+ title: $i18n.get({
+ id: 'basic.elements.SimpleEdge.registerMeta.BackgroundColor',
+ dm: '背景色',
+ }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: label.backgroundFill,
+ },
+ backgroundStroke: {
+ type: 'string',
+ title: $i18n.get({
+ id: 'basic.elements.SimpleEdge.registerMeta.BackgroundStroke',
+ dm: '背景描边',
+ }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: label.backgroundStroke,
+ },
+ },
+ },
+ animate: {
+ type: 'object',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Animation', dm: '动画' }),
+ key: 'aniamte-panel',
+ },
+ properties: {
+ visible: {
+ type: 'boolean',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Switch', dm: '开关' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: animate.visible,
+ 'x-reactions': [
+ {
+ target: 'advanced.animate.type',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.animate.dotColor',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.animate.repeat',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.animate.duration',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ ],
+ },
+ type: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Type', dm: '类型' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Select',
+ enum: [
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Ball', dm: '圆球' }),
+ value: 'circle-running',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.DottedLine', dm: '虚线' }),
+ value: 'line-dash',
+ },
+ {
+ label: $i18n.get({
+ id: 'basic.elements.SimpleEdge.registerMeta.GradualLength',
+ dm: '渐长',
+ }),
+ value: 'line-growth',
+ },
+ ],
+
+ default: animate.type,
+ },
+ dotColor: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.BallColor', dm: '圆球颜色' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: animate.dotColor,
+ },
+ repeat: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Repeat', dm: '重复' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: animate.repeat,
+ },
+ duration: {
+ title: $i18n.get({ id: 'basic.elements.SimpleEdge.registerMeta.Duration', dm: '时长' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ default: animate.duration,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ return schema;
+};
+export default registerMeta;
diff --git a/packages/gi-sdk/src/components/SimpleEdge/registerShape.ts b/packages/gi-sdk/src/components/SimpleEdge/registerShape.ts
new file mode 100644
index 000000000..0ffc499c1
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleEdge/registerShape.ts
@@ -0,0 +1,4 @@
+const registerShape = Graphin => {
+ // graphinEdge 已经注册成功
+};
+export default registerShape;
diff --git a/packages/gi-sdk/src/components/SimpleEdge/registerTransform.ts b/packages/gi-sdk/src/components/SimpleEdge/registerTransform.ts
new file mode 100644
index 000000000..90ebdb62a
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleEdge/registerTransform.ts
@@ -0,0 +1,199 @@
+import { Utils } from '@antv/graphin';
+import type { GIEdgeConfig } from '../../index';
+
+const defaultEdgeTheme = {
+ primaryEdgeColor: '#ddd',
+ edgeSize: 1,
+ mode: 'light' as 'light' | 'dark',
+};
+
+const defaultEdgeStyles = Utils.getEdgeStyleByTheme(defaultEdgeTheme);
+
+const { style } = defaultEdgeStyles;
+const { keyshape, label } = style;
+
+export const defaultConfig = {
+ size: defaultEdgeTheme.edgeSize,
+ color: defaultEdgeTheme.primaryEdgeColor,
+ label: [],
+ advanced: {
+ keyshape: {
+ customPoly: false,
+ poly: 0,
+ lineDash: [0, 0],
+ // lineAppendWidth: keyshape.lineWidth,
+ opacity: keyshape.strokeOpacity,
+ hasArrow: true,
+ },
+ label: {
+ visible: true,
+ fontSize: label.fontSize,
+ offset: [0, 0],
+ fill: label.fill,
+ backgroundEnable: true,
+ backgroundFill: '#fff',
+ backgroundStroke: '#fff',
+ backgroundOpaciy: 1,
+ opacity: 1,
+ },
+ animate: {
+ visible: false,
+ type: 'circle-running',
+ dotColor: 'red',
+ repeat: true,
+ duration: 3000,
+ },
+ },
+ status: {
+ minZoom: {
+ label: {
+ opacity: 0,
+ },
+ 'label-background': {
+ opacity: 0,
+ },
+ },
+ },
+};
+
+export type EdgeConfig = typeof defaultConfig;
+
+/** 数据映射函数 需要根据配置自动生成*/
+const transform = (edges, config: GIEdgeConfig, reset?: boolean) => {
+ try {
+ const { color: color_CFG, size: size_CFG, label: LABEL_KEYS, advanced, status: defaultStatus } = defaultConfig;
+
+ const { keyshape: keyshape_CFG } = advanced;
+
+ const transEdge = (edge, index) => {
+ // properties
+ const { source, target } = edge;
+ const id = edge.id || `${source}-${target}-${index}`;
+ const data = edge.data || edge.properties || edge;
+ const isLoop = edge.source === edge.target; //edge.style && edge.style.keyshape && edge.style.keyshape.type === 'loop';
+ const isPoly = edge.isMultiple;
+ let endArrow = {};
+ const { customPoly, hasArrow } = keyshape_CFG;
+ if (!hasArrow) {
+ //@ts-ignore
+ endArrow = {
+ endArrow: {
+ path: '',
+ },
+ };
+ }
+ const shape: any = {};
+ if (isLoop) {
+ shape.type = 'loop';
+ shape.loop = { ...edge.style?.keyshape.loop };
+ }
+ if (isPoly) {
+ shape.type = 'poly';
+ shape.poly = { ...edge.style?.keyshape.poly };
+ }
+ if (!isPoly && !isLoop) {
+ //只有直线的时候才支持设置弧度,多边的默认是系统分配的弧度
+ shape.type = 'poly';
+ shape.poly = {
+ distance: advanced.keyshape.poly,
+ };
+ }
+ if (customPoly) {
+ //如果用户要强行自定义弧度,那就随他去吧
+ shape.poly = {
+ distance: advanced.keyshape.poly,
+ };
+ }
+
+ /** LABEL */
+ // const LABEL_VALUE = LABEL_KEYS.map(l => data[l]).join('_');
+
+ const LABEL_VALUE = LABEL_KEYS.map((d: string) => {
+ /**
+ * 兼容性处理:原先的label 逻辑是 ${type}.${properpertiesKey}
+ * 现在改为 ${type}^^${properpertiesKey}
+ */
+ const newLabelArray = d.split('^^');
+ const oldLabelArray = d.split('.');
+ let [edgeType, propObjKey, propName] = newLabelArray;
+ const isOld = newLabelArray.length === 1 && newLabelArray[0].split('.').length > 1;
+ if (isOld) {
+ edgeType = oldLabelArray[0];
+ propObjKey = oldLabelArray[1];
+ propName = oldLabelArray[2];
+ }
+
+ // const [edgeType, propObjKey, propName] = d.split('^^');
+
+ // 只有当 nodeType 匹配时才取对应的属性值
+ if (edge.edgeType || 'UNKNOW' === edgeType) {
+ // propName 存在,则 propObjKey 值一定为 properties
+ if (propName) {
+ return data[propObjKey][propName];
+ }
+ /** 如果有汇总边,则强制使用汇总边的文本展示 */
+ const { aggregate } = data;
+ if (aggregate) {
+ const sum = aggregate.reduce((acc, curr) => {
+ const val = curr.data[propObjKey];
+ if (typeof val === 'number') {
+ acc = acc + val;
+ return acc;
+ } else {
+ return '';
+ }
+ }, 0);
+ if (sum === '') {
+ return data['aggregateCount'];
+ }
+ return `(${aggregate.length} 条):${sum.toFixed(2)}`;
+ }
+ return data[propObjKey];
+ }
+
+ return data[edgeType];
+ })
+ .filter(d => d)
+ .join('\n');
+
+ const label: any = {
+ value: LABEL_VALUE,
+ offset: advanced.label.offset,
+ fontSize: advanced.label.fontSize,
+ fill: advanced.label.fill,
+ opacity: advanced.label.opacity,
+ };
+ if (!advanced.label.visible) {
+ label.value = '';
+ }
+ if (advanced.label.backgroundEnable) {
+ label.background = {
+ fill: advanced.label.backgroundFill,
+ stroke: advanced.label.backgroundStroke,
+ opacity: advanced.label.backgroundOpaciy,
+ };
+ }
+
+ let preStyle = (edge && edge.style) || {};
+
+ if (reset) {
+ preStyle = {};
+ }
+
+ return {
+ ...edge,
+ source,
+ target,
+ id,
+ data,
+ type: 'graphin-line',
+ edgeType: edge.edgeType || 'UNKOWN',
+ };
+ };
+ return transEdge;
+ } catch (error) {
+ console.error('parse transform error:', error);
+ return edge => edge;
+ }
+};
+export default transform;
diff --git a/packages/gi-sdk/src/components/SimpleNode/index.tsx b/packages/gi-sdk/src/components/SimpleNode/index.tsx
new file mode 100644
index 000000000..d9ed4561c
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleNode/index.tsx
@@ -0,0 +1,23 @@
+import registerMeta from './registerMeta';
+import registerShape from './registerShape';
+import registerTransform, { defaultConfig } from './registerTransform';
+
+/** index.md 中解析得到默认值,也可用户手动修改 */ import $i18n from '../../i18n';
+const info = {
+ id: 'SimpleNode',
+ category: 'node',
+ type: 'NODE',
+ name: $i18n.get({ id: 'basic.elements.SimpleNode.OfficialNode', dm: '官方节点' }),
+ icon: 'icon-smile',
+ desc: $i18n.get({ id: 'basic.elements.SimpleNode.OfficialNode', dm: '官方节点' }),
+ cover: 'https://gw.alipayobjects.com/mdn/rms_0d75e8/afts/img/A*myb8SrnSy0cAAAAAAAAAAAAAARQnAQ',
+ docs: 'https://www.yuque.com/antv/gi/mkrt58kk7m8qi0cu',
+};
+
+export default {
+ info,
+ defaultProps: defaultConfig,
+ registerShape,
+ registerMeta,
+ registerTransform,
+};
diff --git a/packages/gi-sdk/src/components/SimpleNode/registerMeta.tsx b/packages/gi-sdk/src/components/SimpleNode/registerMeta.tsx
new file mode 100644
index 000000000..eacb88064
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleNode/registerMeta.tsx
@@ -0,0 +1,342 @@
+import $i18n from '../../i18n';
+import { defaultConfig } from './registerTransform';
+
+const { icon, keyshape, label, badge } = defaultConfig.advanced;
+const registerMeta = context => {
+ const { schemaData } = context;
+ const schema = {
+ type: 'object',
+ properties: {
+ size: {
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Size', dm: '大小' }),
+ type: 'number',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ default: defaultConfig.size,
+ },
+ color: {
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Color', dm: '颜色' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: defaultConfig.color,
+ },
+ label: {
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Text', dm: '文本' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'GroupSelect',
+ 'x-component-props': {
+ mode: 'multiple',
+ schemaData: schemaData.nodes,
+ },
+ },
+ advancedPanel: {
+ type: 'void',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse',
+ 'x-component-props': {
+ className: 'gi-assets-elements-advance-panel',
+ // style: { background: 'blue' },
+ ghost: true,
+ },
+ properties: {
+ advanced: {
+ type: 'object',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.AdvancedConfiguration', dm: '高级配置' }),
+ // 暂时不设置高级配置默认收起,否则下面的 visible 控制就失效了
+ key: 'advanced-panel',
+ },
+ properties: {
+ panel: {
+ type: 'void',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse',
+ 'x-component-props': {
+ className: 'gi-assets-elements-panel',
+ style: {
+ // background: 'red',
+ // margin: '-16px',
+ },
+ ghost: true,
+ },
+ properties: {
+ icon: {
+ type: 'object',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Icon', dm: '图标' }),
+ key: 'icon-panel',
+ },
+ properties: {
+ visible: {
+ type: 'boolean',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Visible', dm: '显隐' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ 'x-reactions': [
+ {
+ target: 'advanced.icon.type',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.icon.value',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.icon.fill',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.icon.size',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ ],
+ },
+ type: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Type', dm: '类型' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Select',
+ enum: [
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Text', dm: '文本' }),
+ value: 'text',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.FontIcon', dm: '字体图标' }),
+ value: 'font',
+ },
+ ],
+ default: icon.type,
+ },
+ value: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Icon', dm: '图标' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'IconPicker',
+ default: icon.value,
+ },
+ fill: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Color', dm: '颜色' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: icon.fill,
+ },
+ // size: {
+ // type: 'string',
+ // title: '大小',
+ // 'x-decorator': 'FormItem',
+ // 'x-component': 'NumberPicker',
+ // default: icon.size,
+ // },
+ },
+ },
+ keyshape: {
+ type: 'object',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Node', dm: '节点' }),
+ key: 'keyshape-panel',
+ },
+ properties: {
+ fillOpacity: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Transparency', dm: '透明度' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ max: 1,
+ min: 0,
+ default: keyshape.fillOpacity,
+ },
+ },
+ },
+ label: {
+ type: 'object',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Text', dm: '文本' }),
+ key: 'label-panel',
+ },
+ properties: {
+ visible: {
+ type: 'boolean',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Switch', dm: '开关' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: label.visible,
+ 'x-reactions': [
+ {
+ target: 'advanced.label.fill',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.label.fontSize',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.label.position',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ ],
+ },
+ fill: {
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Color', dm: '颜色' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'ColorInput',
+ default: label.fill,
+ },
+ fontSize: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Size', dm: '大小' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ max: 100,
+ min: 12,
+ default: label.fontSize,
+ },
+ position: {
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Location', dm: '位置' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Select',
+ enum: [
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Top', dm: '顶部' }),
+ value: 'top',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Bottom', dm: '底部' }),
+ value: 'bottom',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.LeftSide', dm: '左侧' }),
+ value: 'left',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.RightSide', dm: '右侧' }),
+ value: 'right',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Middle', dm: '中间' }),
+ value: 'center',
+ },
+ ],
+
+ default: label.position,
+ },
+ },
+ },
+ badge: {
+ type: 'object',
+ 'x-component': 'FormCollapse.CollapsePanel',
+ 'x-component-props': {
+ header: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Logo', dm: '徽标' }),
+ key: 'badge-panel',
+ },
+ properties: {
+ visible: {
+ type: 'boolean',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Visible', dm: '显隐' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ default: badge.visible,
+ 'x-reactions': [
+ {
+ target: 'advanced.badge.type',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ {
+ target: 'advanced.badge.value',
+ fulfill: {
+ state: {
+ visible: '{{$self.value}}',
+ },
+ },
+ },
+ ],
+ },
+ type: {
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Type', dm: '类型' }),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Select',
+ enum: [
+ {
+ label: $i18n.get({
+ id: 'basic.elements.SimpleNode.registerMeta.FieldMapping',
+ dm: '字段映射',
+ }),
+ value: 'mapping',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Text', dm: '文本' }),
+ value: 'text',
+ },
+ {
+ label: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.FontIcon', dm: '字体图标' }),
+ value: 'font',
+ },
+ ],
+
+ default: badge.type,
+ },
+ value: {
+ type: 'string',
+ title: $i18n.get({ id: 'basic.elements.SimpleNode.registerMeta.Text', dm: '文本' }),
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ default: badge.value,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ return schema;
+};
+export default registerMeta;
diff --git a/packages/gi-sdk/src/components/SimpleNode/registerShape.ts b/packages/gi-sdk/src/components/SimpleNode/registerShape.ts
new file mode 100644
index 000000000..ea8a3d952
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleNode/registerShape.ts
@@ -0,0 +1,4 @@
+const registerShape = Graphin => {
+ // graphinNode 已经注册成功
+};
+export default registerShape;
diff --git a/packages/gi-sdk/src/components/SimpleNode/registerTransform.ts b/packages/gi-sdk/src/components/SimpleNode/registerTransform.ts
new file mode 100644
index 000000000..026f44c0f
--- /dev/null
+++ b/packages/gi-sdk/src/components/SimpleNode/registerTransform.ts
@@ -0,0 +1,273 @@
+import { Utils } from '@antv/graphin';
+import type { GINodeConfig } from '../../index';
+import { icons } from '../../index';
+
+const defaultNodeTheme = {
+ primaryColor: '#FF6A00',
+ nodeSize: 26,
+ mode: 'light' as 'light' | 'dark',
+};
+
+const getLabel = (data, LABEL_KEYS) => {
+ return LABEL_KEYS.map((d: string) => {
+ /**
+ * 兼容性处理:原先的label 逻辑是 ${type}.${properpertiesKey}
+ * 现在改为 ${type}^^${properpertiesKey}
+ */
+ const [newNodeType, newLabelKey] = d.split('^^');
+ const [oldNodeType, oldLabelKey] = d.split('.');
+ const key = newLabelKey || oldLabelKey || 'id';
+ return data[key];
+ })
+ .filter(d => d)
+ .join('\n');
+};
+
+const getIconStyleByConfig = (style, data) => {
+ const { keyshape } = style;
+ if (!style.icon || !keyshape) {
+ return {};
+ }
+ const icon = { ...style.icon };
+ const { value } = icon;
+
+ if (icon.visible) {
+ if (icon.type === 'image') {
+ return {
+ fill: 'transparent',
+ size: [keyshape.size, keyshape.size],
+ type: 'image',
+ clip: { r: keyshape.size / 2 },
+ value: value,
+ };
+ }
+
+ if (icon.type === 'font') {
+ return {
+ ...icon,
+ size: keyshape.size / 2,
+ type: 'font',
+ fontFamily: 'iconfont',
+ value: icons[value] || '',
+ fill: icon.fill || keyshape.fill,
+ };
+ }
+ if (icon.type === 'text') {
+ return {
+ ...icon,
+ fontSize: keyshape.size / 4,
+ fill: '#fff',
+ value: value,
+ };
+ }
+ return {
+ ...icon,
+ };
+ }
+ return {
+ ...icon,
+ visible: false,
+ value: '',
+ };
+};
+
+const getBadgesStyleByConfig = (style, data) => {
+ const { badge, keyshape } = style;
+ if (!badge || !keyshape) {
+ return [];
+ }
+
+ const { visible, value, color } = badge;
+
+ if (visible) {
+ const size = Math.round(keyshape.size / 3);
+ const fontSize = size / 2;
+ badge.size = size;
+ badge.stroke = color || keyshape.stroke;
+
+ if (badge.type === 'mapping') {
+ const b = {
+ type: 'text',
+ size,
+ stroke: keyshape.stroke,
+ fill: '#fff',
+ color: keyshape.fill,
+ visible: Boolean(data[value]),
+ value: data[value],
+ fontSize,
+ };
+ return [b];
+ }
+ if (badge.type === 'font') {
+ badge.type = 'font';
+ badge.fontFamily = 'graphin';
+ badge.value = icons[value] || '';
+ }
+ if (badge.type === 'text') {
+ badge.fill = '#fff';
+ badge.color = color || keyshape.fill;
+ badge.value = value;
+ badge.fontSize = fontSize;
+ }
+ return [badge];
+ }
+ return [];
+};
+
+const defaultNodeStyles = Utils.getNodeStyleByTheme(defaultNodeTheme);
+
+const { style, status } = defaultNodeStyles;
+const { keyshape, halo, label, icon } = style;
+
+export const defaultConfig = {
+ size: defaultNodeTheme.nodeSize,
+ color: defaultNodeTheme.primaryColor,
+ label: [],
+ advanced: {
+ keyshape: {
+ ...keyshape,
+ fillOpacity: 0.8,
+ },
+ label: {
+ ...label,
+ opacity: 1,
+ visible: true,
+ },
+ icon: {
+ ...icon,
+ fill: '#fff',
+ type: 'font',
+ value: '',
+ opacity: 1,
+ visible: false,
+ },
+ badge: {
+ visible: false,
+ position: 'RT',
+ type: 'text',
+ value: '',
+ size: Math.round(keyshape.size / 3), // 徽标占据九宫格的最右上角,所以/3
+ fill: '#fff',
+ color: '#fff',
+ stroke: keyshape.stroke,
+ isMapping: false,
+ },
+ halo: {
+ ...halo,
+ visible: false,
+ lineWidth: 0,
+ },
+ },
+ status: {
+ minZoom: {
+ label: {
+ opacity: 0,
+ },
+ icon: {
+ opacity: 0,
+ },
+ badges: {
+ opacity: 0,
+ },
+ },
+ },
+};
+export type NodeConfig = typeof defaultConfig;
+
+/** 数据映射函数 需要根据配置自动生成*/
+const transform = (_nodes, nodeConfig: GINodeConfig, reset?: boolean) => {
+ try {
+ /** 解构配置项 */
+
+ const { color, size, label: LABEL_KEYS, advanced, status: userStatus } = defaultConfig;
+
+ let isBug = false;
+ //@ts-ignore
+ if (!Object.is(advanced)) {
+ isBug = true;
+ }
+ const { halo } = isBug ? defaultConfig.advanced : advanced;
+ const transNode = node => {
+ // properties
+ const data = node.data || node.properties || node;
+
+ const keyshape = {
+ ...advanced.keyshape,
+ fill: color,
+ stroke: color,
+ size: size,
+ };
+ advanced.keyshape = keyshape;
+ const LABEL_VALUE = getLabel(data, LABEL_KEYS);
+ const icon = getIconStyleByConfig(advanced, data);
+ const badges = getBadgesStyleByConfig(advanced, data);
+
+ const label = {
+ ...advanced.label,
+ value: advanced.label.visible ? LABEL_VALUE : '',
+ };
+
+ let preStyle = (node && node.style) || {};
+ if (reset) {
+ preStyle = {};
+ }
+
+ const styleByConfig = {
+ keyshape,
+ label,
+ icon,
+ halo,
+ badges,
+ status: {
+ ...status,
+ ...userStatus,
+ highlight: {
+ keyshape: {
+ lineWidth: 4,
+ fillOpacity: 0.6,
+ },
+ },
+ active: {
+ halo: {
+ visible: true,
+ },
+ keyshape: {
+ lineWidth: 5,
+ },
+ },
+ /** 扩散的状态 */
+ query_start: {
+ halo: {
+ visible: true,
+ stroke: color,
+ lineWidth: 4,
+ lineDash: [8, 8],
+ },
+ },
+ query_normal: {
+ halo: {
+ visible: true,
+ stroke: color,
+ lineWidth: 1,
+ lineDash: [8, 8],
+ },
+ },
+ },
+ };
+
+ return {
+ ...node,
+ id: node.id,
+ data,
+ nodeType: node.nodeType || 'UNKNOW',
+ type: 'circle-node',
+ // 数据中的style还是优先级最高的
+ };
+ };
+ return transNode;
+ } catch (error) {
+ console.error('parse transform error:', error);
+ return node => node;
+ }
+};
+export default transform;
diff --git a/packages/gi-sdk/src/hooks/useComponents.tsx b/packages/gi-sdk/src/hooks/useComponents.tsx
index 3c7d4c29a..105fd44c4 100644
--- a/packages/gi-sdk/src/hooks/useComponents.tsx
+++ b/packages/gi-sdk/src/hooks/useComponents.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import DefaultInitializer from '../components/Initializer';
const DEFAULT_GICC_LAYOUT = {
id: 'EmptyLayout',
@@ -20,7 +21,7 @@ const useComponents = (state, propsComponentsCfg, ComponentAssets) => {
};
}, {});
- const { component: InitializerComponent } = ComponentAssets[initializer.id];
+ const { component: InitializerComponent } = ComponentAssets[initializer.id] || DefaultInitializer;
const { props: InitializerProps } = ComponentCfgMap[initializer.id];
diff --git a/packages/gi-sdk/src/index.tsx b/packages/gi-sdk/src/index.tsx
index 46bec5b0e..d461d442d 100644
--- a/packages/gi-sdk/src/index.tsx
+++ b/packages/gi-sdk/src/index.tsx
@@ -1,4 +1,3 @@
-/** export */
import pkg from '../package.json';
import {
deepClone,
@@ -9,6 +8,23 @@ import {
GIAC_PROPS,
} from './components/const';
import GIAComponent from './components/GIAC';
+export { Icon, icons } from '@antv/gi-common-components';
+export { Compatible } from '@antv/graphin';
+export { default as CollapseCard } from './components/CollapseCard';
+export type { IGIAC } from './components/const';
+export { default as EngineBanner } from './components/EngineBanner';
+export { default as EngineServer } from './components/EngineServer';
+/** default assets */
+export { default as Initializer } from './components/Initializer';
+export { default as SimpleEdge } from './components/SimpleEdge';
+export { default as SimpleNode } from './components/SimpleNode';
+/** default assets */
+export { default as Studio } from './components/Studio';
+export { Info } from './constants/info';
+export { default as useContainer } from './hooks/useContainer';
+export { Shortcuts, useShortcuts } from './utils';
+export { common };
+
import template from './constants/template';
import { useContext } from './context';
import GISDK from './GISDK';
@@ -27,26 +43,16 @@ const extra = {
deepClone,
GIAComponent,
};
-export { Icon, icons } from '@antv/gi-common-components';
-export { default as CollapseCard } from './components/CollapseCard';
-export type { IGIAC } from './components/const';
-export { default as EngineBanner } from './components/EngineBanner';
-export { default as EngineServer } from './components/EngineServer';
-export { default as Studio } from './components/Studio';
-export { Info } from './constants/info';
-export { default as useContainer } from './hooks/useContainer';
-export { Shortcuts, useShortcuts } from './utils';
-export { common };
const common = {
createDownload,
};
+
// export { default as Icon } from './components/Icon';
/** export typing */
-export { COLORS, IEdgeSchema, INodeSchema } from './process/schema';
-
export { changeLanguage, formatMessage, getCurrentLanguage, LANGUAGE_KEY_NAME } from './process/locale';
-export type { IGraphData } from './process/schema';
+export { COLORS } from './process/schema';
+export type { IEdgeSchema, IGraphData, INodeSchema } from './process/schema';
export type {
AssetCategory,
AssetInfo,
diff --git a/packages/gi-sdk/src/process/loaderAssets.tsx b/packages/gi-sdk/src/process/loaderAssets.tsx
index 5a3b005de..185b15994 100644
--- a/packages/gi-sdk/src/process/loaderAssets.tsx
+++ b/packages/gi-sdk/src/process/loaderAssets.tsx
@@ -42,6 +42,7 @@ export const loadJS = memoize(async (options: AssetPackage) => {
};
});
}, JSON.stringify);
+
export const loader = async (options: AssetPackage[]) => {
return Promise.all([
...options.map(opt => {
diff --git a/packages/gi-sdk/src/process/transDataByConfig.ts b/packages/gi-sdk/src/process/transDataByConfig.ts
index 24ed00fa2..30a930e34 100644
--- a/packages/gi-sdk/src/process/transDataByConfig.ts
+++ b/packages/gi-sdk/src/process/transDataByConfig.ts
@@ -1,4 +1,6 @@
import { GraphinData, IUserEdge } from '@antv/graphin';
+import SimpleEdge from '../components/SimpleEdge';
+import SimpleNode from '../components/SimpleNode';
import { GIAssets, GIConfig } from '../typing';
import { uniqueElementsBy } from './common';
import { filterByRules } from './filterByRules';
@@ -49,7 +51,8 @@ export const transDataByConfig = (
}
const Element = ElementAssets[id];
const filterData = filterByRules(elementData, { logic, expressions });
- return Element.registerTransform(filterData, item, reset);
+ const elementMapper = Element.registerTransform(filterData, item, reset);
+ return filterData.map(elementMapper);
})
.reduce((acc, curr) => {
return [...curr, ...acc];
@@ -63,9 +66,21 @@ export const transDataByConfig = (
const restElements = elementData.filter(n => {
return uniqueIds.indexOf(n.id) === -1;
});
+ //@ts-ignore
+ let elementAsset = ElementAssets[basicConfig.id];
+ if (!elementAsset) {
+ if (elementType === 'edges') {
+ //@ts-ignore
+ elementAsset = SimpleEdge;
+ } else {
+ //@ts-ignore
+ elementAsset = SimpleNode;
+ }
+ }
//@ts-ignore
- const restData = ElementAssets[basicConfig.id].registerTransform(restElements, basicConfig, reset);
+ const restMapper = elementAsset.registerTransform(restElements, basicConfig, reset);
+ const restData = restElements.map(restMapper);
const nodes = [...uniqueElements, ...restData];
console.timeEnd(`${elementType.toUpperCase()}_TRANS_COST`);
diff --git a/packages/gi-sdk/src/typing.ts b/packages/gi-sdk/src/typing.ts
index bc7cd271f..665c61f33 100644
--- a/packages/gi-sdk/src/typing.ts
+++ b/packages/gi-sdk/src/typing.ts
@@ -101,7 +101,7 @@ export interface Props {
schemaData?: GraphSchemaData;
style?: React.CSSProperties;
className?: string;
- children?: React.ReactChildren | JSX.Element | JSX.Element[];
+ children?: React.ReactNode[];
}
export type AssetType =