G6 数据操作 API 完全指南:从查询、增删改到层级遍历
【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址: https://gitcode.com/gh_mirrors/g6/G6
导读
本文以 G6(JavaScript 图可视化框架)官方数据 API 文档(packages/site/docs/api/data.zh.md)为主体,系统梳理图数据从查询、写入到层级遍历的完整生命周期操作。读者学完本文后,将掌握getData/getNodeData等 29 个数据 API 的签名、参数、返回值与函数式写法,理解setData差异更新、addChildrenData自动建边、节点删除级联清理等底层实现原理,并能在实际项目中正确组织节点、边、组合数据。
一、G6 数据模型与核心类型
G6 是数据驱动的图可视化引擎,使用标准 JSON 描述图结构。图数据(GraphData)由三部分组成:nodes(节点数据)、edges(边数据)、combos(组合数据),其类型定义位于 packages/g6/src/spec/data.ts:
interface GraphData { nodes?: NodeData[]; // 节点数据 edges?: EdgeData[]; // 边数据 combos?: ComboData[]; // 组合数据 }1.1 节点数据(NodeData)
interface NodeData { id: string; // 节点 ID(必选,唯一标识) type?: string; // 节点类型(内置或自定义节点名称) data?: Record<string, any>; // 自定义业务数据,可在样式映射回调中获取 style?: Record<string, any>; // 节点样式:位置、大小、颜色等 states?: string[]; // 节点初始状态 combo?: string | null; // 所属组合 ID,无则为 null children?: string[]; // 子节点 ID 数组(仅树图场景) depth?: number; // 节点深度(仅树图场景) }完整字段说明可参考 数据使用手册。注意combo与children是两种相互独立的层级结构:combo表达"组合层级"(节点归属哪个组合),children表达"树层级"(树图中父子节点关系)。
1.2 边数据(EdgeData)
interface EdgeData { source: string; // 起点 ID(必选) target: string; // 终点 ID(必选) id?: string; // 边 ID(可选) type?: string; // 边类型 data?: Record<string, any>; // 自定义业务数据 style?: Record<string, any>; // 边样式:线条颜色、宽度、箭头等 states?: string[]; // 边初始状态 }边 ID 的生成规则:当边数据未显式提供id时,G6 会通过idOf工具函数以`${source}-${target}`作为默认边 ID(见 packages/g6/src/utils/id.ts)。因此graph.removeEdgeData(['node1-node2'])可以直接删除由node1、node2构成的默认 ID 边。
1.3 组合数据(ComboData)
interface ComboData { id: string; // 组合 ID(必选) type?: string; // 组合类型 data?: Record<string, any>; // 组合自定义数据 style?: Record<string, any>; // 组合样式 states?: string[]; // 组合初始状态 combo?: string | null; // 父组合 ID(支持嵌套) }1.4 操作辅助类型
除三种元素数据外,数据 API 还涉及以下类型(定义于 packages/g6/src/types/data.ts):
| 类型 | 定义 | 用途 |
|---|---|---|
ID | string | 元素 ID |
DataID | { nodes?: ID[]; edges?: ID[]; combos?: ID[] } | 批量删除时按类型组织 ID |
ElementDatum | NodeData \| EdgeData \| ComboData | 通用元素数据(忽略类型) |
NodeLikeData | NodeData \| ComboData | 节点或组合数据 |
PartialNodeLikeData<T> | Partial<T> & Pick<T, 'id'> | 更新数据:必须含id,其余字段可选 |
PartialEdgeData<T> | (Partial<T> & Pick<T,'source'\|'target'>) \| (Partial<T> & Pick<T,'id'>) | 更新边数据:必须含source+target或含id |
PartialGraphData | { nodes?; edges?; combos? }(均为 Partial 类型) | 部分图数据更新 |
HierarchyKey | 'tree' \| 'combo' | 指定层级类型:树层级或组合层级 |
最佳实践(详见 数据使用手册):避免使用与 G6 内部字段(
id、type、style等)同名的业务标识符;将业务数据放入data字段;通过样式映射回调根据业务数据动态设置视觉属性,实现数据与样式分离。
二、数据查询 API
2.1 Graph.getData():获取完整图数据
getData(): Required<GraphData>;- 返回值:包含全部节点、边、组合数据的完整图数据。从底层实现看,它分别委托
getNodeData()、getEdgeData()、getComboData()组装结果(见 packages/g6/src/runtime/data.ts)。
const graphData = graph.getData(); console.log('节点数据:', graphData.nodes); console.log('边数据:', graphData.edges); console.log('组合数据:', graphData.combos);2.2 getNodeData / getEdgeData / getComboData:按类型查询
三个 API 均支持无参全量、单 ID、ID 数组三种调用方式,签名完全对称:
// 节点 getNodeData(): NodeData[]; getNodeData(id: ID): NodeData; getNodeData(ids: ID[]): NodeData[]; // 边 getEdgeData(): EdgeData[]; getEdgeData(id: ID): EdgeData; getEdgeData(ids: ID[]): EdgeData[]; // 组合 getComboData(): ComboData[]; getComboData(id: ID): ComboData; getComboData(ids: ID[]): ComboData[];参数说明:
| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 元素 ID | string | - | 二选一 |
| ids | 元素 ID 数组 | string[] | - | 二选一 |
注意事项:查询单个元素时,传入的id必须真实存在,否则会抛出异常(从源码看,getNodeData(id)走model.getNode(id),不存在即报错,见 packages/g6/src/runtime/graph.ts)。
// 获取所有节点 const nodes = graph.getNodeData(); // 获取单个节点 const node = graph.getNodeData('node1'); console.log('节点位置:', node.style.x, node.style.y); // 获取多个节点 const [node1, node2] = graph.getNodeData(['node1', 'node2']); // 边的起点和终点 const edge = graph.getEdgeData('edge1'); console.log('边的起点和终点:', edge.source, edge.target); // 组合包含的节点 const combo = graph.getComboData('combo1'); console.log('组合包含的节点:', combo.children);2.3 Graph.getElementData():忽略类型的通用查询
getElementData(id: ID): ElementDatum; getElementData(ids: ID[]): ElementDatum[];⚠️注意:此 API 直接获取元素的数据而不必考虑元素类型。底层通过getElementType(id)判断元素是 node、edge 还是 combo 后再取对应数据(见 packages/g6/src/runtime/data.ts),因此可以混传节点与边的 ID。
const element = graph.getElementData('node-1'); console.log('元素数据:', element); const elements = graph.getElementData(['node-1', 'edge-1']); console.log('多个元素数据:', elements);2.4 Graph.getElementDataByState():按状态查询
getElementDataByState(elementType: 'node', state: string): NodeData[]; getElementDataByState(elementType: 'edge', state: string): EdgeData[]; getElementDataByState(elementType: 'combo', state: string): ComboData[];| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| elementType | 元素类型 | 'node' \| 'edge' \| 'combo' | - | ✓ |
| state | 状态 | string | - | ✓ |
实现上,该 API 先取回指定类型全部数据,再按datum.states?.includes(state)过滤(见 packages/g6/src/runtime/data.ts)。
内置状态(G6 内置主题已为以下状态预置了样式,见 packages/g6/src/themes/base.ts):
'selected'(选中:显示 halo 光圈、加粗描边)'highlight'(高亮:加粗描边)'active'(激活:显示 halo)'inactive'(非激活:降低透明度)'disabled'(禁用:降低透明度与填充色)
const selectedNodes = graph.getElementDataByState('node', 'selected'); console.log('选中的节点:', selectedNodes); const selectedEdges = graph.getElementDataByState('edge', 'selected'); console.log('选中的边:', selectedEdges); const selectedCombos = graph.getElementDataByState('combo', 'selected'); console.log('选中的组合:', selectedCombos);2.5 关系查询:getNeighborNodesData 与 getRelatedEdgesData
获取一跳邻居节点
getNeighborNodesData(id: ID): NodeData[];| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 节点或组合的 ID | string | - | ✓ |
底层调用 graphlib 的getNeighbors(id)获取与指定节点直接相连的所有节点(packages/g6/src/runtime/data.ts)。对应单元测试见 packages/g6/tests/unit/runtime/data.spec.ts。
const neighbors = graph.getNeighborNodesData('node-1'); console.log('邻居节点:', neighbors);获取关联边
getRelatedEdgesData(id: ID, direction?: EdgeDirection): EdgeData[];| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 节点或组合的 ID | string | - | ✓ |
| direction | 边的方向 | 'in' \| 'out' \| 'both' | 'both' |
direction为'in'表示入边(以该节点为 target),'out'表示出边(以该节点为 source),'both'为全部关联边。测试用例验证了方向过滤行为(packages/g6/tests/unit/runtime/data.spec.ts)。
const relatedEdges = graph.getRelatedEdgesData('node-1'); console.log('关联边:', relatedEdges);2.6 层级查询:父子、祖先、后代
G6 中树形层级('tree')与组合层级('combo')相互独立,对应不同的数据结构,因此部分层级 API 需要显式指定HierarchyKey(见 packages/g6/src/types/data.ts)。
getParentData():获取父元素
getParentData(id: ID, hierarchy: HierarchyKey): NodeLikeData | undefined;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 节点或组合的 ID | string | - | ✓ |
| hierarchy | 指定层级关系类型 | 'tree' \| 'combo' | - |
返回值:父元素数据(NodeData或ComboData),不存在则返回undefined。底层要求指定层级结构必须已存在(model.hasTreeStructure),否则同样返回undefined(packages/g6/src/runtime/data.ts)。
// 获取树图中节点的父节点 const treeParent = graph.getParentData('node1', 'tree'); // 获取组合中节点的父组合 const comboParent = graph.getParentData('node1', 'combo');getChildrenData():获取子元素
getChildrenData(id: ID): (NodeData | ComboData)[];| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 节点或组合的 ID | string | - | ✓ |
注意:
- 查询 combo 的子元素:如果
id对应的是 combo 元素,可直接通过此 API 获取其所有子元素。 - 查询节点的子元素:如果
id对应的是节点,仅当图数据为树结构(节点数据维护children字段)时才能获取到子元素,否则返回空数组。
从源码看,该 API 会根据元素类型自动选择层级结构:节点走TREE_KEY,组合走COMBO_KEY(packages/g6/src/runtime/data.ts)。测试中getChildrenData('combo-1')正确返回了 combo 下所有节点(packages/g6/tests/unit/runtime/data.spec.ts)。
// 获取组合的子元素 const children = graph.getChildrenData('combo1'); console.log('子节点数量:', children.length); // 处理每个子元素 children.forEach((child) => { console.log('子元素ID:', child.id); });getAncestorsData():获取全部祖先
getAncestorsData(id: ID, hierarchy: HierarchyKey): NodeLikeData[];| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 节点或组合的 ID | string | - | ✓ |
| hierarchy | 指定层级关系类型 | 'tree' \| 'combo' | - | ✓ |
返回值:祖先元素数据数组,从父节点到根节点的顺序排列。对应测试验证了getAncestorsData('Logistic regression', 'tree')返回['Classification', 'Modeling Methods'](packages/g6/tests/unit/runtime/data.spec.ts)。
// 获取树图中节点的所有祖先节点 const treeAncestors = graph.getAncestorsData('node1', 'tree'); console.log( '祖先节点路径:', treeAncestors.map((node) => node.id), ); // 获取组合中节点的所有父组合 const comboAncestors = graph.getAncestorsData('node1', 'combo');getDescendantsData():获取全部后代
getDescendantsData(id: ID): NodeLikeData[];| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 节点或组合的 ID | string | - | ✓ |
返回值:后代元素数据数组(NodeData[]或ComboData[])。从源码看,实现基于深度优先遍历(DFS),自动向下递归getChildrenData收集所有后代,且不含元素自身(packages/g6/src/runtime/data.ts)。
// 获取节点的所有后代 const descendants = graph.getDescendantsData('node1'); console.log('后代数量:', descendants.length); // 处理所有后代元素 descendants.forEach((descendant) => { console.log('后代元素ID:', descendant.id); });三、数据写入 API
3.1 Graph.setData():全量设置数据
setData(data: GraphData | ((prev: GraphData) => GraphData)): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| data | 新的图数据或返回新图数据的函数 | GraphData \| ((prev: GraphData) => GraphData) | - | ✓ |
全量替换 + 自动差异计算:设置全量数据会替换当前图中的所有数据,G6 会自动对比新旧数据的差异(进入/更新/退出三集合,见 packages/g6/src/runtime/data.ts),只对发生变化的元素执行新增、更新或删除,避免整图重绘。
// 直接设置数据 graph.setData({ nodes: [ { id: 'node1', style: { x: 100, y: 100 } }, { id: 'node2', style: { x: 200, y: 200 } }, ], edges: [{ id: 'edge1', source: 'node1', target: 'node2' }], }); // 使用函数式增量更新:获取当前图数据,并返回新的图数据 graph.setData((prev) => ({ ...prev, nodes: [...prev.nodes, { id: 'node3', style: { x: 300, y: 300 } }], }));3.2 新增数据:addData 与按类型添加
addData():新增元素数据
addData(data: GraphData | ((prev: GraphData) => GraphData)): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| data | 需要添加的图数据或返回新图数据的函数 | GraphData \| ((prev: GraphData) => GraphData) | - | ✓ |
从源码看,addData内部会先添加 combo、再添加 node、最后添加 edge,以确保组合关系与边的端点先就绪(packages/g6/src/runtime/data.ts)。
graph.addData({ nodes: [{ id: 'node-1' }, { id: 'node-2' }], edges: [{ source: 'node-1', target: 'node-2' }], });addNodeData():新增节点
addNodeData(data: NodeData[] | ((prev: NodeData[]) => NodeData[])): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| data | 要添加的节点数据或返回节点数据的函数 | NodeData[] \| (prev: NodeData[]) => NodeData[] | - | ✓ |
// 添加单个节点 graph.addNodeData([ { id: 'node1', style: { x: 100, y: 100 }, data: { label: '节点 1' }, }, ]); // 批量添加节点 graph.addNodeData([ { id: 'node2', style: { x: 200, y: 200 } }, { id: 'node3', style: { x: 300, y: 300 } }, ]); // 函数式添加 graph.addNodeData((prev) => [...prev, { id: 'node4', style: { x: 400, y: 400 } }]);addEdgeData():新增边
addEdgeData(data: EdgeData[] | ((prev: EdgeData[]) => EdgeData[])): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| data | 要添加的边数据或返回边数据的函数 | EdgeData[] \| ((prev: EdgeData[]) => EdgeData[]) | - | ✓ |
// 添加单条边 graph.addEdgeData([ { id: 'edge1', source: 'node1', target: 'node2', data: { weight: 1, label: '关系', }, }, ]); // 批量添加边 graph.addEdgeData([ { id: 'edge2', source: 'node2', target: 'node3' }, { id: 'edge3', source: 'node3', target: 'node1' }, ]); // 函数式添加 graph.addEdgeData((prev) => [...prev, { id: 'edge4', source: 'node1', target: 'node4' }]);addComboData():新增组合
addComboData(data: ComboData[] | ((prev: ComboData[]) => ComboData[])): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| data | 要添加的组合数据或返回组合数据的函数 | ComboData[] \| (prev: ComboData[]) => ComboData[] | - | ✓ |
graph.addComboData([{ id: 'combo1', children: ['node1', 'node2'] }]);也可以通过
combo字段直接建立嵌套组合(组合的combo指向父组合),详细组合操作示例见 数据使用手册。
addChildrenData():为树图节点添加子节点
addChildrenData(parentId: ID, childrenData: NodeData[]): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| parentId | 父节点 ID | string | - | ✓ |
| childrenData | 子节点数据 | NodeData[] | - | ✓ |
⚠️注意:为组合添加子节点请使用addNodeData/addComboData方法;本方法仅用于树图节点。
底层自动完成三件事:新增子节点 → 更新父节点的children数组 → 自动为父子创建边({ source: parentId, target: childId }),见 packages/g6/src/runtime/data.ts。
graph.addChildrenData('node1', [{ id: 'node2' }]);3.3 更新数据:局部更新 + 浅合并
更新类 API 的共同约定:只需要传入需要更新的字段,不必传入完整数据。底层通过mergeElementsData进行"第一层浅合并"——顶层属性逐字段合并,data、style下的二级数据则整体覆盖(见 packages/g6/src/utils/data.ts)。
// 通用更新:可同时更新节点与边 updateData(data: PartialGraphData | ((prev: GraphData) => PartialGraphData)): void; // 按类型更新 updateNodeData(data: NodeData[] | ((prev: NodeData[]) => NodeData[])): void; updateEdgeData(data: (PartialEdgeData<EdgeData>[] | ((prev: EdgeData[]) => PartialEdgeData<EdgeData>[]))): void; updateComboData(data: (ComboData[] | ((prev: ComboData[]) => ComboData[]))): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| data | 要更新的元素数据或返回元素数据的函数 | 见各签名 | - | ✓ |
更新边数据的特殊约束:updateEdgeData传入的每条数据必须满足"含source+target"或"含id"二者之一(PartialEdgeData类型约束);若修改了source/target,底层会调用updateEdgeSource/updateEdgeTarget重新接线(packages/g6/src/runtime/data.ts)。
// 通用更新 graph.updateData({ nodes: [{ id: 'node-1', style: { x: 100, y: 100 } }], edges: [{ id: 'edge-1', style: { lineWidth: 2 } }], }); // 更新节点位置 graph.updateNodeData([{ id: 'node-1', style: { x: 100, y: 100 } }]); // 更新边样式 graph.updateEdgeData([{ id: 'edge-1', style: { lineWidth: 2 } }]); // 更新组合位置 graph.updateComboData([{ id: 'combo-1', style: { x: 100, y: 100 } }]);3.4 删除数据:级联清理
// 通用删除:按类型组织 ID removeData(ids: DataID | ((data: GraphData) => DataID)): void; // 按类型删除 removeNodeData(ids: ID[] | ((data: NodeData[]) => ID[])): void; removeEdgeData(ids: ID[] | ((data: EdgeData[]) => ID[])): void; removeComboData(ids: ID[] | ((data: ComboData[]) => ID[])): void;| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| ids | 要删除的元素 ID 或返回元素 ID 的函数 | 见各签名 | - | ✓ |
级联删除行为(源码实现见 packages/g6/src/runtime/data.ts):
removeData内部先删边、再删节点、最后删组合,保证删除节点时关联边已先行清理;removeNodeData删除节点时会自动移除与该节点关联的所有边(removeEdgeData(this.getRelatedEdgesData(id).map(idOf))),避免产生悬挂边;- 删除组合时其子节点会被移动到父组合的
children列表中(removeNodeLikeHierarchy逻辑)。
// 通用删除 graph.removeData({ nodes: ['node-1', 'node-2'], edges: ['edge-1'], }); // 删除节点(自动清理关联边) graph.removeNodeData(['node-1', 'node-2']); // 删除边(支持默认 ID,即 'source-target' 形式) graph.removeEdgeData(['edge-1']); // 删除组合 graph.removeComboData(['combo-1']);3.5 存在性判断:hasNode / hasEdge / hasCombo
三个方法用于在操作前判断元素是否存在,避免对不存在的元素执行操作而抛出异常:
hasNode(id: ID): boolean; // 判断节点是否存在 hasEdge(id: ID): boolean; // 判断边是否存在 hasCombo(id: ID): boolean; // 判断组合是否存在| 参数 | 描述 | 类型 | 默认值 | 必选 |
|---|---|---|---|---|
| id | 需要判断的元素 ID | ID | - | ✓ |
从源码看,hasNode内部会排除 combo(model.hasNode(id) && !this.isCombo(id)),hasCombo则要求"既是节点又是 combo"(packages/g6/src/runtime/data.ts)。对应单元测试覆盖了存在/不存在两种场景(packages/g6/tests/unit/runtime/data.spec.ts)。
if (graph.hasNode('node-1')) { console.log('节点存在'); } if (graph.hasEdge('edge-1')) { console.log('边存在'); } if (graph.hasCombo('combo-1')) { console.log('组合存在'); }四、底层实现与实战建议
4.1 数据 API 的调用链
Graph 上所有数据 API 均为薄封装,真正实现集中在DataController(packages/g6/src/runtime/data.ts),其内部基于@antv/graphlib的图模型维护节点、边与两套层级结构:
graph.getData() / setData() / addNodeData() ... └─> Graph 实例方法(packages/g6/src/runtime/graph.ts) └─> DataController(packages/g6/src/runtime/data.ts) └─> GraphLib 图模型 + 变更记录(changes)所有变更都会被记录到changes数组中(pushChange),供元素控制器驱动后续绘制更新;batch()将多次变更合并为一次批处理,silence()则用于运行时调整元素而不留下变更记录(如拖拽节点时同步位置,见 packages/g6/src/runtime/data.ts)。
4.2 自动 zIndex 维护
新增、更新数据后,DataController会自动计算元素的zIndex:组合与节点按所属层级逐层递增(子元素 zIndex = 父元素 zIndex + 1),边取两端节点 zIndex 的最大值再减一(packages/g6/src/runtime/data.ts)。因此在业务代码中通常无需手动维护层级顺序。
4.3 函数式更新的适用场景
setData、addData、updateData及按类型的增删改方法均支持函数式参数((prev) => newData),这在需要基于当前数据做条件判断时非常实用,例如:
// 仅当节点不存在时才添加 graph.addNodeData((prev) => prev.some((n) => n.id === 'node1') ? prev : [...prev, { id: 'node1' }], );4.4 综合示例:完整的 CRUD 流程
import { Graph } from '@antv/g6'; const graph = new Graph({ container: 'container', data: { nodes: [{ id: 'node1' }, { id: 'node2' }], edges: [{ source: 'node1', target: 'node2' }], }, }); await graph.render(); // 查询 const all = graph.getData(); // 全量数据 const node1 = graph.getNodeData('node1'); // 单个节点 const neighbors = graph.getNeighborNodesData('node1'); // 邻居节点 // 新增 graph.addNodeData([{ id: 'node3', data: { label: '新节点' } }]); graph.addEdgeData([{ source: 'node1', target: 'node3' }]); // 更新 graph.updateNodeData([{ id: 'node1', style: { x: 300, y: 300 } }]); // 删除(自动清理 node3 的关联边) graph.removeNodeData(['node3']); // 树图子节点操作 graph.addChildrenData('node1', [{ id: 'node4' }]); // 自动建边 node1 -> node4 const descendants = graph.getDescendantsData('node1'); await graph.draw(); // 数据变更后重新绘制五、小结
G6 的数据 API 覆盖了图数据操作的全生命周期:getData/getNodeData/getEdgeData/getComboData/getElementData负责按范围与类型查询,getElementDataByState支持按状态筛选,getNeighborNodesData/getRelatedEdgesData处理关系查询,getParentData/getChildrenData/getAncestorsData/getDescendantsData完成树与组合的层级遍历;写入侧则通过setData(差异更新)、add*、update*(局部浅合并)、remove*(级联清理)与hasNode/hasEdge/hasCombo构成完备的增删改查体系。结合 DataController 源码 与 单元测试 可以更深入地理解其批处理、差异计算与层级维护机制,从而在复杂图应用中编写高效、可靠的数据操作代码。
【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址: https://gitcode.com/gh_mirrors/g6/G6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考