更健壮的格式判断,修复了ui-transform相关属性的赋值

This commit is contained in:
root
2025-07-23 22:43:47 +08:00
parent 7c5cd5c603
commit f842cf8704
6 changed files with 331 additions and 69 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -389,7 +389,19 @@ export class ComponentTools implements ToolExecutor {
} }
// Step 3: 自动检测和转换属性值 // Step 3: 自动检测和转换属性值
const propertyInfo = this.analyzeProperty(targetComponent, property); let propertyInfo;
try {
console.log(`[ComponentTools] Analyzing property: ${property}`);
propertyInfo = this.analyzeProperty(targetComponent, property);
} catch (analyzeError: any) {
console.error(`[ComponentTools] Error in analyzeProperty:`, analyzeError);
resolve({
success: false,
error: `Failed to analyze property '${property}': ${analyzeError.message}`
});
return;
}
if (!propertyInfo.exists) { if (!propertyInfo.exists) {
resolve({ resolve({
success: false, success: false,
@@ -486,6 +498,42 @@ export class ComponentTools implements ToolExecutor {
}); });
} }
} }
} else if (componentType === 'cc.UITransform' && (property === '_contentSize' || property === 'contentSize')) {
// Special handling for UITransform contentSize - set width and height separately
const width = Number(value.width) || 100;
const height = Number(value.height) || 100;
// Set width first
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: `__comps__.${rawComponentIndex}.width`,
dump: { value: width }
});
// Then set height
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: `__comps__.${rawComponentIndex}.height`,
dump: { value: height }
});
} else if (componentType === 'cc.UITransform' && (property === '_anchorPoint' || property === 'anchorPoint')) {
// Special handling for UITransform anchorPoint - set anchorX and anchorY separately
const anchorX = Number(value.x) || 0.5;
const anchorY = Number(value.y) || 0.5;
// Set anchorX first
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: `__comps__.${rawComponentIndex}.anchorX`,
dump: { value: anchorX }
});
// Then set anchorY
await Editor.Message.request('scene', 'set-property', {
uuid: nodeUuid,
path: `__comps__.${rawComponentIndex}.anchorY`,
dump: { value: anchorY }
});
} 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', {
@@ -600,6 +648,51 @@ export class ComponentTools implements ToolExecutor {
}; };
} }
private isValidPropertyDescriptor(propData: any): boolean {
// 检查是否是有效的属性描述对象
if (typeof propData !== 'object' || propData === null) {
return false;
}
try {
const keys = Object.keys(propData);
// 避免遍历简单的数值对象(如 {width: 200, height: 150}
const isSimpleValueObject = keys.every(key => {
const value = propData[key];
return typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean';
});
if (isSimpleValueObject) {
return false;
}
// 检查是否包含属性描述符的特征字段,不使用'in'操作符
const hasName = keys.includes('name');
const hasValue = keys.includes('value');
const hasType = keys.includes('type');
const hasDisplayName = keys.includes('displayName');
const hasReadonly = keys.includes('readonly');
// 必须包含name或value字段且通常还有type字段
const hasValidStructure = (hasName || hasValue) && (hasType || hasDisplayName || hasReadonly);
// 额外检查如果有default字段且结构复杂避免深度遍历
if (keys.includes('default') && propData.default && typeof propData.default === 'object') {
const defaultKeys = Object.keys(propData.default);
if (defaultKeys.includes('value') && typeof propData.default.value === 'object') {
// 这种情况下我们只返回顶层属性不深入遍历default.value
return hasValidStructure;
}
}
return hasValidStructure;
} catch (error) {
console.warn(`[isValidPropertyDescriptor] Error checking property descriptor:`, error);
return false;
}
}
private analyzeProperty(component: any, propertyName: string): { exists: boolean; type: string; availableProperties: string[]; originalValue: any } { private analyzeProperty(component: any, propertyName: string): { exists: boolean; type: string; availableProperties: string[]; originalValue: any } {
// 从复杂的组件结构中提取可用属性 // 从复杂的组件结构中提取可用属性
const availableProperties: string[] = []; const availableProperties: string[] = [];
@@ -608,7 +701,7 @@ export class ComponentTools implements ToolExecutor {
// 尝试多种方式查找属性: // 尝试多种方式查找属性:
// 1. 直接属性访问 // 1. 直接属性访问
if (propertyName in component) { if (Object.prototype.hasOwnProperty.call(component, propertyName)) {
propertyValue = component[propertyName]; propertyValue = component[propertyName];
propertyExists = true; propertyExists = true;
} }
@@ -619,11 +712,20 @@ export class ComponentTools implements ToolExecutor {
if (component.properties.value && typeof component.properties.value === 'object') { if (component.properties.value && typeof component.properties.value === 'object') {
const valueObj = component.properties.value; const valueObj = component.properties.value;
for (const [key, propData] of Object.entries(valueObj)) { for (const [key, propData] of Object.entries(valueObj)) {
if (typeof propData === 'object' && propData && 'value' in propData) { // 检查propData是否是一个有效的属性描述对象
// 确保propData是对象且包含预期的属性结构
if (this.isValidPropertyDescriptor(propData)) {
const propInfo = propData as any; const propInfo = propData as any;
availableProperties.push(key); availableProperties.push(key);
if (key === propertyName) { if (key === propertyName) {
propertyValue = propInfo.value; // 优先使用value属性如果没有则使用propData本身
try {
const propKeys = Object.keys(propInfo);
propertyValue = propKeys.includes('value') ? propInfo.value : propInfo;
} catch (error) {
// 如果检查失败直接使用propInfo
propertyValue = propInfo;
}
propertyExists = true; propertyExists = true;
} }
} }
@@ -631,11 +733,18 @@ export class ComponentTools implements ToolExecutor {
} else { } else {
// 备用方案直接从properties查找 // 备用方案直接从properties查找
for (const [key, propData] of Object.entries(component.properties)) { for (const [key, propData] of Object.entries(component.properties)) {
if (typeof propData === 'object' && propData && 'value' in propData) { if (this.isValidPropertyDescriptor(propData)) {
const propInfo = propData as any; const propInfo = propData as any;
availableProperties.push(key); availableProperties.push(key);
if (key === propertyName) { if (key === propertyName) {
propertyValue = propInfo.value; // 优先使用value属性如果没有则使用propData本身
try {
const propKeys = Object.keys(propInfo);
propertyValue = propKeys.includes('value') ? propInfo.value : propInfo;
} catch (error) {
// 如果检查失败直接使用propInfo
propertyValue = propInfo;
}
propertyExists = true; propertyExists = true;
} }
} }
@@ -676,15 +785,21 @@ export class ComponentTools implements ToolExecutor {
} else if (typeof propertyValue === 'boolean') { } else if (typeof propertyValue === 'boolean') {
type = 'boolean'; type = 'boolean';
} else if (propertyValue && typeof propertyValue === 'object') { } else if (propertyValue && typeof propertyValue === 'object') {
if ('r' in propertyValue && 'g' in propertyValue && 'b' in propertyValue) { try {
type = 'color'; const keys = Object.keys(propertyValue);
} else if ('x' in propertyValue && 'y' in propertyValue) { if (keys.includes('r') && keys.includes('g') && keys.includes('b')) {
type = propertyValue.z !== undefined ? 'vec3' : 'vec2'; type = 'color';
} else if ('width' in propertyValue && 'height' in propertyValue) { } else if (keys.includes('x') && keys.includes('y')) {
type = 'size'; type = propertyValue.z !== undefined ? 'vec3' : 'vec2';
} else if ('uuid' in propertyValue || '__uuid__' in propertyValue) { } else if (keys.includes('width') && keys.includes('height')) {
type = 'asset'; type = 'size';
} else { } else if (keys.includes('uuid') || keys.includes('__uuid__')) {
type = 'asset';
} else {
type = 'object';
}
} catch (error) {
console.warn(`[analyzeProperty] Error checking property type for: ${JSON.stringify(propertyValue)}`);
type = 'object'; type = 'object';
} }
} else if (propertyValue === null || propertyValue === undefined) { } else if (propertyValue === null || propertyValue === undefined) {
@@ -725,26 +840,44 @@ export class ComponentTools implements ToolExecutor {
case 'color': case 'color':
if (typeof inputValue === 'object' && inputValue !== null) { if (typeof inputValue === 'object' && inputValue !== null) {
// 如果输入是颜色对象,直接使用 // 先检查对象的值是否都是数字或字符串,避免对复杂对象使用'in'操作符
if ('r' in inputValue || 'g' in inputValue || 'b' in inputValue) { try {
return { // 如果输入是颜色对象,直接使用
r: Number(inputValue.r) || 0, const inputKeys = Object.keys(inputValue);
g: Number(inputValue.g) || 0, if (inputKeys.includes('r') || inputKeys.includes('g') || inputKeys.includes('b')) {
b: Number(inputValue.b) || 0, return {
a: Number(inputValue.a) !== undefined ? Number(inputValue.a) : 255 r: Number(inputValue.r) || 0,
}; g: Number(inputValue.g) || 0,
b: Number(inputValue.b) || 0,
a: Number(inputValue.a) !== undefined ? Number(inputValue.a) : 255
};
}
} catch (error) {
// 如果使用'in'操作符出错,说明不是有效的颜色对象
console.warn(`[smartConvertValue] Invalid color input: ${JSON.stringify(inputValue)}`);
} }
} else if (typeof inputValue === 'string') { } else if (typeof inputValue === 'string') {
// 如果是字符串,尝试解析为十六进制颜色 // 如果是字符串,尝试解析为十六进制颜色
return this.parseColorString(inputValue); return this.parseColorString(inputValue);
} }
// 保持原值结构,只更新提供的值 // 保持原值结构,只更新提供的值
return { try {
r: Number(inputValue.r) || originalValue.r || 255, const inputKeys = typeof inputValue === 'object' && inputValue ? Object.keys(inputValue) : [];
g: Number(inputValue.g) || originalValue.g || 255, return {
b: Number(inputValue.b) || originalValue.b || 255, r: inputKeys.includes('r') ? Number(inputValue.r) : (originalValue.r || 255),
a: Number(inputValue.a) !== undefined ? Number(inputValue.a) : (originalValue.a || 255) g: inputKeys.includes('g') ? Number(inputValue.g) : (originalValue.g || 255),
}; b: inputKeys.includes('b') ? Number(inputValue.b) : (originalValue.b || 255),
a: inputKeys.includes('a') ? Number(inputValue.a) : (originalValue.a || 255)
};
} catch (error) {
// 如果有任何错误,返回原值或默认值
return {
r: originalValue.r || 255,
g: originalValue.g || 255,
b: originalValue.b || 255,
a: originalValue.a || 255
};
}
case 'vec2': case 'vec2':
if (typeof inputValue === 'object' && inputValue !== null) { if (typeof inputValue === 'object' && inputValue !== null) {

View File

@@ -259,8 +259,8 @@ export class NodeTools implements ToolExecutor {
if (!targetParentUuid) { if (!targetParentUuid) {
try { try {
const sceneInfo = await Editor.Message.request('scene', 'query-node-tree'); const sceneInfo = await Editor.Message.request('scene', 'query-node-tree');
if (sceneInfo && typeof sceneInfo === 'object' && 'uuid' in sceneInfo) { if (sceneInfo && typeof sceneInfo === 'object' && !Array.isArray(sceneInfo) && Object.prototype.hasOwnProperty.call(sceneInfo, 'uuid')) {
targetParentUuid = sceneInfo.uuid; targetParentUuid = (sceneInfo as any).uuid;
console.log(`No parent specified, using scene root: ${targetParentUuid}`); console.log(`No parent specified, using scene root: ${targetParentUuid}`);
} else if (Array.isArray(sceneInfo) && sceneInfo.length > 0 && sceneInfo[0].uuid) { } else if (Array.isArray(sceneInfo) && sceneInfo.length > 0 && sceneInfo[0].uuid) {
// 如果返回的是数组,使用第一个元素(通常是场景根节点) // 如果返回的是数组,使用第一个元素(通常是场景根节点)

View File

@@ -221,7 +221,7 @@ export class ValidationTools implements ToolExecutor {
// Required fields checking // Required fields checking
if (schema.required && Array.isArray(schema.required)) { if (schema.required && Array.isArray(schema.required)) {
for (const field of schema.required) { for (const field of schema.required) {
if (!(field in data)) { if (!Object.prototype.hasOwnProperty.call(data, field)) {
errors.push(`Missing required field: ${field}`); errors.push(`Missing required field: ${field}`);
suggestions.push(`Add required field "${field}"`); suggestions.push(`Add required field "${field}"`);
} }