现象:
"spaceType", "spacePositioning")原因:
formatJSONToText()方法在字段为空时跳过该部分rawContent(JSON格式)ion-icon现象:
<ion-icon>标签需求:
<span class="icon-text">✏️</span>ion-icon,提升企业微信端兼容性文件: design-analysis-ai.service.ts
修改内容:
在返回空字符串前调用后备方法
private formatJSONToText(jsonResult: any): string {
console.log('🔄 [formatJSONToText] 开始格式化JSON结果...');
const sections = [];
// ... 原有逻辑
const formattedText = sections.join('\n');
// 🔥 后备机制:如果格式化结果为空,尝试从JSON直接生成文本
if (!formattedText || formattedText.trim().length === 0) {
console.warn('⚠️ [formatJSONToText] 格式化结果为空,使用后备方案...');
return this.fallbackFormatJSON(jsonResult);
}
return formattedText;
}
private fallbackFormatJSON(jsonResult: any): string {
const lines: string[] = [];
// 遍历JSON对象的所有字段
const fieldMap: { [key: string]: string } = {
'spaceType': '空间类型',
'spacePositioning': '一、空间定位与场景属性',
'layout': '二、空间布局与动线',
'hardDecoration': '三、硬装系统细节',
'colorAnalysis': '四、色调精准分析',
'materials': '五、材质应用解析',
'form': '六、形体与比例',
'style': '七、风格与氛围营造',
'suggestions': '八、专业优化建议',
'summary': '设计概要'
};
for (const [key, title] of Object.entries(fieldMap)) {
if (jsonResult[key] && typeof jsonResult[key] === 'string' && jsonResult[key].trim().length > 0) {
if (key === 'spaceType' || key === 'summary') {
lines.push(`${title}:${jsonResult[key]}\n`);
} else {
lines.push(`${title}\n\n${jsonResult[key]}\n`);
}
}
}
const result = lines.join('\n');
console.log('✅ [fallbackFormatJSON] 后备格式化完成,长度:', result.length);
return result || '暂无分析内容';
}
private beautifyJSON(jsonResult: any): string {
const lines: string[] = [];
for (const [key, value] of Object.entries(jsonResult)) {
if (value && typeof value === 'string' && value.trim().length > 0) {
const chineseTitle = this.getChineseTitleForKey(key);
lines.push(`【${chineseTitle}】\n${value}\n`);
}
}
return lines.join('\n') || '分析结果为空,请重新分析';
}
private getChineseTitleForKey(key: string): string {
const titleMap: { [key: string]: string } = {
'spaceType': '空间类型',
'spacePositioning': '空间定位与场景属性',
// ... 其他映射
};
return titleMap[key] || key;
}
private parseJSONAnalysis(jsonResult: any): any {
console.log('📝 [parseJSONAnalysis] 开始解析JSON分析结果...');
// 将JSON字段转换为易读的格式化文本
let formattedContent = this.formatJSONToText(jsonResult);
// 🔥 关键:如果formattedContent为空或过短,说明JSON可能没有标准字段
if (!formattedContent || formattedContent.trim().length < 50) {
console.warn('⚠️ [parseJSONAnalysis] 格式化内容过短,尝试后备方案...');
formattedContent = this.fallbackFormatJSON(jsonResult);
}
// 🔥 最终校验:如果还是为空,使用原始JSON的美化版本
if (!formattedContent || formattedContent.trim().length < 20) {
console.warn('⚠️ [parseJSONAnalysis] 后备方案也失败,使用JSON美化版本...');
formattedContent = this.beautifyJSON(jsonResult);
}
console.log('✅ [parseJSONAnalysis] 最终格式化内容长度:', formattedContent.length);
return {
rawContent: JSON.stringify(jsonResult, null, 2),
formattedContent: formattedContent, // 确保有内容
structuredData: { /* ... */ },
// ...
};
}
文件: stage-requirements.component.html
替换映射表:
| 原ion-icon | 替换emoji | 用途 |
|-----------|----------|------|
| name="send" | ✉️ | 发送按钮 |
| name="analytics" | 📊 | 开始AI分析 |
| name="sparkles" | ✨ | AI助手/欢迎图标 |
| name="color-palette" | 🎨 | 分析设计风格 |
| name="bulb" | 💡 | 灯光设计 |
| name="cube" | 📦 | 材质分析 |
| name="resize" | 🔄 | 空间优化 |
| name="person" | 👤 | 用户头像 |
| name="copy" | 📋 | 复制 |
| name="refresh" | 🔄 | 重新生成 |
| name="thumbs-up" | 👍 | 有帮助 |
| name="thumbs-down" | 👎 | 无帮助 |
| name="trash" | 🗑️ | 清空对话 |
| name="download" | 💾 | 导出对话 |
| name="checkmark-circle" | ✅ | 确认分析结果 |
| name="document-text" | 📄 | 生成客服标注/报告 |
| name="document" | 📄 | 文件图标 |
示例修改:
<!-- ❌ 修改前 -->
<button class="send-btn">
<ion-icon name="send"></ion-icon>
</button>
<!-- ✅ 修改后 -->
<button class="send-btn">
<span class="icon-text">✉️</span>
</button>
文件: stage-requirements.component.scss
修改位置:
.send-btn).input-actions-left .action-btn).message-actions .action-btn).prompt-chip).quick-action-btn).btn-start-analysis).welcome-icon).message-avatar)添加样式:
// 🔥 企业微信端emoji支持
.icon-text {
font-size: 20px;
line-height: 1;
display: inline-block;
}
示例(发送按钮):
.send-btn {
width: 40px;
height: 40px;
border: none;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
ion-icon {
font-size: 20px;
}
// 🔥 企业微信端emoji支持
.icon-text {
font-size: 20px;
line-height: 1;
display: inline-block;
}
}
修改内容:
formatJSONToText()方法(添加日志和后备机制)fallbackFormatJSON()后备格式化方法beautifyJSON()JSON美化方法getChineseTitleForKey()字段名映射方法parseJSONAnalysis()方法(多层校验)替换ion-icon为emoji:
添加.icon-text样式支持:
.send-btn - 发送按钮.input-actions-left .action-btn - 输入区域操作按钮.message-actions .action-btn - 消息操作按钮.prompt-chip - 快捷提示按钮.quick-action-btn - 快捷操作按钮.btn-start-analysis - 开始AI分析按钮.welcome-icon - 欢迎图标.message-avatar - 消息头像修复前:
{
"spaceType": "客餐厅一体化",
"spacePositioning": "该空间定位为高品质住宅的核心公共区域..."
}
修复后:
空间类型:客餐厅一体化
一、空间定位与场景属性
该空间定位为高品质住宅的核心公共区域...
二、空间布局与动线
布局采用开放式设计...
修复前:
<button class="send-btn">
<ion-icon name="send"></ion-icon>
</button>
修复后:
<button class="send-btn">
<span class="icon-text">✉️</span>
</button>
AI返回JSON对象
↓
formatJSONToText()
↓
检查是否为空/过短 ❌
↓
fallbackFormatJSON() (后备方案1)
↓
检查是否为空/过短 ❌
↓
beautifyJSON() (后备方案2)
↓
确保返回有效内容 ✅
↓
HTML显示格式化文本
HTML模板
↓
<span class="icon-text">emoji</span>
↓
CSS样式 (.icon-text)
↓
浏览器原生emoji渲染 ✅
🔄 [formatJSONToText] 开始格式化JSON结果...
🔍 [formatJSONToText] JSON字段数量: 9
✅ [formatJSONToText] 格式化完成,长度: 2341
📝 [formatJSONToText] 内容预览: 一、空间定位与场景属性
该空间定位为高品质住宅...
📝 [parseJSONAnalysis] 开始解析JSON分析结果...
✅ [parseJSONAnalysis] 最终格式化内容长度: 2341
🔄 [formatJSONToText] 开始格式化JSON结果...
🔍 [formatJSONToText] JSON字段数量: 2
✅ [formatJSONToText] 格式化完成,长度: 0
⚠️ [formatJSONToText] 格式化结果为空,使用后备方案...
✅ [fallbackFormatJSON] 后备格式化完成,长度: 156
📝 [parseJSONAnalysis] 开始解析JSON分析结果...
⚠️ [parseJSONAnalysis] 格式化内容过短,尝试后备方案...
✅ [parseJSONAnalysis] 最终格式化内容长度: 156
cd e:\yinsanse\yss-project
npm run build:prod
.\deploy.ps1
/dev/yss/.icon-text使用统一样式ion-icon相同的大小和位置AI分析时,对话框中显示的是原始JSON格式:
{
"spaceType": "客厅",
"spacePositioning": "该空间定位为高品质居住环境...",
"layout": "空间采用L型规划..."
}
而不是格式化的中文易读文本
文件: design-analysis-ai.service.ts (第91-100行)
// ❌ 问题代码:没有检测和解析JSON字符串
if (typeof content === 'string') {
displayText = content; // 直接显示,即使是JSON字符串
}
问题:
typeof content === 'string'后,直接显示内容检测JSON字符串并格式化:在流式输出时,先检测是否为JSON字符串,解析后再格式化。
// ✅ 修复后:检测JSON字符串并格式化
let jsonObject: any = null;
// 1. 尝试获取JSON对象
if (typeof content === 'string') {
// 检查是否是JSON字符串
try {
const trimmed = content.trim();
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
jsonObject = JSON.parse(trimmed); // 解析JSON字符串
console.log('🔄 检测到JSON字符串,已解析为对象');
} else {
displayText = content; // 普通文本,直接显示
}
} catch (e) {
displayText = content; // 解析失败,当作普通文本
}
} else if (typeof content === 'object') {
jsonObject = content;
}
// 2. 如果是JSON对象,进行格式化
if (jsonObject) {
displayText = this.formatJSONToText(jsonObject);
// 如果格式化失败或内容过短,使用后备方案
if (!displayText || displayText.trim().length < 50) {
displayText = this.fallbackFormatJSON(jsonObject);
}
// 最后兜底:美化JSON
if (!displayText || displayText.trim().length < 20) {
displayText = this.beautifyJSON(jsonObject);
}
}
关键改进:
{或[开头(JSON格式)JSON.parse()解析为对象修复前:
对话框显示:
{
"spaceType": "客厅",
"spacePositioning": "该空间定位为..."
}
修复后:
对话框显示:
一、空间定位与场景属性
该空间定位为高品质居住环境中的核心社交与休闲区域...
二、空间布局与动线
空间采用L型规划...
design-analysis-ai.service.ts (第91-147行)
{或[开头)修复时间: 2024-12-01 修复人员: Cascade AI 测试状态: ✅ 待验证 最后更新: 2024-12-01 16:30 (新增对话框JSON显示修复)