| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- import { Injectable } from '@angular/core';
- import { FmodeParse } from 'fmode-ng/core';
- import { FmodeChatCompletion, completionJSON } from 'fmode-ng/core/agent/chat/completion';
- const Parse = FmodeParse.with('nova');
- /**
- * 室内设计AI分析服务
- * 使用豆包1.6模型进行设计分析
- */
- @Injectable({
- providedIn: 'root'
- })
- export class DesignAnalysisAIService {
-
- // AI模型配置(豆包1.6)
- private readonly AI_MODEL = 'fmode-1.6-cn';
- constructor() {}
- /**
- * 分析参考图片,识别场景类型和设计维度
- */
- async analyzeReferenceImages(options: {
- images: string[];
- textDescription?: string;
- spaceType?: string;
- conversationHistory?: Array<{ role: string; content: string }>;
- deepThinking?: boolean;
- onProgressChange?: (progress: string) => void;
- onContentStream?: (content: string) => void; // 新增:流式内容回调
- loading?: any;
- }): Promise<any> {
- return new Promise(async (resolve, reject) => {
- try {
- // 构建详细的分析提示词
- const prompt = this.buildAnalysisPrompt(
- options.spaceType,
- options.textDescription,
- options.conversationHistory,
- options.deepThinking
- );
- options.onProgressChange?.('正在识别场景和分析设计维度...');
- // 🔥 直接使用图片URL,不转base64(参考ai-k12-daofa的实现)
- console.log('📸 准备传入图片URL到AI模型...');
- console.log('📸 图片URL列表:', options.images);
- // 日志输出,帮助调试
- console.log('🤖 调用豆包1.6模型进行vision分析...');
- console.log('📸 图片数量:', options.images.length);
- console.log('📝 提示词长度:', prompt.length, '字符');
- console.log('🏠 空间类型:', options.spaceType);
- console.log('💬 对话历史:', options.conversationHistory?.length || 0, '条');
-
- // 检查提示词长度(建议不超过10000字符)
- if (prompt.length > 10000) {
- console.warn('⚠️ 提示词过长,可能导致API调用失败');
- }
- // 🔥 使用completionJSON进行vision分析(严格参考ai-k12-daofa的成功实现)
- console.log('🚀 开始调用completionJSON进行vision分析...');
-
- // 定义JSON schema(与提示词中的JSON格式完全一致)
- const outputSchema = `{
- "spaceType": "空间类型(如:客餐厅一体化、主卧、玄关等)",
- "spacePositioning": "空间定位与场景属性的详细分析",
- "layout": "空间布局与动线的详细分析",
- "hardDecoration": "硬装系统细节的详细分析(顶面、墙面、地面、门窗)",
- "colorAnalysis": "色调精准分析(主色调、辅助色、色调关系)",
- "materials": "材质应用解析(自然材质、现代材质、材质对比)",
- "form": "形体与比例分析(空间形体、家具形体、造型细节)",
- "style": "风格与氛围营造(风格识别、氛围手法)",
- "suggestions": "专业优化建议(居住适配、细节优化、落地可行性)",
- "summary": "简洁摘要(格式: 空间类型 | 风格 | 色调 | 氛围)"
- }`;
-
- // 流式内容累积
- let streamContent = '';
-
- try {
- // 🔥 关键:使用completionJSON + vision: true + images (URL数组)
- console.log('📤 发送给AI的提示词:', prompt);
- console.log('📤 JSON Schema:', outputSchema);
- console.log('📤 图片URL:', options.images);
-
- const result = await completionJSON(
- prompt,
- outputSchema, // 🔥 关键:提供JSON schema
- (content) => {
- // 流式回调(模拟)
- console.log('📥 AI流式响应:', typeof content, content);
- if (content && options.onContentStream) {
- streamContent = content;
- // 将JSON转为易读文本
- const displayText = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
- options.onContentStream(displayText);
- options.onProgressChange?.(`已接收 ${displayText.length} 字符...`);
- }
- },
- 2, // 重试次数
- {
- model: this.AI_MODEL,
- vision: true, // 🔥 关键:启用vision
- images: options.images, // 🔥 关键:直接传URL数组
- max_tokens: 8000
- }
- );
-
- console.log('📥 AI最终返回结果:', result);
- console.log('📥 返回结果类型:', typeof result);
- // 获取最终内容(result应该就是JSON对象)
- const analysisResult = result;
-
- console.log('✅ AI分析完成,返回JSON对象:', analysisResult);
- console.log('📝 AI返回JSON预览:', JSON.stringify(analysisResult).substring(0, 500));
- // 验证返回的JSON结构
- if (!analysisResult || typeof analysisResult !== 'object') {
- console.error('❌ AI返回格式错误,不是JSON对象');
- reject(new Error('AI返回格式异常,请重试'));
- return;
- }
- // 检查必要字段
- if (!analysisResult.spaceType || !analysisResult.spacePositioning) {
- console.error('❌ AI返回JSON缺少必要字段');
- console.error('🔍 AI返回的完整对象:', analysisResult);
- reject(new Error('AI分析结果不完整,请重试'));
- return;
- }
-
- // 解析JSON结果
- const analysisData = this.parseJSONAnalysis(analysisResult);
-
- console.log('📊 解析后的分析数据:', analysisData);
- resolve(analysisData);
- } catch (err: any) {
- console.error('❌ completionJSON失败,详细错误:', err);
- console.error('❌ 错误类型:', err?.constructor?.name);
- console.error('❌ 错误消息:', err?.message);
-
- // 🔥 关键:如果completionJSON失败,尝试使用FmodeChatCompletion作为备选方案
- if (err?.message?.includes('JSON') || err?.message?.includes('格式')) {
- console.warn('⚠️ completionJSON解析失败,尝试使用FmodeChatCompletion备选方案...');
-
- try {
- // 使用FmodeChatCompletion获取纯文本响应
- const textPrompt = this.buildTextAnalysisPrompt(options.spaceType, options.textDescription);
- const messageList = [{
- role: 'user',
- content: textPrompt,
- images: options.images
- }];
-
- const completion = new FmodeChatCompletion(messageList, {
- model: this.AI_MODEL,
- max_tokens: 8000
- });
-
- let fullContent = '';
- const subscription = completion.sendCompletion({
- isDirect: true,
- }).subscribe({
- next: (message: any) => {
- const content = message?.content || '';
- if (content) {
- fullContent = content;
- options.onContentStream?.(content);
- }
-
- if (message?.complete && fullContent) {
- console.log('✅ FmodeChatCompletion备选方案成功,内容长度:', fullContent.length);
- const analysisData = this.parseAnalysisContent(fullContent);
- resolve(analysisData);
- subscription?.unsubscribe();
- }
- },
- error: (err2: any) => {
- console.error('❌ FmodeChatCompletion备选方案也失败:', err2);
- reject(new Error('AI分析失败,请稍后重试'));
- subscription?.unsubscribe();
- }
- });
-
- return; // 使用备选方案,不继续执行下面的reject
- } catch (fallbackErr: any) {
- console.error('❌ 备选方案失败:', fallbackErr);
- }
- }
-
- // 根据错误类型提供更具体的错误信息
- let errorMessage = 'AI分析失败';
- if (err?.message?.includes('500')) {
- errorMessage = 'AI服务暂时不可用(服务器错误),请稍后重试';
- } else if (err?.message?.includes('timeout')) {
- errorMessage = 'AI分析超时,请减少图片数量或简化需求后重试';
- } else if (err?.message?.includes('token')) {
- errorMessage = '提示词过长,请简化描述或减少对话历史';
- } else if (err?.message) {
- errorMessage = `AI分析失败: ${err.message}`;
- }
-
- reject(new Error(errorMessage));
- }
- } catch (error: any) {
- reject(new Error('分析失败: ' + error.message));
- }
- });
- }
- /**
- * 构建纯文本分析提示词(用于FmodeChatCompletion备选方案)
- */
- private buildTextAnalysisPrompt(spaceType?: string, textDescription?: string): string {
- let prompt = `请对图片中的室内设计进行专业分析,从以下8个维度详细展开:
- 一、空间定位与场景属性
- 二、空间布局与动线
- 三、硬装系统细节
- 四、色调精准分析
- 五、材质应用解析
- 六、形体与比例
- 七、风格与氛围营造
- 八、专业优化建议
- 要求:
- 1. 基于图片实际视觉内容进行分析
- 2. 每个维度2-4个段落,每段3-5行
- 3. 使用专业的室内设计术语
- 4. 不使用Markdown符号,使用纯文本格式`;
- if (spaceType) {
- prompt += `\n5. 空间类型参考: ${spaceType}`;
- }
- if (textDescription) {
- prompt += `\n6. 客户需求参考: ${textDescription}`;
- }
- return prompt;
- }
- /**
- * 构建AI分析提示词(JSON格式输出,兼容completionJSON)
- * 参考ai-k12-daofa的简洁提示词风格
- */
- private buildAnalysisPrompt(spaceType?: string, textDescription?: string, conversationHistory?: Array<{ role: string; content: string }>, deepThinking?: boolean): string {
- // 🔥 关键:提示词要简洁明了,直接要求JSON格式(参考ai-k12-daofa)
- let prompt = `请分析图片中的室内设计,并按以下JSON格式输出:
- {
- "spaceType": "空间类型(如:客餐厅一体化、主卧、玄关等)",
- "spacePositioning": "空间定位与场景属性的详细分析",
- "layout": "空间布局与动线的详细分析",
- "hardDecoration": "硬装系统细节的详细分析(顶面、墙面、地面、门窗)",
- "colorAnalysis": "色调精准分析(主色调、辅助色、色调关系)",
- "materials": "材质应用解析(自然材质、现代材质、材质对比)",
- "form": "形体与比例分析(空间形体、家具形体、造型细节)",
- "style": "风格与氛围营造(风格识别、氛围手法)",
- "suggestions": "专业优化建议(居住适配、细节优化、落地可行性)",
- "summary": "简洁摘要(格式: 空间类型 | 风格 | 色调 | 氛围)"
- }
- 要求:
- 1. 基于图片实际视觉内容进行分析
- 2. 每个字段提供详细描述(200-400字)
- 3. 使用专业的室内设计术语
- 4. 不提及品牌名称,仅描述材质、色调、形态`;
- // 添加空间类型提示
- if (spaceType) {
- prompt += `\n5. 空间类型参考: ${spaceType}`;
- }
- // 添加客户需求提示
- if (textDescription) {
- prompt += `\n6. 客户需求参考: ${textDescription}`;
- }
-
- return prompt;
- }
- /**
- * 解析JSON格式的AI分析结果(新方法,处理completionJSON返回的JSON对象)
- */
- private parseJSONAnalysis(jsonResult: any): any {
- console.log('📝 解析JSON分析结果...');
- // 将JSON字段转换为易读的格式化文本
- const formattedContent = this.formatJSONToText(jsonResult);
- return {
- rawContent: JSON.stringify(jsonResult, null, 2), // 原始JSON
- formattedContent: formattedContent, // 格式化文本
- structuredData: {
- spacePositioning: jsonResult.spacePositioning || '',
- layout: jsonResult.layout || '',
- hardDecoration: jsonResult.hardDecoration || '',
- colorAnalysis: jsonResult.colorAnalysis || '',
- materials: jsonResult.materials || '',
- form: jsonResult.form || '',
- style: jsonResult.style || '',
- suggestions: jsonResult.suggestions || ''
- },
- spaceType: jsonResult.spaceType || '',
- summary: jsonResult.summary || '',
- hasContent: true,
- timestamp: new Date().toISOString()
- };
- }
- /**
- * 将JSON结果转换为易读的文本格式
- */
- private formatJSONToText(jsonResult: any): string {
- const sections = [];
-
- if (jsonResult.spacePositioning) {
- sections.push(`一、空间定位与场景属性\n\n${jsonResult.spacePositioning}\n`);
- }
- if (jsonResult.layout) {
- sections.push(`二、空间布局与动线\n\n${jsonResult.layout}\n`);
- }
- if (jsonResult.hardDecoration) {
- sections.push(`三、硬装系统细节\n\n${jsonResult.hardDecoration}\n`);
- }
- if (jsonResult.colorAnalysis) {
- sections.push(`四、色调精准分析\n\n${jsonResult.colorAnalysis}\n`);
- }
- if (jsonResult.materials) {
- sections.push(`五、材质应用解析\n\n${jsonResult.materials}\n`);
- }
- if (jsonResult.form) {
- sections.push(`六、形体与比例\n\n${jsonResult.form}\n`);
- }
- if (jsonResult.style) {
- sections.push(`七、风格与氛围营造\n\n${jsonResult.style}\n`);
- }
- if (jsonResult.suggestions) {
- sections.push(`八、专业优化建议\n\n${jsonResult.suggestions}\n`);
- }
-
- return sections.join('\n');
- }
- /**
- * 解析AI分析内容(优化版:格式化处理,确保结构清晰)
- * @deprecated 使用parseJSONAnalysis代替
- */
- private parseAnalysisContent(content: string): any {
- console.log('📝 AI返回的原始内容长度:', content.length);
- console.log('📝 AI返回的内容预览:', content.substring(0, 500));
- if (!content || content.length < 50) {
- console.warn('⚠️ AI返回内容过短或为空');
- return {
- rawContent: content,
- formattedContent: content,
- hasContent: false,
- timestamp: new Date().toISOString()
- };
- }
- // 格式化处理:优化段落、间距、结构
- const formattedContent = this.formatAnalysisContent(content);
- // 提取结构化信息
- const structuredData = this.extractStructuredInfo(content);
- return {
- rawContent: content, // 原始AI输出
- formattedContent: formattedContent, // 格式化后的内容
- structuredData: structuredData, // 结构化数据(维度分段)
- hasContent: content.length > 50,
- timestamp: new Date().toISOString()
- };
- }
- /**
- * 格式化分析内容:优化排版、段落、间距
- */
- private formatAnalysisContent(content: string): string {
- let formatted = content;
- // 1. 统一维度标题格式(确保维度标题前后有空行)
- const dimensionPattern = /([一二三四五六七八九十]、[^\n]+)/g;
- formatted = formatted.replace(dimensionPattern, '\n\n$1\n');
- // 2. 处理过长段落:如果段落超过300字,尝试在句号处换行
- const paragraphs = formatted.split('\n');
- const processedParagraphs = paragraphs.map(para => {
- if (para.trim().length > 300) {
- // 在句号、问号、感叹号后添加换行,但保持在段落内
- return para.replace(/([。!?])(?=[^。!?\n]{50,})/g, '$1\n');
- }
- return para;
- });
- formatted = processedParagraphs.join('\n');
- // 3. 清理多余空行(超过2个连续空行压缩为2个)
- formatted = formatted.replace(/\n{3,}/g, '\n\n');
- // 4. 确保维度之间有明确的空行分隔
- formatted = formatted.replace(/(一、|二、|三、|四、|五、|六、|七、|八、)/g, '\n\n$1');
- // 5. 移除开头和结尾的多余空行
- formatted = formatted.trim();
- // 6. 确保每个维度内部段落之间有适当间距
- formatted = formatted.replace(/([。!?])\s*\n(?=[^\n一二三四五六七八])/g, '$1\n\n');
- // 7. 最后清理:确保格式整洁
- formatted = formatted.replace(/\n{3,}/g, '\n\n');
- return formatted;
- }
- /**
- * 提取结构化信息:将内容按维度分段
- */
- private extractStructuredInfo(content: string): any {
- const dimensions: any = {
- spacePositioning: '', // 空间定位与场景属性
- layout: '', // 空间布局与动线
- hardDecoration: '', // 硬装系统细节
- colorAnalysis: '', // 色调精准分析
- materials: '', // 材质应用解析
- form: '', // 形体与比例
- style: '', // 风格与氛围营造
- suggestions: '' // 专业优化建议
- };
- // 按维度标题分割内容
- const dimensionRegex = /([一二三四五六七八]、[^\n]+)\n+([\s\S]*?)(?=\n[一二三四五六七八]、|$)/g;
- let match;
- while ((match = dimensionRegex.exec(content)) !== null) {
- const title = match[1].trim();
- const contentText = match[2].trim();
- // 根据标题关键词匹配到对应维度
- if (title.includes('空间定位') || title.includes('场景属性')) {
- dimensions.spacePositioning = contentText;
- } else if (title.includes('布局') || title.includes('动线')) {
- dimensions.layout = contentText;
- } else if (title.includes('硬装') || title.includes('系统细节')) {
- dimensions.hardDecoration = contentText;
- } else if (title.includes('色调') || title.includes('色彩')) {
- dimensions.colorAnalysis = contentText;
- } else if (title.includes('材质')) {
- dimensions.materials = contentText;
- } else if (title.includes('形体') || title.includes('比例')) {
- dimensions.form = contentText;
- } else if (title.includes('风格') || title.includes('氛围')) {
- dimensions.style = contentText;
- } else if (title.includes('建议') || title.includes('优化')) {
- dimensions.suggestions = contentText;
- }
- }
- return dimensions;
- }
- /**
- * 生成简洁摘要:提取关键信息,适合客服和设计师快速查看
- */
- generateBriefSummary(analysisData: any): string {
- if (!analysisData || !analysisData.rawContent) {
- return '暂无分析内容';
- }
- const content = analysisData.rawContent;
- const summary: string[] = [];
- // 1. 提取空间类型
- const spaceTypeMatch = content.match(/(?:这是|空间为|属于).*?([^\n]{2,20}?(?:空间|客厅|餐厅|卧室|厨房|卫生间|玄关|书房))/);
- if (spaceTypeMatch) {
- summary.push(spaceTypeMatch[1].trim());
- }
- // 2. 提取风格关键词
- const styleKeywords = ['现代', '法式', '简约', '极简', '轻奢', '新中式', '日式', '北欧', '工业风', '台式', '侘寂', '美式'];
- const foundStyles: string[] = [];
- styleKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundStyles.includes(keyword)) {
- foundStyles.push(keyword);
- }
- });
- if (foundStyles.length > 0) {
- summary.push(foundStyles.join('+'));
- }
- // 3. 提取色调关键词
- const colorKeywords = ['暖色系', '冷色系', '暖调', '冷调', '高级灰', '奶白', '米白', '暖灰', '木色', '原木色'];
- const foundColors: string[] = [];
- colorKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundColors.includes(keyword)) {
- foundColors.push(keyword);
- }
- });
- if (foundColors.length > 0) {
- summary.push(foundColors.join('、'));
- }
- // 4. 提取氛围关键词
- const moodKeywords = ['温馨', '舒适', '精致', '高级', '松弛', '静谧', '优雅', '时尚', '女性向', '男性向', '亲子'];
- const foundMoods: string[] = [];
- moodKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundMoods.includes(keyword)) {
- foundMoods.push(keyword);
- }
- });
- if (foundMoods.length > 0) {
- summary.push(foundMoods.slice(0, 2).join('、'));
- }
- // 5. 提取关键材质
- const materialKeywords = ['木材', '大理石', '瓷砖', '布艺', '皮革', '金属', '玻璃', '护墙板'];
- const foundMaterials: string[] = [];
- materialKeywords.forEach(keyword => {
- if (content.includes(keyword) && !foundMaterials.includes(keyword)) {
- foundMaterials.push(keyword);
- }
- });
- if (foundMaterials.length > 0) {
- summary.push('主要材质:' + foundMaterials.slice(0, 3).join('、'));
- }
- return summary.length > 0 ? summary.join(' | ') : '整体设计基于图片实际内容分析';
- }
- /**
- * 生成客服标注格式:提取客户要求的关键点
- */
- generateCustomerServiceNotes(analysisData: any, customerRequirements?: string): string {
- if (!analysisData) {
- return '暂无标注内容';
- }
- const notes: string[] = [];
-
- // 优先使用JSON格式的structuredData,否则使用rawContent
- const structuredData = analysisData.structuredData;
- const rawContent = analysisData.rawContent || '';
- // 1. 客户要求(如果有)
- if (customerRequirements) {
- notes.push(`【客户要求】\n${customerRequirements}`);
- }
- // 2. 空间类型识别
- let spaceType = '';
- if (structuredData?.spaceType) {
- spaceType = structuredData.spaceType;
- } else {
- // 从rawContent提取
- const spaceMatch = rawContent.match(/(?:这是|空间为|属于).*?([^\n]{2,20}?(?:空间|客厅|餐厅|卧室|厨房|卫生间|玄关|书房|客餐厅一体化|三室两厅|两室一厅))/);
- if (spaceMatch) spaceType = spaceMatch[1].trim();
- }
- if (spaceType) {
- notes.push(`【空间类型】\n${spaceType}`);
- }
- // 3. 风格定位(从结构化数据或rawContent提取)
- let styleInfo = '';
- if (structuredData?.style) {
- // 提取风格关键词
- const styleKeywords = ['现代', '法式', '简约', '极简', '轻奢', '新中式', '日式', '北欧', '工业风', '侘寂', '美式', '台式'];
- const foundStyles: string[] = [];
- styleKeywords.forEach(keyword => {
- if (structuredData.style.includes(keyword) && !foundStyles.includes(keyword)) {
- foundStyles.push(keyword);
- }
- });
- if (foundStyles.length > 0) {
- styleInfo = foundStyles.join('+') + '风格';
- }
-
- // 提取氛围描述
- const moodMatch = structuredData.style.match(/(?:氛围|营造|呈现).*?([^\n。]{5,30}?(?:温馨|舒适|精致|高级|松弛|静谧|优雅|时尚))/);
- if (moodMatch) {
- styleInfo += `,${moodMatch[1].trim()}`;
- }
- } else if (rawContent) {
- // 从rawContent提取风格
- const styleMatch = rawContent.match(/(?:风格|呈现|属于).*?([^\n。]{5,40}?(?:风格|法式|现代|简约|极简))/);
- if (styleMatch) styleInfo = styleMatch[1].trim();
- }
- if (styleInfo) {
- notes.push(`【风格定位】\n${styleInfo}`);
- }
- // 4. 色调要求(从色彩分析提取)
- let colorInfo = '';
- if (structuredData?.colorAnalysis) {
- // 提取主色调
- const mainColorMatch = structuredData.colorAnalysis.match(/主色调[::]\s*([^\n。]{5,50})/);
- if (mainColorMatch) {
- colorInfo = `主色调:${mainColorMatch[1].trim()}`;
- }
-
- // 提取辅助色
- const subColorMatch = structuredData.colorAnalysis.match(/辅助色[::]\s*([^\n。]{5,50})/);
- if (subColorMatch) {
- colorInfo += `\n辅助色:${subColorMatch[1].trim()}`;
- }
- } else if (rawContent) {
- // 从rawContent提取色调
- const colorMatch = rawContent.match(/(?:色调|色彩|主色)[::]\s*([^\n。]{5,50})/);
- if (colorMatch) colorInfo = colorMatch[1].trim();
- }
- if (colorInfo) {
- notes.push(`【色调要求】\n${colorInfo}`);
- }
- // 5. 材质要求(从硬装和材质维度提取)
- const materials: string[] = [];
- if (structuredData?.hardDecoration) {
- // 提取地面材质
- const floorMatch = structuredData.hardDecoration.match(/地面[::]\s*([^\n。]{5,40})/);
- if (floorMatch) materials.push(`地面:${floorMatch[1].trim()}`);
-
- // 提取墙面材质
- const wallMatch = structuredData.hardDecoration.match(/墙面[::]\s*([^\n。]{5,40})/);
- if (wallMatch) materials.push(`墙面:${wallMatch[1].trim()}`);
-
- // 提取顶面材质
- const ceilingMatch = structuredData.hardDecoration.match(/顶面[::]\s*([^\n。]{5,40})/);
- if (ceilingMatch) materials.push(`顶面:${ceilingMatch[1].trim()}`);
- }
-
- // 从材质维度补充
- if (structuredData?.materials && materials.length < 2) {
- const materialMatch = structuredData.materials.match(/(?:主要材质|材质应用)[::]\s*([^\n。]{10,60})/);
- if (materialMatch) materials.push(materialMatch[1].trim());
- }
-
- if (materials.length > 0) {
- notes.push(`【材质要求】\n${materials.join('\n')}`);
- }
- // 6. 空间布局要点
- if (structuredData?.layout) {
- const layoutMatch = structuredData.layout.match(/(?:布局特点|空间关系)[::]\s*([^\n。]{10,60})/);
- if (layoutMatch) {
- notes.push(`【布局要点】\n${layoutMatch[1].trim()}`);
- }
- }
- // 7. 施工注意事项(从优化建议提取)
- if (structuredData?.suggestions) {
- const attentionPoints: string[] = [];
-
- // 提取落地可行性
- const feasibilityMatch = structuredData.suggestions.match(/落地可行性[::]\s*([^\n。]{10,80})/);
- if (feasibilityMatch) attentionPoints.push(feasibilityMatch[1].trim());
-
- // 提取细节优化
- const detailMatch = structuredData.suggestions.match(/细节优化[::]\s*([^\n。]{10,80})/);
- if (detailMatch) attentionPoints.push(detailMatch[1].trim());
-
- if (attentionPoints.length > 0) {
- notes.push(`【施工注意】\n${attentionPoints.join('\n')}`);
- }
- }
- // 8. 品质要求(固定添加)
- notes.push(`【品质要求】\n新客户,需严格把控施工品质和材料质量`);
- return notes.length > 0 ? notes.join('\n\n') : '请根据分析内容补充具体要求';
- }
- /**
- * 生成客户报告(此方法保留以便后续使用)
- */
- async generateClientReport(options: {
- analysisData: any;
- spaceName: string;
- onContentChange?: (content: string) => void;
- loading?: any;
- }): Promise<string> {
- return new Promise(async (resolve, reject) => {
- try {
- // 使用格式化后的内容
- const content = options.analysisData?.formattedContent || options.analysisData?.rawContent || '暂无报告内容';
- resolve(content);
- } catch (error: any) {
- reject(new Error('生成报告失败: ' + error.message));
- }
- });
- }
- }
|