优化了token的使用,将常用操作的token使用量降低了90%,完善了属性中的节点引用。重构了组件属性赋值这块的接口处理。但是目前仍旧还没有解决组件类型引用的类型显示问题,这个和cocos creator本身的引擎有关。这个还待解决

This commit is contained in:
root
2025-07-24 17:28:26 +08:00
parent f842cf8704
commit f395b9f1bc
2 changed files with 1124 additions and 52 deletions

File diff suppressed because one or more lines are too long

View File

@@ -73,7 +73,11 @@ export class ComponentTools implements ToolExecutor {
}, },
{ {
name: 'set_component_property', name: 'set_component_property',
description: 'Set component property value - AI只需提供4个简单参数节点UUID、组件名称、属性名称、属性值', description: 'Set component property value - AI必须提供5个参数节点UUID、组件名称、属性名称、属性类型、属性值\n' +
'重要:对于引用类型属性:\n' +
'• 节点引用(propertyType: "node"): value传入目标节点的UUID字符串\n' +
'• 组件引用(propertyType: "component"): value传入目标组件的UUID字符串\n' +
'• 资源引用(propertyType: "spriteFrame/prefab/asset"): value传入资源UUID字符串',
inputSchema: { inputSchema: {
type: 'object', type: 'object',
properties: { properties: {
@@ -84,17 +88,44 @@ export class ComponentTools implements ToolExecutor {
componentType: { componentType: {
type: 'string', type: 'string',
description: 'Component type - 组件类型', description: 'Component type - 组件类型',
enum: ['cc.Label', 'cc.Sprite', 'cc.Button', 'cc.Toggle', 'cc.Slider', 'cc.ScrollView', 'cc.EditBox', 'cc.ProgressBar', 'cc.RichText', 'cc.Mask', 'cc.Graphics', 'cc.Layout', 'cc.Widget', 'cc.UITransform'] enum: ['cc.Label', 'cc.Sprite', 'cc.Button', 'cc.Toggle', 'cc.Slider', 'cc.ScrollView', 'cc.EditBox', 'cc.ProgressBar', 'cc.RichText', 'cc.Mask', 'cc.Graphics', 'cc.Layout', 'cc.Widget', 'cc.UITransform', 'TestMCPScript']
}, },
property: { property: {
type: 'string', type: 'string',
description: 'Property name - 属性名称,常见值: string(文本), color(颜色), fontSize(字体大小), spriteFrame(精灵帧), enabled(启用状态), position(位置), scale(缩放), rotation(旋转)' description: 'Property name - 属性名称'
},
propertyType: {
type: 'string',
description: 'Property type - 属性类型(必须明确指定)',
enum: [
'string', 'number', 'boolean', 'integer', 'float',
'color', 'vec2', 'vec3', 'size',
'node', 'component', 'spriteFrame', 'prefab', 'asset',
'nodeArray', 'colorArray', 'numberArray', 'stringArray'
]
}, },
value: { value: {
description: 'Property value - 属性值,支持的类型:\n• 字符串: "Hello World"\n• 数字: 32, 1.5\n• 布尔值: true, false\n• 颜色对象: {"r":255,"g":0,"b":0,"a":255} 或 "#FF0000"\n• 向量对象: {"x":100,"y":50} 或 {"x":1,"y":2,"z":3}\n• 尺寸对象: {"width":100,"height":50}\n• 资源UUID: "asset-uuid-string"' description: 'Property value - 属性值,根据propertyType使用不同格式:\n' +
'基础类型:\n' +
'• string: "Hello World"\n' +
'• number/integer/float: 42 或 3.14\n' +
'• boolean: true 或 false\n' +
'• color: {"r":255,"g":0,"b":0,"a":255}\n' +
'• vec2: {"x":100,"y":50}\n' +
'• vec3: {"x":1,"y":2,"z":3}\n' +
'• size: {"width":100,"height":50}\n' +
'引用类型(重要):\n' +
'• node: "target-node-uuid-string" 目标节点的UUID字符串\n' +
'• component: "target-component-uuid-string" 目标组件的UUID字符串\n' +
'• spriteFrame/prefab/asset: "asset-uuid-string" 资源UUID字符串\n' +
'数组类型:\n' +
'• nodeArray: ["uuid1","uuid2","uuid3"]\n' +
'• colorArray: [{"r":255,"g":0,"b":0,"a":255},{"r":0,"g":255,"b":0,"a":255}]\n' +
'• numberArray: [1,2,3,4,5]\n' +
'• stringArray: ["item1","item2","item3"]'
} }
}, },
required: ['nodeUuid', 'componentType', 'property', 'value'] required: ['nodeUuid', 'componentType', 'property', 'propertyType', 'value']
} }
}, },
{ {
@@ -335,24 +366,35 @@ export class ComponentTools implements ToolExecutor {
} }
private extractComponentProperties(component: any): Record<string, any> { private extractComponentProperties(component: any): Record<string, any> {
console.log(`[extractComponentProperties] Processing component:`, Object.keys(component));
// 检查组件是否有 value 属性,这通常包含实际的组件属性
if (component.value && typeof component.value === 'object') {
console.log(`[extractComponentProperties] Found component.value with properties:`, Object.keys(component.value));
return component.value; // 直接返回 value 对象,它包含所有组件属性
}
// 备用方案:从组件对象中直接提取属性
const properties: Record<string, any> = {}; const properties: Record<string, any> = {};
const excludeKeys = ['__type__', 'enabled', 'node', '_id']; const excludeKeys = ['__type__', 'enabled', 'node', '_id', '__scriptAsset', 'uuid', 'name', '_name', '_objFlags', '_enabled', 'type', 'readonly', 'visible', 'cid', 'editor', 'extends'];
for (const key in component) { for (const key in component) {
if (!excludeKeys.includes(key) && !key.startsWith('_')) { if (!excludeKeys.includes(key) && !key.startsWith('_')) {
console.log(`[extractComponentProperties] Found direct property '${key}':`, typeof component[key]);
properties[key] = component[key]; properties[key] = component[key];
} }
} }
console.log(`[extractComponentProperties] Final extracted properties:`, Object.keys(properties));
return properties; return properties;
} }
private async setComponentProperty(args: any): Promise<ToolResponse> { private async setComponentProperty(args: any): Promise<ToolResponse> {
const { nodeUuid, componentType, property, value } = args; const { nodeUuid, componentType, property, propertyType, value } = args;
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
try { try {
console.log(`[ComponentTools] Setting ${componentType}.${property} = ${JSON.stringify(value)} on node ${nodeUuid}`); console.log(`[ComponentTools] Setting ${componentType}.${property} (type: ${propertyType}) = ${JSON.stringify(value)} on node ${nodeUuid}`);
// Step 1: 获取组件信息使用与getComponents相同的方法 // Step 1: 获取组件信息使用与getComponents相同的方法
const componentsResponse = await this.getComponents(nodeUuid); const componentsResponse = await this.getComponents(nodeUuid);
@@ -410,13 +452,198 @@ export class ComponentTools implements ToolExecutor {
return; return;
} }
const processedValue = this.smartConvertValue(value, propertyInfo); // Step 4: 处理属性值和设置
const originalValue = propertyInfo.originalValue; const originalValue = propertyInfo.originalValue;
let processedValue: any;
console.log(`[ComponentTools] Converting value: ${JSON.stringify(value)} -> ${JSON.stringify(processedValue)} (type: ${propertyInfo.type})`); // 根据明确的propertyType处理属性值
switch (propertyType) {
case 'string':
processedValue = String(value);
break;
case 'number':
case 'integer':
case 'float':
processedValue = Number(value);
break;
case 'boolean':
processedValue = Boolean(value);
break;
case 'color':
if (typeof value === 'object' && value !== null) {
processedValue = {
r: Math.min(255, Math.max(0, Number(value.r) || 0)),
g: Math.min(255, Math.max(0, Number(value.g) || 0)),
b: Math.min(255, Math.max(0, Number(value.b) || 0)),
a: value.a !== undefined ? Math.min(255, Math.max(0, Number(value.a))) : 255
};
} else {
throw new Error('Color value must be an object with r, g, b properties');
}
break;
case 'vec2':
if (typeof value === 'object' && value !== null) {
processedValue = {
x: Number(value.x) || 0,
y: Number(value.y) || 0
};
} else {
throw new Error('Vec2 value must be an object with x, y properties');
}
break;
case 'vec3':
if (typeof value === 'object' && value !== null) {
processedValue = {
x: Number(value.x) || 0,
y: Number(value.y) || 0,
z: Number(value.z) || 0
};
} else {
throw new Error('Vec3 value must be an object with x, y, z properties');
}
break;
case 'size':
if (typeof value === 'object' && value !== null) {
processedValue = {
width: Number(value.width) || 0,
height: Number(value.height) || 0
};
} else {
throw new Error('Size value must be an object with width, height properties');
}
break;
case 'node':
if (typeof value === 'string') {
processedValue = { uuid: value };
} else {
throw new Error('Node reference value must be a string UUID');
}
break;
case 'component':
if (typeof value === 'string') {
// 组件引用:需要验证节点上是否有对应组件类型
// 推断组件类型从属性名称例如labelComponent -> Label, buttonComponent -> Button
let expectedComponentType = '';
const propertyLower = property.toLowerCase();
if (propertyLower.includes('label')) {
expectedComponentType = 'cc.Label';
} else if (propertyLower.includes('button')) {
expectedComponentType = 'cc.Button';
} else if (propertyLower.includes('sprite')) {
expectedComponentType = 'cc.Sprite';
} else if (propertyLower.includes('canvas')) {
expectedComponentType = 'cc.Canvas';
} else if (propertyLower.includes('camera')) {
expectedComponentType = 'cc.Camera';
}
// 验证目标节点是否存在指定组件
try {
const nodeData = await Editor.Message.request('scene', 'query-node', value);
if (nodeData && nodeData.__comps__ && expectedComponentType) {
const targetComponent = nodeData.__comps__.find((comp: any) =>
comp.__type__ === expectedComponentType
);
if (targetComponent) {
// 找到匹配组件使用组件的UUID
processedValue = { uuid: (targetComponent as any).uuid || value };
} else {
console.warn(`[setComponentProperty] Node ${value} does not have component ${expectedComponentType}`);
processedValue = { uuid: value }; // 仍然设置让Editor处理
}
} else {
// 无法推断组件类型或节点不存在直接使用UUID
processedValue = { uuid: value };
}
} catch (error) {
console.warn(`[setComponentProperty] Failed to verify component on node ${value}: ${error}`);
processedValue = { uuid: value }; // 仍然设置让Editor处理
}
} else if (typeof value === 'object' && value !== null && value.node && value.component) {
// 查找具体的组件UUID
try {
const nodeData = await Editor.Message.request('scene', 'query-node', value.node);
if (nodeData && nodeData.__comps__) {
const targetComponent = nodeData.__comps__.find((comp: any) =>
comp.__type__ === value.component
);
if (targetComponent) {
processedValue = { uuid: (targetComponent as any).uuid || "" };
} else {
processedValue = { uuid: "" };
}
} else {
processedValue = { uuid: "" };
}
} catch (error) {
console.warn(`[setComponentProperty] Failed to find component: ${error}`);
processedValue = { uuid: "" };
}
} else {
throw new Error('Component reference value must be a string UUID or object with node and component properties');
}
break;
case 'spriteFrame':
case 'prefab':
case 'asset':
if (typeof value === 'string') {
processedValue = { uuid: value };
} else {
throw new Error(`${propertyType} value must be a string UUID`);
}
break;
case 'nodeArray':
if (Array.isArray(value)) {
processedValue = value.map((item: any) => {
if (typeof item === 'string') {
return { uuid: item };
} else {
throw new Error('NodeArray items must be string UUIDs');
}
});
} else {
throw new Error('NodeArray value must be an array');
}
break;
case 'colorArray':
if (Array.isArray(value)) {
processedValue = value.map((item: any) => {
if (typeof item === 'object' && item !== null && 'r' in item) {
return {
r: Math.min(255, Math.max(0, Number(item.r) || 0)),
g: Math.min(255, Math.max(0, Number(item.g) || 0)),
b: Math.min(255, Math.max(0, Number(item.b) || 0)),
a: item.a !== undefined ? Math.min(255, Math.max(0, Number(item.a))) : 255
};
} else {
throw new Error('ColorArray items must be objects with r, g, b properties');
}
});
} else {
throw new Error('ColorArray value must be an array');
}
break;
case 'numberArray':
if (Array.isArray(value)) {
processedValue = value.map((item: any) => Number(item));
} else {
throw new Error('NumberArray value must be an array');
}
break;
case 'stringArray':
if (Array.isArray(value)) {
processedValue = value.map((item: any) => String(item));
} else {
throw new Error('StringArray value must be an array');
}
break;
default:
throw new Error(`Unsupported property type: ${propertyType}`);
}
// Step 4: 设置属性值 console.log(`[ComponentTools] Converting value: ${JSON.stringify(value)} -> ${JSON.stringify(processedValue)} (type: ${propertyType})`);
// 需要重新获取原始节点数据来构建正确的路径
// Step 5: 获取原始节点数据来构建正确的路径
const rawNodeData = await Editor.Message.request('scene', 'query-node', nodeUuid); const rawNodeData = await Editor.Message.request('scene', 'query-node', nodeUuid);
if (!rawNodeData || !rawNodeData.__comps__) { if (!rawNodeData || !rawNodeData.__comps__) {
resolve({ resolve({
@@ -534,6 +761,189 @@ export class ComponentTools implements ToolExecutor {
path: `__comps__.${rawComponentIndex}.anchorY`, path: `__comps__.${rawComponentIndex}.anchorY`,
dump: { value: anchorY } dump: { value: anchorY }
}); });
} else if (propertyInfo.type === 'color' && processedValue && typeof processedValue === 'object') {
// 特殊处理颜色属性确保RGBA值正确
// Cocos Creator颜色值范围是0-255
const colorValue = {
r: Math.min(255, Math.max(0, Number(processedValue.r) || 0)),
g: Math.min(255, Math.max(0, Number(processedValue.g) || 0)),
b: Math.min(255, Math.max(0, Number(processedValue.b) || 0)),
a: processedValue.a !== undefined ? Math.min(255, Math.max(0, Number(processedValue.a))) : 255
};
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: colorValue,
type: 'cc.Color'
}
});
} else if (propertyInfo.type === 'vec3' && processedValue && typeof processedValue === 'object') {
// 特殊处理Vec3属性
const vec3Value = {
x: Number(processedValue.x) || 0,
y: Number(processedValue.y) || 0,
z: Number(processedValue.z) || 0
};
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: vec3Value,
type: 'cc.Vec3'
}
});
} else if (propertyInfo.type === 'vec2' && processedValue && typeof processedValue === 'object') {
// 特殊处理Vec2属性
const vec2Value = {
x: Number(processedValue.x) || 0,
y: Number(processedValue.y) || 0
};
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: vec2Value,
type: 'cc.Vec2'
}
});
} else if (propertyInfo.type === 'size' && processedValue && typeof processedValue === 'object') {
// 特殊处理Size属性
const sizeValue = {
width: Number(processedValue.width) || 0,
height: Number(processedValue.height) || 0
};
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: sizeValue,
type: 'cc.Size'
}
});
} else if ((propertyInfo.type === 'node' || propertyInfo.type === 'cc.Node') && processedValue && typeof processedValue === 'object' && 'uuid' in processedValue) {
// 特殊处理节点引用
console.log(`[ComponentTools] Setting node reference with UUID: ${processedValue.uuid}`);
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: processedValue,
type: 'cc.Node'
}
});
} else if (propertyInfo.type === 'component') {
// 特殊处理组件引用 - 方案B使用完整的属性描述符
let componentValue;
let originalPropertyDescriptor: any = {};
// 关键优化:获取完整的原始属性描述符
try {
const tempResult = await this.getComponentInfo(nodeUuid, componentType);
if (tempResult.success && tempResult.data?.properties?.[property]) {
const propertyMeta = tempResult.data.properties[property];
if (propertyMeta && typeof propertyMeta === 'object') {
// 保存完整的属性描述符除了value之外的所有信息
originalPropertyDescriptor = { ...propertyMeta };
delete originalPropertyDescriptor.value; // 移除旧的value我们会设置新的
console.log(`[setComponentProperty] Preserving original property descriptor for ${property}:`, Object.keys(originalPropertyDescriptor));
}
}
} catch (error) {
console.warn(`[setComponentProperty] Failed to get original property descriptor: ${error}`);
}
if (typeof processedValue === 'string') {
// 如果是字符串假设是组件的UUID
componentValue = { uuid: processedValue };
} else if (processedValue && typeof processedValue === 'object') {
if (processedValue.node && processedValue.component) {
// 如果提供了节点和组件类型需要查找具体的组件UUID
try {
const nodeData = await Editor.Message.request('scene', 'query-node', processedValue.node);
if (nodeData && nodeData.__comps__) {
const targetComponent = nodeData.__comps__.find((comp: any) =>
comp.__type__ === processedValue.component
);
if (targetComponent) {
componentValue = { uuid: (targetComponent as any).uuid || "" };
} else {
componentValue = { uuid: "" };
}
} else {
componentValue = { uuid: "" };
}
} catch (error) {
console.warn(`[setComponentProperty] Failed to find component: ${error}`);
componentValue = { uuid: "" };
}
} else {
componentValue = processedValue;
}
} else {
componentValue = { uuid: "" };
}
// 方案B使用完整的属性描述符保持所有原始元数据
const dumpData = {
value: componentValue,
...originalPropertyDescriptor // 保持完整的属性描述符
};
console.log(`[setComponentProperty] Setting with complete descriptor:`, Object.keys(dumpData));
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: dumpData
});
} else if (propertyInfo.type === 'nodeArray' && Array.isArray(processedValue)) {
// 特殊处理节点数组
const nodeArrayValue = processedValue.map((item: any) => {
if (typeof item === 'string') {
return { __uuid__: item };
} else if (item && typeof item === 'object' && (item.uuid || item.__uuid__)) {
return { __uuid__: item.uuid || item.__uuid__ };
} else {
return { __uuid__: "" };
}
});
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: nodeArrayValue,
type: 'cc.Node'
}
});
} else if (propertyInfo.type === 'colorArray' && Array.isArray(processedValue)) {
// 特殊处理颜色数组
const colorArrayValue = processedValue.map((item: any) => {
if (item && typeof item === 'object' && 'r' in item) {
return {
r: Math.min(255, Math.max(0, Number(item.r) || 0)),
g: Math.min(255, Math.max(0, Number(item.g) || 0)),
b: Math.min(255, Math.max(0, Number(item.b) || 0)),
a: item.a !== undefined ? Math.min(255, Math.max(0, Number(item.a))) : 255
};
} else {
return { r: 255, g: 255, b: 255, a: 255 };
}
});
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: propertyPath,
dump: {
value: colorArrayValue,
type: 'cc.Color'
}
});
} else { } else {
// Normal property setting for non-asset properties // Normal property setting for non-asset properties
await Editor.Message.request('scene', 'set-property', { await Editor.Message.request('scene', 'set-property', {
@@ -543,7 +953,8 @@ export class ComponentTools implements ToolExecutor {
}); });
} }
// Step 5: 验证设置结果 // Step 5: 等待Editor完成更新然后验证设置结果
await new Promise(resolve => setTimeout(resolve, 200)); // 等待200ms让Editor完成更新
const verification = await this.verifyPropertyChange(nodeUuid, componentType, property, originalValue, processedValue); const verification = await this.verifyPropertyChange(nodeUuid, componentType, property, originalValue, processedValue);
resolve({ resolve({
@@ -773,7 +1184,16 @@ export class ComponentTools implements ToolExecutor {
let type = 'unknown'; let type = 'unknown';
// 智能类型检测 // 智能类型检测
if (typeof propertyValue === 'string') { if (Array.isArray(propertyValue)) {
// 数组类型检测
if (propertyName.toLowerCase().includes('node')) {
type = 'nodeArray';
} else if (propertyName.toLowerCase().includes('color')) {
type = 'colorArray';
} else {
type = 'array';
}
} else if (typeof propertyValue === 'string') {
// Check if property name suggests it's an asset // Check if property name suggests it's an asset
if (['spriteFrame', 'texture', 'material', 'font', 'clip', 'prefab'].includes(propertyName.toLowerCase())) { if (['spriteFrame', 'texture', 'material', 'font', 'clip', 'prefab'].includes(propertyName.toLowerCase())) {
type = 'asset'; type = 'asset';
@@ -794,7 +1214,17 @@ export class ComponentTools implements ToolExecutor {
} else if (keys.includes('width') && keys.includes('height')) { } else if (keys.includes('width') && keys.includes('height')) {
type = 'size'; type = 'size';
} else if (keys.includes('uuid') || keys.includes('__uuid__')) { } else if (keys.includes('uuid') || keys.includes('__uuid__')) {
type = 'asset'; // 检查是否是节点引用通过属性名或__id__属性判断
if (propertyName.toLowerCase().includes('node') ||
propertyName.toLowerCase().includes('target') ||
keys.includes('__id__')) {
type = 'node';
} else {
type = 'asset';
}
} else if (keys.includes('__id__')) {
// 节点引用特征
type = 'node';
} else { } else {
type = 'object'; type = 'object';
} }
@@ -806,6 +1236,11 @@ export class ComponentTools implements ToolExecutor {
// For null/undefined values, check property name to determine type // For null/undefined values, check property name to determine type
if (['spriteFrame', 'texture', 'material', 'font', 'clip', 'prefab'].includes(propertyName.toLowerCase())) { if (['spriteFrame', 'texture', 'material', 'font', 'clip', 'prefab'].includes(propertyName.toLowerCase())) {
type = 'asset'; type = 'asset';
} else if (propertyName.toLowerCase().includes('node') ||
propertyName.toLowerCase().includes('target')) {
type = 'node';
} else if (propertyName.toLowerCase().includes('component')) {
type = 'component';
} else { } else {
type = 'unknown'; type = 'unknown';
} }
@@ -849,7 +1284,7 @@ export class ComponentTools implements ToolExecutor {
r: Number(inputValue.r) || 0, r: Number(inputValue.r) || 0,
g: Number(inputValue.g) || 0, g: Number(inputValue.g) || 0,
b: Number(inputValue.b) || 0, b: Number(inputValue.b) || 0,
a: Number(inputValue.a) !== undefined ? Number(inputValue.a) : 255 a: inputValue.a !== undefined ? Number(inputValue.a) : 255
}; };
} }
} catch (error) { } catch (error) {
@@ -907,6 +1342,16 @@ export class ComponentTools implements ToolExecutor {
} }
return originalValue; return originalValue;
case 'node':
if (typeof inputValue === 'string') {
// 节点引用需要特殊处理
return inputValue;
} else if (typeof inputValue === 'object' && inputValue !== null) {
// 如果已经是对象形式返回UUID或完整对象
return inputValue.uuid || inputValue;
}
return originalValue;
case 'asset': case 'asset':
if (typeof inputValue === 'string') { if (typeof inputValue === 'string') {
// 如果输入是字符串路径转换为asset对象 // 如果输入是字符串路径转换为asset对象
@@ -938,35 +1383,115 @@ export class ComponentTools implements ToolExecutor {
} }
private async verifyPropertyChange(nodeUuid: string, componentType: string, property: string, originalValue: any, expectedValue: any): Promise<{ verified: boolean; actualValue: any; fullData: any }> { private async verifyPropertyChange(nodeUuid: string, componentType: string, property: string, originalValue: any, expectedValue: any): Promise<{ verified: boolean; actualValue: any; fullData: any }> {
console.log(`[verifyPropertyChange] Starting verification for ${componentType}.${property}`);
console.log(`[verifyPropertyChange] Expected value:`, JSON.stringify(expectedValue));
console.log(`[verifyPropertyChange] Original value:`, JSON.stringify(originalValue));
try { try {
// 重新获取组件信息进行验证 // 重新获取组件信息进行验证
console.log(`[verifyPropertyChange] Calling getComponentInfo...`);
const componentInfo = await this.getComponentInfo(nodeUuid, componentType); const componentInfo = await this.getComponentInfo(nodeUuid, componentType);
console.log(`[verifyPropertyChange] getComponentInfo success:`, componentInfo.success);
const allComponents = await this.getComponents(nodeUuid); const allComponents = await this.getComponents(nodeUuid);
console.log(`[verifyPropertyChange] getComponents success:`, allComponents.success);
if (componentInfo.success && componentInfo.data) { if (componentInfo.success && componentInfo.data) {
const actualValue = componentInfo.data.properties?.[property]; console.log(`[verifyPropertyChange] Component data available, extracting property '${property}'`);
const verified = JSON.stringify(actualValue) !== JSON.stringify(originalValue); const allPropertyNames = Object.keys(componentInfo.data.properties || {});
console.log(`[verifyPropertyChange] Available properties:`, allPropertyNames);
const propertyData = componentInfo.data.properties?.[property];
console.log(`[verifyPropertyChange] Raw property data for '${property}':`, JSON.stringify(propertyData));
return { // 从属性数据中提取实际值
let actualValue = propertyData;
console.log(`[verifyPropertyChange] Initial actualValue:`, JSON.stringify(actualValue));
if (propertyData && typeof propertyData === 'object' && 'value' in propertyData) {
actualValue = propertyData.value;
console.log(`[verifyPropertyChange] Extracted actualValue from .value:`, JSON.stringify(actualValue));
} else {
console.log(`[verifyPropertyChange] No .value property found, using raw data`);
}
// 修复验证逻辑:检查实际值是否匹配期望值
let verified = false;
if (typeof expectedValue === 'object' && expectedValue !== null && 'uuid' in expectedValue) {
// 对于引用类型(节点/组件/资源比较UUID
const actualUuid = actualValue && typeof actualValue === 'object' && 'uuid' in actualValue ? actualValue.uuid : '';
const expectedUuid = expectedValue.uuid || '';
verified = actualUuid === expectedUuid && expectedUuid !== '';
console.log(`[verifyPropertyChange] Reference comparison:`);
console.log(` - Expected UUID: "${expectedUuid}"`);
console.log(` - Actual UUID: "${actualUuid}"`);
console.log(` - UUID match: ${actualUuid === expectedUuid}`);
console.log(` - UUID not empty: ${expectedUuid !== ''}`);
console.log(` - Final verified: ${verified}`);
} else {
// 对于其他类型,直接比较值
console.log(`[verifyPropertyChange] Value comparison:`);
console.log(` - Expected type: ${typeof expectedValue}`);
console.log(` - Actual type: ${typeof actualValue}`);
if (typeof actualValue === typeof expectedValue) {
if (typeof actualValue === 'object' && actualValue !== null && expectedValue !== null) {
// 对象类型的深度比较
verified = JSON.stringify(actualValue) === JSON.stringify(expectedValue);
console.log(` - Object comparison (JSON): ${verified}`);
} else {
// 基本类型的直接比较
verified = actualValue === expectedValue;
console.log(` - Direct comparison: ${verified}`);
}
} else {
// 类型不匹配时的特殊处理(如数字和字符串)
const stringMatch = String(actualValue) === String(expectedValue);
const numberMatch = Number(actualValue) === Number(expectedValue);
verified = stringMatch || numberMatch;
console.log(` - String match: ${stringMatch}`);
console.log(` - Number match: ${numberMatch}`);
console.log(` - Type mismatch verified: ${verified}`);
}
}
console.log(`[verifyPropertyChange] Final verification result: ${verified}`);
console.log(`[verifyPropertyChange] Final actualValue:`, JSON.stringify(actualValue));
const result = {
verified, verified,
actualValue, actualValue,
fullData: { fullData: {
updatedComponent: componentInfo.data, // 只返回修改的属性信息,不返回完整组件数据
allNodeComponents: allComponents.data, modifiedProperty: {
changeDetails: { name: property,
property,
before: originalValue, before: originalValue,
expected: expectedValue, expected: expectedValue,
actual: actualValue, actual: actualValue,
verified verified,
propertyMetadata: propertyData // 只包含这个属性的元数据
},
// 简化的组件信息
componentSummary: {
nodeUuid,
componentType,
totalProperties: Object.keys(componentInfo.data?.properties || {}).length
} }
} }
}; };
console.log(`[verifyPropertyChange] Returning result:`, JSON.stringify(result, null, 2));
return result;
} else {
console.log(`[verifyPropertyChange] ComponentInfo failed or no data:`, componentInfo);
} }
} catch (error) { } catch (error) {
console.warn('[verifyPropertyChange] Verification failed:', error); console.error('[verifyPropertyChange] Verification failed with error:', error);
console.error('[verifyPropertyChange] Error stack:', error instanceof Error ? error.stack : 'No stack trace');
} }
console.log(`[verifyPropertyChange] Returning fallback result`);
return { return {
verified: false, verified: false,
actualValue: undefined, actualValue: undefined,