|
|
@@ -182,22 +182,75 @@ def generate_post_detail_html(post_data: Dict, post_idx: int) -> str:
|
|
|
return html
|
|
|
|
|
|
|
|
|
-def generate_inspiration_detail_html(inspiration_point: Dict) -> str:
|
|
|
- """生成灵感点详情HTML"""
|
|
|
+def generate_inspiration_detail_html(inspiration_point: Dict, feature_status_map: Dict[str, str] = None) -> str:
|
|
|
+ """生成灵感点详情HTML
|
|
|
+
|
|
|
+ Args:
|
|
|
+ inspiration_point: 灵感点数据
|
|
|
+ feature_status_map: 特征名称到状态的映射 {特征名称: "相同"|"相似"|"无关"}
|
|
|
+ """
|
|
|
name = inspiration_point.get("名称", "")
|
|
|
desc = inspiration_point.get("描述", "")
|
|
|
features = inspiration_point.get("特征列表", [])
|
|
|
|
|
|
- features_html = "".join([
|
|
|
- f'<span class="feature-tag">{html_module.escape(f if isinstance(f, str) else f.get("特征名称", ""))} <span class="feature-weight">({f.get("权重", 1.0) if isinstance(f, dict) else 1.0})</span></span>'
|
|
|
- for f in features
|
|
|
- ])
|
|
|
+ if feature_status_map is None:
|
|
|
+ feature_status_map = {}
|
|
|
+
|
|
|
+ # 计算该灵感点的整体结论
|
|
|
+ feature_statuses = []
|
|
|
+ features_html_list = []
|
|
|
+ for f in features:
|
|
|
+ feature_name = f if isinstance(f, str) else f.get("特征名称", "")
|
|
|
+ weight = f.get("权重", 1.0) if isinstance(f, dict) else 1.0
|
|
|
+
|
|
|
+ # 获取该特征的状态
|
|
|
+ status = feature_status_map.get(feature_name, "无关")
|
|
|
+ feature_statuses.append(status)
|
|
|
+
|
|
|
+ if status == "相同":
|
|
|
+ status_class = "feature-same"
|
|
|
+ status_label = "相同"
|
|
|
+ elif status == "相似":
|
|
|
+ status_class = "feature-similar"
|
|
|
+ status_label = "相似"
|
|
|
+ else:
|
|
|
+ status_class = "feature-unrelated"
|
|
|
+ status_label = "无关"
|
|
|
+
|
|
|
+ features_html_list.append(
|
|
|
+ f'<span class="feature-tag {status_class}">'
|
|
|
+ f'<span class="feature-status-label">{status_label}</span> '
|
|
|
+ f'{html_module.escape(feature_name)} '
|
|
|
+ f'<span class="feature-weight">({weight})</span>'
|
|
|
+ f'</span>'
|
|
|
+ )
|
|
|
+
|
|
|
+ features_html = "".join(features_html_list)
|
|
|
+
|
|
|
+ # 计算灵感点结论
|
|
|
+ has_same = "相同" in feature_statuses
|
|
|
+ has_similar = "相似" in feature_statuses
|
|
|
+ has_unrelated = "无关" in feature_statuses
|
|
|
+
|
|
|
+ if not has_unrelated:
|
|
|
+ # 没有无关的 -> 找到
|
|
|
+ insp_conclusion = "找到"
|
|
|
+ insp_conclusion_class = "insp-conclusion-found"
|
|
|
+ elif has_same or has_similar:
|
|
|
+ # 有相同或相似,但也有无关 -> 部分找到
|
|
|
+ insp_conclusion = "部分找到"
|
|
|
+ insp_conclusion_class = "insp-conclusion-partial"
|
|
|
+ else:
|
|
|
+ # 都是无关 -> 都找不到
|
|
|
+ insp_conclusion = "都找不到"
|
|
|
+ insp_conclusion_class = "insp-conclusion-not-found"
|
|
|
|
|
|
html = f'''
|
|
|
<div class="inspiration-detail-card">
|
|
|
<div class="inspiration-header">
|
|
|
<span class="inspiration-type-badge">灵感点</span>
|
|
|
<h3 class="inspiration-name">{html_module.escape(name)}</h3>
|
|
|
+ <span class="inspiration-conclusion {insp_conclusion_class}">{insp_conclusion}</span>
|
|
|
</div>
|
|
|
<div class="inspiration-description">
|
|
|
<div class="desc-label">描述:</div>
|
|
|
@@ -249,6 +302,123 @@ def load_feature_source_mapping() -> Dict:
|
|
|
return {}
|
|
|
|
|
|
|
|
|
+def generate_single_match_html(match: Dict, match_idx: int, post_idx: int, insp_idx: int, feature_idx: int, category_mapping: Dict = None, source_mapping: Dict = None) -> str:
|
|
|
+ """生成单个匹配项的HTML
|
|
|
+
|
|
|
+ Args:
|
|
|
+ match: 单个匹配数据
|
|
|
+ match_idx: 匹配项索引
|
|
|
+ post_idx: 帖子索引
|
|
|
+ insp_idx: 灵感点索引
|
|
|
+ feature_idx: 特征索引
|
|
|
+ category_mapping: 特征分类映射
|
|
|
+ source_mapping: 特征来源映射
|
|
|
+ """
|
|
|
+ persona_name = match.get("人设特征名称", "")
|
|
|
+ feature_type = match.get("特征类型", "")
|
|
|
+ feature_categories = match.get("特征分类", [])
|
|
|
+ match_result = match.get("匹配结果", {})
|
|
|
+ similarity = match_result.get("相似度", 0.0)
|
|
|
+ explanation = match_result.get("说明", "")
|
|
|
+
|
|
|
+ # 根据相似度确定颜色和标签
|
|
|
+ if similarity >= 0.9:
|
|
|
+ color = "#10b981" # 绿色 - 相同
|
|
|
+ label = "相同"
|
|
|
+ elif similarity >= 0.8:
|
|
|
+ color = "#f59e0b" # 橙色 - 相似
|
|
|
+ label = "相似"
|
|
|
+ else:
|
|
|
+ color = "#9ca3af" # 灰色 - 无关
|
|
|
+ label = "无关"
|
|
|
+
|
|
|
+ match_id = f"post-{post_idx}-insp-{insp_idx}-feat-{feature_idx}-match-{match_idx}"
|
|
|
+
|
|
|
+ # 生成特征类型和分类标签
|
|
|
+ type_badge_html = ""
|
|
|
+ if feature_type:
|
|
|
+ type_badge_html = f'<span class="feature-type-badge">{html_module.escape(feature_type)}</span>'
|
|
|
+
|
|
|
+ categories_badge_html = ""
|
|
|
+ if feature_categories:
|
|
|
+ categories_text = " / ".join(feature_categories)
|
|
|
+ categories_badge_html = f'<span class="feature-category-badge">{html_module.escape(categories_text)}</span>'
|
|
|
+
|
|
|
+ # 获取该人设特征的分类信息
|
|
|
+ categories_html = ""
|
|
|
+ if category_mapping and persona_name:
|
|
|
+ found_categories = None
|
|
|
+ # 依次在灵感点、关键点、目的点中查找
|
|
|
+ for persona_type in ["灵感点", "关键点", "目的点"]:
|
|
|
+ if persona_type in category_mapping:
|
|
|
+ type_mapping = category_mapping[persona_type]
|
|
|
+ if persona_name in type_mapping:
|
|
|
+ found_categories = type_mapping[persona_name].get("所属分类", [])
|
|
|
+ break
|
|
|
+
|
|
|
+ if found_categories:
|
|
|
+ # 简洁样式:[大类/中类/小类]
|
|
|
+ categories_reversed = list(reversed(found_categories))
|
|
|
+ categories_text = "/".join(categories_reversed)
|
|
|
+ categories_html = f'<span class="category-simple">[{html_module.escape(categories_text)}]</span>'
|
|
|
+
|
|
|
+ # 获取该人设特征的历史帖子来源
|
|
|
+ historical_posts_html = ""
|
|
|
+ if source_mapping and persona_name and persona_name in source_mapping:
|
|
|
+ source_list = source_mapping[persona_name]
|
|
|
+ if source_list:
|
|
|
+ historical_cards = []
|
|
|
+ for source_item in source_list:
|
|
|
+ post_detail = source_item.get("帖子详情", {})
|
|
|
+ if post_detail:
|
|
|
+ card_html = generate_historical_post_card_html(post_detail, source_item)
|
|
|
+ historical_cards.append(card_html)
|
|
|
+
|
|
|
+ if historical_cards:
|
|
|
+ historical_posts_html = f'''
|
|
|
+ <div class="historical-posts-section">
|
|
|
+ <h4 class="historical-posts-title">历史帖子来源</h4>
|
|
|
+ <div class="historical-posts-grid">
|
|
|
+ {"".join(historical_cards)}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ '''
|
|
|
+
|
|
|
+ # 生成历史帖子HTML
|
|
|
+ historical_posts_html = ""
|
|
|
+ if source_mapping and persona_name and persona_name in source_mapping:
|
|
|
+ source_list = source_mapping[persona_name]
|
|
|
+ if source_list:
|
|
|
+ for source_item in source_list[:5]: # 最多5个
|
|
|
+ post_detail = source_item.get("帖子详情", {})
|
|
|
+ if post_detail:
|
|
|
+ card_html = generate_historical_post_card_html(post_detail, source_item)
|
|
|
+ historical_posts_html += card_html
|
|
|
+
|
|
|
+ # 将数据编码到data属性中
|
|
|
+ import html as html_encode
|
|
|
+ data_explanation = html_encode.escape(explanation)
|
|
|
+ data_historical = html_encode.escape(historical_posts_html)
|
|
|
+
|
|
|
+ # 生成紧凑的匹配项HTML(可点击,弹出模态框)
|
|
|
+ html = f'''
|
|
|
+ <div class="match-item-compact"
|
|
|
+ data-persona-name="{html_module.escape(persona_name)}"
|
|
|
+ data-feature-type="{html_module.escape(feature_type)}"
|
|
|
+ data-similarity="{similarity}"
|
|
|
+ data-label="{label}"
|
|
|
+ data-explanation="{data_explanation}"
|
|
|
+ data-historical-posts="{data_historical}"
|
|
|
+ onclick="showMatchDetail(this)">
|
|
|
+ {type_badge_html}
|
|
|
+ <span class="persona-name">{html_module.escape(persona_name)}</span>
|
|
|
+ <span class="score-badge">相似度: {similarity:.2f}</span>
|
|
|
+ <span class="relation-badge" style="background: {color};">{label}</span>
|
|
|
+ </div>
|
|
|
+ '''
|
|
|
+ return html
|
|
|
+
|
|
|
+
|
|
|
def generate_match_results_html(how_steps: List[Dict], feature_idx: int, insp_idx: int, post_idx: int, category_mapping: Dict = None, source_mapping: Dict = None) -> str:
|
|
|
"""生成可折叠的匹配结果HTML"""
|
|
|
if not how_steps or len(how_steps) == 0:
|
|
|
@@ -271,27 +441,45 @@ def generate_match_results_html(how_steps: List[Dict], feature_idx: int, insp_id
|
|
|
# 按相似度排序
|
|
|
sorted_matches = sorted(match_results, key=lambda x: x.get("匹配结果", {}).get("相似度", 0), reverse=True)
|
|
|
|
|
|
- # 统计相似度分布(按区间统计)
|
|
|
+ # 找出最高相似度,确定状态
|
|
|
+ max_similarity = 0.0
|
|
|
+ if match_results:
|
|
|
+ max_similarity = max(match.get("匹配结果", {}).get("相似度", 0) for match in match_results)
|
|
|
+
|
|
|
+ # 根据最高相似度确定状态
|
|
|
+ if max_similarity >= 0.9:
|
|
|
+ status = "相同"
|
|
|
+ status_class = "status-same"
|
|
|
+ elif max_similarity >= 0.8:
|
|
|
+ status = "相似"
|
|
|
+ status_class = "status-similar"
|
|
|
+ else:
|
|
|
+ status = "无关"
|
|
|
+ status_class = "status-unrelated"
|
|
|
+
|
|
|
+ found_status_html = f'<span class="feature-match-status {status_class}">{status}</span>'
|
|
|
+
|
|
|
+ # 统计相似度分布
|
|
|
similarity_ranges = {
|
|
|
- "高相似 (≥0.7)": 0,
|
|
|
- "中相似 (0.4-0.7)": 0,
|
|
|
- "低相似 (<0.4)": 0
|
|
|
+ "相同 (≥0.9)": 0,
|
|
|
+ "相似 (0.8-0.9)": 0,
|
|
|
+ "无关 (<0.8)": 0
|
|
|
}
|
|
|
for match in match_results:
|
|
|
similarity = match.get("匹配结果", {}).get("相似度", 0)
|
|
|
- if similarity >= 0.7:
|
|
|
- similarity_ranges["高相似 (≥0.7)"] += 1
|
|
|
- elif similarity >= 0.4:
|
|
|
- similarity_ranges["中相似 (0.4-0.7)"] += 1
|
|
|
+ if similarity >= 0.9:
|
|
|
+ similarity_ranges["相同 (≥0.9)"] += 1
|
|
|
+ elif similarity >= 0.8:
|
|
|
+ similarity_ranges["相似 (0.8-0.9)"] += 1
|
|
|
else:
|
|
|
- similarity_ranges["低相似 (<0.4)"] += 1
|
|
|
+ similarity_ranges["无关 (<0.8)"] += 1
|
|
|
|
|
|
# 生成统计信息
|
|
|
stats_items = []
|
|
|
range_colors = {
|
|
|
- "高相似 (≥0.7)": "#10b981",
|
|
|
- "中相似 (0.4-0.7)": "#f59e0b",
|
|
|
- "低相似 (<0.4)": "#9ca3af"
|
|
|
+ "相同 (≥0.9)": "#10b981",
|
|
|
+ "相似 (0.8-0.9)": "#f59e0b",
|
|
|
+ "无关 (<0.8)": "#9ca3af"
|
|
|
}
|
|
|
for range_name, count in similarity_ranges.items():
|
|
|
if count > 0:
|
|
|
@@ -299,95 +487,62 @@ def generate_match_results_html(how_steps: List[Dict], feature_idx: int, insp_id
|
|
|
stats_items.append(f'<span class="stat-badge" style="background: {color};">{range_name}: {count}</span>')
|
|
|
stats_html = "".join(stats_items)
|
|
|
|
|
|
- # 生成匹配项
|
|
|
- matches_html = ""
|
|
|
+ # 按特征类型分组匹配项
|
|
|
+ match_groups = {
|
|
|
+ "标签": [],
|
|
|
+ "分类": []
|
|
|
+ }
|
|
|
+
|
|
|
for i, match in enumerate(sorted_matches):
|
|
|
- persona_name = match.get("人设特征名称", "")
|
|
|
feature_type = match.get("特征类型", "")
|
|
|
- feature_categories = match.get("特征分类", [])
|
|
|
- match_result = match.get("匹配结果", {})
|
|
|
- similarity = match_result.get("相似度", 0.0)
|
|
|
- explanation = match_result.get("说明", "")
|
|
|
-
|
|
|
- # 根据相似度确定颜色
|
|
|
- if similarity >= 0.7:
|
|
|
- color = "#10b981" # 绿色 - 高相似
|
|
|
- label = "高相似"
|
|
|
- elif similarity >= 0.4:
|
|
|
- color = "#f59e0b" # 橙色 - 中相似
|
|
|
- label = "中相似"
|
|
|
- else:
|
|
|
- color = "#9ca3af" # 灰色 - 低相似
|
|
|
- label = "低相似"
|
|
|
-
|
|
|
- match_id = f"post-{post_idx}-insp-{insp_idx}-feat-{feature_idx}-match-{i}"
|
|
|
-
|
|
|
- # 生成特征类型和分类标签
|
|
|
- type_badge_html = ""
|
|
|
- if feature_type:
|
|
|
- type_badge_html = f'<span class="feature-type-badge">{html_module.escape(feature_type)}</span>'
|
|
|
-
|
|
|
- categories_badge_html = ""
|
|
|
- if feature_categories:
|
|
|
- categories_text = " / ".join(feature_categories)
|
|
|
- categories_badge_html = f'<span class="feature-category-badge">{html_module.escape(categories_text)}</span>'
|
|
|
-
|
|
|
- # 获取该人设特征的分类信息
|
|
|
- # 需要在三个类型中查找该特征
|
|
|
- categories_html = ""
|
|
|
- if category_mapping and persona_name:
|
|
|
- found_categories = None
|
|
|
- # 依次在灵感点、关键点、目的点中查找
|
|
|
- for persona_type in ["灵感点", "关键点", "目的点"]:
|
|
|
- if persona_type in category_mapping:
|
|
|
- type_mapping = category_mapping[persona_type]
|
|
|
- if persona_name in type_mapping:
|
|
|
- found_categories = type_mapping[persona_name].get("所属分类", [])
|
|
|
- break
|
|
|
-
|
|
|
- if found_categories:
|
|
|
- # 简洁样式:[大类/中类/小类]
|
|
|
- categories_reversed = list(reversed(found_categories))
|
|
|
- categories_text = "/".join(categories_reversed)
|
|
|
- categories_html = f'<span class="category-simple">[{html_module.escape(categories_text)}]</span>'
|
|
|
-
|
|
|
- # 获取该人设特征的历史帖子来源
|
|
|
- historical_posts_html = ""
|
|
|
- if source_mapping and persona_name and persona_name in source_mapping:
|
|
|
- source_list = source_mapping[persona_name]
|
|
|
- if source_list:
|
|
|
- historical_cards = []
|
|
|
- for source_item in source_list:
|
|
|
- post_detail = source_item.get("帖子详情", {})
|
|
|
- if post_detail:
|
|
|
- card_html = generate_historical_post_card_html(post_detail, source_item)
|
|
|
- historical_cards.append(card_html)
|
|
|
-
|
|
|
- if historical_cards:
|
|
|
- historical_posts_html = f'''
|
|
|
- <div class="historical-posts-section">
|
|
|
- <h4 class="historical-posts-title">历史帖子来源</h4>
|
|
|
- <div class="historical-posts-grid">
|
|
|
- {"".join(historical_cards)}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- '''
|
|
|
+ if feature_type in match_groups:
|
|
|
+ match_groups[feature_type].append((i, match))
|
|
|
+
|
|
|
+ # 生成分组的匹配项HTML
|
|
|
+ matches_html = ""
|
|
|
+
|
|
|
+ # 先显示"标签"匹配结果
|
|
|
+ if match_groups["标签"]:
|
|
|
+ group_id = f"post-{post_idx}-insp-{insp_idx}-feat-{feature_idx}-group-label"
|
|
|
+ group_matches_html = ""
|
|
|
+ for i, match in match_groups["标签"]:
|
|
|
+ match_html = generate_single_match_html(
|
|
|
+ match, i, post_idx, insp_idx, feature_idx,
|
|
|
+ category_mapping, source_mapping
|
|
|
+ )
|
|
|
+ group_matches_html += match_html
|
|
|
|
|
|
matches_html += f'''
|
|
|
- <div class="match-item-collapsible">
|
|
|
- <div class="match-header" onclick="toggleMatch('{match_id}')">
|
|
|
- <div class="match-header-left">
|
|
|
- <span class="expand-icon" id="{match_id}-icon">▶</span>
|
|
|
- <span class="persona-name">{categories_html} {html_module.escape(persona_name)}</span>
|
|
|
- {type_badge_html}
|
|
|
- {categories_badge_html}
|
|
|
- <span class="relation-badge" style="background: {color};">{label}</span>
|
|
|
- <span class="score-badge">相似度: {similarity:.2f}</span>
|
|
|
- </div>
|
|
|
+ <div class="match-group-section">
|
|
|
+ <div class="match-group-header" onclick="toggleMatchGroup('{group_id}')">
|
|
|
+ <span class="expand-icon" id="{group_id}-icon">▼</span>
|
|
|
+ <h4 class="match-group-title">匹配标签 ({len(match_groups["标签"])})</h4>
|
|
|
</div>
|
|
|
- <div class="match-content" id="{match_id}-content" style="display: none;">
|
|
|
- <div class="match-explanation">{html_module.escape(explanation)}</div>
|
|
|
- {historical_posts_html}
|
|
|
+ <div class="match-group-content" id="{group_id}-content">
|
|
|
+ {group_matches_html}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ '''
|
|
|
+
|
|
|
+ # 再显示"分类"匹配结果
|
|
|
+ if match_groups["分类"]:
|
|
|
+ group_id = f"post-{post_idx}-insp-{insp_idx}-feat-{feature_idx}-group-category"
|
|
|
+ group_matches_html = ""
|
|
|
+ for i, match in match_groups["分类"]:
|
|
|
+ match_html = generate_single_match_html(
|
|
|
+ match, i, post_idx, insp_idx, feature_idx,
|
|
|
+ category_mapping, source_mapping
|
|
|
+ )
|
|
|
+ group_matches_html += match_html
|
|
|
+
|
|
|
+ matches_html += f'''
|
|
|
+ <div class="match-group-section">
|
|
|
+ <div class="match-group-header" onclick="toggleMatchGroup('{group_id}')">
|
|
|
+ <span class="expand-icon" id="{group_id}-icon">▼</span>
|
|
|
+ <h4 class="match-group-title">匹配分类 ({len(match_groups["分类"])})</h4>
|
|
|
+ </div>
|
|
|
+ <div class="match-group-content" id="{group_id}-content">
|
|
|
+ {group_matches_html}
|
|
|
</div>
|
|
|
</div>
|
|
|
'''
|
|
|
@@ -400,6 +555,7 @@ def generate_match_results_html(how_steps: List[Dict], feature_idx: int, insp_id
|
|
|
<div class="header-left">
|
|
|
<span class="expand-icon" id="{section_id}-icon">▼</span>
|
|
|
<h4>匹配结果: {html_module.escape(feature_name)} <span class="feature-weight-display">(权重: {feature_weight})</span></h4>
|
|
|
+ {found_status_html}
|
|
|
</div>
|
|
|
<div class="match-stats">{stats_html}</div>
|
|
|
</div>
|
|
|
@@ -411,11 +567,21 @@ def generate_match_results_html(how_steps: List[Dict], feature_idx: int, insp_id
|
|
|
return html
|
|
|
|
|
|
|
|
|
-def generate_toc_html(post_data: Dict, post_idx: int) -> str:
|
|
|
- """生成目录导航HTML"""
|
|
|
+def generate_toc_html(post_data: Dict, post_idx: int, feature_status_map: Dict[str, str] = None, overall_conclusion: str = "") -> str:
|
|
|
+ """生成目录导航HTML
|
|
|
+
|
|
|
+ Args:
|
|
|
+ post_data: 帖子数据
|
|
|
+ post_idx: 帖子索引
|
|
|
+ feature_status_map: 特征名称到状态的映射 {特征名称: "相同"|"相似"|"无关"}
|
|
|
+ overall_conclusion: 整体结论
|
|
|
+ """
|
|
|
how_result = post_data.get("how解构结果", {})
|
|
|
inspiration_list = how_result.get("灵感点列表", [])
|
|
|
|
|
|
+ if feature_status_map is None:
|
|
|
+ feature_status_map = {}
|
|
|
+
|
|
|
toc_items = []
|
|
|
|
|
|
# 帖子详情
|
|
|
@@ -434,11 +600,45 @@ def generate_toc_html(post_data: Dict, post_idx: int) -> str:
|
|
|
features = how_steps[0].get("特征列表", [])
|
|
|
for feat_idx, feature_data in enumerate(features):
|
|
|
feature_name = feature_data.get("特征名称", f"特征 {feat_idx + 1}")
|
|
|
- toc_items.append(f'<div class="toc-item toc-level-2" onclick="scrollToSection(\'post-{post_idx}-feat-{insp_idx}-{feat_idx}\')"><span class="toc-badge toc-badge-feature">特征</span> {html_module.escape(feature_name)}</div>')
|
|
|
+
|
|
|
+ # 获取状态
|
|
|
+ status = feature_status_map.get(feature_name, "无关")
|
|
|
+ if status == "相同":
|
|
|
+ status_class = "toc-feature-same"
|
|
|
+ status_label = "相同"
|
|
|
+ elif status == "相似":
|
|
|
+ status_class = "toc-feature-similar"
|
|
|
+ status_label = "相似"
|
|
|
+ else:
|
|
|
+ status_class = "toc-feature-unrelated"
|
|
|
+ status_label = "无关"
|
|
|
+
|
|
|
+ toc_items.append(f'<div class="toc-item toc-level-2 {status_class}" onclick="scrollToSection(\'post-{post_idx}-feat-{insp_idx}-{feat_idx}\')"><span class="toc-badge toc-badge-feature">特征</span> {html_module.escape(feature_name)} <span class="toc-feature-status">{status_label}</span></div>')
|
|
|
+
|
|
|
+ # 整体结论HTML
|
|
|
+ conclusion_html = ""
|
|
|
+ if overall_conclusion:
|
|
|
+ if overall_conclusion == "找到":
|
|
|
+ conclusion_class = "conclusion-found"
|
|
|
+ conclusion_icon = "✓"
|
|
|
+ elif overall_conclusion == "部分找到":
|
|
|
+ conclusion_class = "conclusion-partial"
|
|
|
+ conclusion_icon = "~"
|
|
|
+ else: # 都找不到
|
|
|
+ conclusion_class = "conclusion-not-found"
|
|
|
+ conclusion_icon = "✗"
|
|
|
+
|
|
|
+ conclusion_html = f'''
|
|
|
+ <div class="toc-conclusion {conclusion_class}">
|
|
|
+ <span class="conclusion-icon">{conclusion_icon}</span>
|
|
|
+ <span class="conclusion-text">{overall_conclusion}</span>
|
|
|
+ </div>
|
|
|
+ '''
|
|
|
|
|
|
return f'''
|
|
|
<div class="toc-container">
|
|
|
<div class="toc-header">目录导航</div>
|
|
|
+ {conclusion_html}
|
|
|
<div class="toc-content">
|
|
|
{"".join(toc_items)}
|
|
|
</div>
|
|
|
@@ -448,20 +648,59 @@ def generate_toc_html(post_data: Dict, post_idx: int) -> str:
|
|
|
|
|
|
def generate_post_content_html(post_data: Dict, post_idx: int, category_mapping: Dict = None, source_mapping: Dict = None) -> str:
|
|
|
"""生成单个帖子的完整内容HTML"""
|
|
|
- # 生成目录
|
|
|
- toc_html = generate_toc_html(post_data, post_idx)
|
|
|
-
|
|
|
- # 1. 帖子详情
|
|
|
- post_detail_html = generate_post_detail_html(post_data, post_idx)
|
|
|
-
|
|
|
# 2. 灵感点详情和匹配结果
|
|
|
how_result = post_data.get("how解构结果", {})
|
|
|
inspiration_list = how_result.get("灵感点列表", [])
|
|
|
|
|
|
- # 生成所有灵感点的详情HTML(只包含灵感点详情,不包含匹配结果)
|
|
|
+ # 先计算所有特征的状态(基于最高相似度)
|
|
|
+ feature_status_map = {} # {特征名称: "相同"|"相似"|"无关"}
|
|
|
+ for inspiration_point in inspiration_list:
|
|
|
+ how_steps = inspiration_point.get("how步骤列表", [])
|
|
|
+ if how_steps:
|
|
|
+ features = how_steps[0].get("特征列表", [])
|
|
|
+ for feature_data in features:
|
|
|
+ feature_name = feature_data.get("特征名称", "")
|
|
|
+ match_results = feature_data.get("匹配结果", [])
|
|
|
+
|
|
|
+ # 找出最高相似度
|
|
|
+ max_similarity = 0.0
|
|
|
+ if match_results:
|
|
|
+ max_similarity = max(match.get("匹配结果", {}).get("相似度", 0) for match in match_results)
|
|
|
+
|
|
|
+ # 根据最高相似度确定状态
|
|
|
+ if max_similarity >= 0.9:
|
|
|
+ feature_status_map[feature_name] = "相同"
|
|
|
+ elif max_similarity >= 0.8:
|
|
|
+ feature_status_map[feature_name] = "相似"
|
|
|
+ else:
|
|
|
+ feature_status_map[feature_name] = "无关"
|
|
|
+
|
|
|
+ # 计算整体结论
|
|
|
+ status_values = list(feature_status_map.values())
|
|
|
+ has_same = "相同" in status_values
|
|
|
+ has_similar = "相似" in status_values
|
|
|
+ has_unrelated = "无关" in status_values
|
|
|
+
|
|
|
+ if not has_unrelated:
|
|
|
+ # 没有无关的 -> 找到
|
|
|
+ overall_conclusion = "找到"
|
|
|
+ elif has_same or has_similar:
|
|
|
+ # 有相同或相似,但也有无关 -> 部分找到
|
|
|
+ overall_conclusion = "部分找到"
|
|
|
+ else:
|
|
|
+ # 都是无关 -> 都找不到
|
|
|
+ overall_conclusion = "都找不到"
|
|
|
+
|
|
|
+ # 生成目录(传入状态映射和整体结论)
|
|
|
+ toc_html = generate_toc_html(post_data, post_idx, feature_status_map, overall_conclusion)
|
|
|
+
|
|
|
+ # 1. 帖子详情
|
|
|
+ post_detail_html = generate_post_detail_html(post_data, post_idx)
|
|
|
+
|
|
|
+ # 生成所有灵感点的详情HTML(传入状态映射)
|
|
|
inspirations_detail_html = ""
|
|
|
for insp_idx, inspiration_point in enumerate(inspiration_list):
|
|
|
- inspiration_detail = generate_inspiration_detail_html(inspiration_point)
|
|
|
+ inspiration_detail = generate_inspiration_detail_html(inspiration_point, feature_status_map)
|
|
|
inspirations_detail_html += f'''
|
|
|
<div id="post-{post_idx}-insp-{insp_idx}" class="inspiration-detail-item content-section">
|
|
|
{inspiration_detail}
|
|
|
@@ -721,6 +960,80 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
padding-left: 30px;
|
|
|
}}
|
|
|
|
|
|
+ .toc-level-2.toc-feature-same {{
|
|
|
+ border-left: 3px solid #10b981;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-level-2.toc-feature-similar {{
|
|
|
+ border-left: 3px solid #f59e0b;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-level-2.toc-feature-unrelated {{
|
|
|
+ border-left: 3px solid #9ca3af;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-feature-status {{
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 11px;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 4px;
|
|
|
+ margin-left: 8px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-feature-same .toc-feature-status {{
|
|
|
+ background: #d1fae5;
|
|
|
+ color: #065f46;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-feature-similar .toc-feature-status {{
|
|
|
+ background: #fed7aa;
|
|
|
+ color: #92400e;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-feature-unrelated .toc-feature-status {{
|
|
|
+ background: #e5e7eb;
|
|
|
+ color: #4b5563;
|
|
|
+ }}
|
|
|
+
|
|
|
+ /* 目录整体结论 */
|
|
|
+ .toc-conclusion {{
|
|
|
+ padding: 15px 20px;
|
|
|
+ margin: 10px;
|
|
|
+ border-radius: 8px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 14px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-conclusion.conclusion-found {{
|
|
|
+ background: #d1fae5;
|
|
|
+ color: #065f46;
|
|
|
+ border: 2px solid #10b981;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-conclusion.conclusion-partial {{
|
|
|
+ background: #fed7aa;
|
|
|
+ color: #92400e;
|
|
|
+ border: 2px solid #f59e0b;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .toc-conclusion.conclusion-not-found {{
|
|
|
+ background: #fee2e2;
|
|
|
+ color: #991b1b;
|
|
|
+ border: 2px solid #ef4444;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .conclusion-icon {{
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 700;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .conclusion-text {{
|
|
|
+ flex: 1;
|
|
|
+ }}
|
|
|
+
|
|
|
.toc-badge {{
|
|
|
display: inline-block;
|
|
|
padding: 2px 8px;
|
|
|
@@ -964,6 +1277,33 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .inspiration-conclusion {{
|
|
|
+ padding: 6px 14px;
|
|
|
+ border-radius: 16px;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 700;
|
|
|
+ margin-left: auto;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .inspiration-conclusion.insp-conclusion-found {{
|
|
|
+ background: #d1fae5;
|
|
|
+ color: #065f46;
|
|
|
+ border: 2px solid #10b981;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .inspiration-conclusion.insp-conclusion-partial {{
|
|
|
+ background: #fed7aa;
|
|
|
+ color: #92400e;
|
|
|
+ border: 2px solid #f59e0b;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .inspiration-conclusion.insp-conclusion-not-found {{
|
|
|
+ background: #fee2e2;
|
|
|
+ color: #991b1b;
|
|
|
+ border: 2px solid #ef4444;
|
|
|
}}
|
|
|
|
|
|
.inspiration-type-badge {{
|
|
|
@@ -1024,6 +1364,29 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
border-radius: 16px;
|
|
|
font-size: 13px;
|
|
|
font-weight: 500;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-tag.feature-same {{
|
|
|
+ background: #10b981;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-tag.feature-similar {{
|
|
|
+ background: #f59e0b;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-tag.feature-unrelated {{
|
|
|
+ background: #9ca3af;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-status-label {{
|
|
|
+ font-weight: 700;
|
|
|
+ font-size: 11px;
|
|
|
+ padding: 2px 6px;
|
|
|
+ background: rgba(255, 255, 255, 0.3);
|
|
|
+ border-radius: 4px;
|
|
|
}}
|
|
|
|
|
|
/* 匹配结果部分 */
|
|
|
@@ -1090,6 +1453,151 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
gap: 8px;
|
|
|
}}
|
|
|
|
|
|
+ .match-group-section {{
|
|
|
+ margin-bottom: 20px;
|
|
|
+ background: white;
|
|
|
+ border: 1px solid #e5e7eb;
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-group-header {{
|
|
|
+ padding: 12px 18px;
|
|
|
+ background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
|
|
|
+ border-bottom: 2px solid #d1d5db;
|
|
|
+ cursor: pointer;
|
|
|
+ user-select: none;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ transition: background 0.2s;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-group-header:hover {{
|
|
|
+ background: linear-gradient(135deg, #e5e7eb 0%, #d1d5db 100%);
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-group-title {{
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #374151;
|
|
|
+ margin: 0;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-group-content {{
|
|
|
+ padding: 15px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 10px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-item-compact {{
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ padding: 6px 12px;
|
|
|
+ background: white;
|
|
|
+ border: 1px solid #e5e7eb;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-size: 13px;
|
|
|
+ transition: all 0.2s;
|
|
|
+ cursor: pointer;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-item-compact:hover {{
|
|
|
+ border-color: #667eea;
|
|
|
+ box-shadow: 0 2px 4px rgba(102, 126, 234, 0.1);
|
|
|
+ transform: translateY(-1px);
|
|
|
+ }}
|
|
|
+
|
|
|
+ /* 匹配详情模态框 */
|
|
|
+ .match-modal {{
|
|
|
+ display: none;
|
|
|
+ position: fixed;
|
|
|
+ z-index: 3000;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ animation: fadeIn 0.2s;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-modal-content {{
|
|
|
+ position: relative;
|
|
|
+ background: white;
|
|
|
+ margin: 5% auto;
|
|
|
+ padding: 0;
|
|
|
+ width: 80%;
|
|
|
+ max-width: 900px;
|
|
|
+ max-height: 80vh;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
|
+ overflow: hidden;
|
|
|
+ animation: slideDown 0.3s;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-modal-header {{
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: white;
|
|
|
+ padding: 20px 25px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-modal-title {{
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin: 0;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-modal-close {{
|
|
|
+ color: white;
|
|
|
+ font-size: 32px;
|
|
|
+ font-weight: 300;
|
|
|
+ cursor: pointer;
|
|
|
+ background: none;
|
|
|
+ border: none;
|
|
|
+ padding: 0;
|
|
|
+ line-height: 1;
|
|
|
+ transition: transform 0.2s;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-modal-close:hover {{
|
|
|
+ transform: scale(1.2);
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-modal-body {{
|
|
|
+ padding: 25px;
|
|
|
+ max-height: calc(80vh - 80px);
|
|
|
+ overflow-y: auto;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-detail-section {{
|
|
|
+ margin-bottom: 25px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-detail-section h3 {{
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #374151;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-bottom: 8px;
|
|
|
+ border-bottom: 2px solid #e5e7eb;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .match-explanation-text {{
|
|
|
+ line-height: 1.8;
|
|
|
+ color: #4b5563;
|
|
|
+ padding: 15px;
|
|
|
+ background: #f9fafb;
|
|
|
+ border-radius: 8px;
|
|
|
+ border-left: 4px solid #667eea;
|
|
|
+ }}
|
|
|
+
|
|
|
+
|
|
|
.match-item-collapsible {{
|
|
|
border: 1px solid #e5e7eb;
|
|
|
border-radius: 8px;
|
|
|
@@ -1135,6 +1643,12 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
color: #111827;
|
|
|
}}
|
|
|
|
|
|
+ .match-item-compact .persona-name {{
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #374151;
|
|
|
+ }}
|
|
|
+
|
|
|
.relation-badge {{
|
|
|
padding: 3px 10px;
|
|
|
border-radius: 12px;
|
|
|
@@ -1172,6 +1686,52 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
border: 1px solid #93c5fd;
|
|
|
}}
|
|
|
|
|
|
+ .level-badge {{
|
|
|
+ padding: 4px 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-right: 6px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .level-badge-match {{
|
|
|
+ background: #e0e7ff;
|
|
|
+ color: #4338ca;
|
|
|
+ border: 1px solid #a5b4fc;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .level-badge-category {{
|
|
|
+ background: #fef3c7;
|
|
|
+ color: #92400e;
|
|
|
+ border: 1px solid #fcd34d;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-match-status {{
|
|
|
+ padding: 5px 12px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 700;
|
|
|
+ margin-left: 12px;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-match-status.status-same {{
|
|
|
+ background: #d1fae5;
|
|
|
+ color: #065f46;
|
|
|
+ border: 2px solid #10b981;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-match-status.status-similar {{
|
|
|
+ background: #fed7aa;
|
|
|
+ color: #92400e;
|
|
|
+ border: 2px solid #f59e0b;
|
|
|
+ }}
|
|
|
+
|
|
|
+ .feature-match-status.status-unrelated {{
|
|
|
+ background: #e5e7eb;
|
|
|
+ color: #4b5563;
|
|
|
+ border: 2px solid #9ca3af;
|
|
|
+ }}
|
|
|
+
|
|
|
.match-content {{
|
|
|
padding: 16px;
|
|
|
background: #f9fafb;
|
|
|
@@ -1322,7 +1882,7 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
.post-detail-modal {{
|
|
|
display: none;
|
|
|
position: fixed;
|
|
|
- z-index: 1000;
|
|
|
+ z-index: 4000;
|
|
|
left: 0;
|
|
|
top: 0;
|
|
|
width: 100%;
|
|
|
@@ -1548,6 +2108,17 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
{contents_html}
|
|
|
</div>
|
|
|
|
|
|
+ <!-- 匹配详情模态框 -->
|
|
|
+ <div id="matchModal" class="match-modal" onclick="closeMatchModal(event)">
|
|
|
+ <div class="match-modal-content" onclick="event.stopPropagation()">
|
|
|
+ <div class="match-modal-header">
|
|
|
+ <h2 class="match-modal-title" id="matchModalTitle"></h2>
|
|
|
+ <button class="match-modal-close" onclick="closeMatchModal()">×</button>
|
|
|
+ </div>
|
|
|
+ <div class="match-modal-body" id="matchModalBody"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 帖子详情模态框 -->
|
|
|
<div id="postDetailModal" class="post-detail-modal" onclick="closePostDetail(event)"></div>
|
|
|
|
|
|
@@ -1620,6 +2191,69 @@ def generate_combined_html(posts_data: List[Dict], category_mapping: Dict = None
|
|
|
}}
|
|
|
}}
|
|
|
|
|
|
+ function toggleMatchGroup(groupId) {{
|
|
|
+ var content = document.getElementById(groupId + '-content');
|
|
|
+ var icon = document.getElementById(groupId + '-icon');
|
|
|
+
|
|
|
+ if (content.style.display === 'none') {{
|
|
|
+ content.style.display = 'flex';
|
|
|
+ icon.textContent = '▼';
|
|
|
+ }} else {{
|
|
|
+ content.style.display = 'none';
|
|
|
+ icon.textContent = '▶';
|
|
|
+ }}
|
|
|
+ }}
|
|
|
+
|
|
|
+ function showMatchDetail(element) {{
|
|
|
+ var modal = document.getElementById('matchModal');
|
|
|
+ var title = document.getElementById('matchModalTitle');
|
|
|
+ var body = document.getElementById('matchModalBody');
|
|
|
+
|
|
|
+ // 从data属性读取数据
|
|
|
+ var personaName = element.dataset.personaName;
|
|
|
+ var featureType = element.dataset.featureType;
|
|
|
+ var similarity = parseFloat(element.dataset.similarity);
|
|
|
+ var label = element.dataset.label;
|
|
|
+ var explanation = element.dataset.explanation;
|
|
|
+ var historicalPosts = element.dataset.historicalPosts;
|
|
|
+
|
|
|
+ // 设置标题
|
|
|
+ title.textContent = '[' + featureType + '] ' + personaName + ' (相似度: ' + similarity.toFixed(2) + ' - ' + label + ')';
|
|
|
+
|
|
|
+ // 生成内容
|
|
|
+ var bodyHTML = '<div class="match-detail-section">';
|
|
|
+ bodyHTML += '<h3>匹配说明</h3>';
|
|
|
+ bodyHTML += '<div class="match-explanation-text">' + explanation + '</div>';
|
|
|
+ bodyHTML += '</div>';
|
|
|
+
|
|
|
+ // 如果有历史帖子
|
|
|
+ if (historicalPosts && historicalPosts.trim().length > 0) {{
|
|
|
+ bodyHTML += '<div class="match-detail-section">';
|
|
|
+ bodyHTML += '<h3>历史帖子来源</h3>';
|
|
|
+ bodyHTML += '<div class="historical-posts-grid">';
|
|
|
+ bodyHTML += historicalPosts;
|
|
|
+ bodyHTML += '</div>';
|
|
|
+ bodyHTML += '</div>';
|
|
|
+ }}
|
|
|
+
|
|
|
+ body.innerHTML = bodyHTML;
|
|
|
+ modal.style.display = 'block';
|
|
|
+ }}
|
|
|
+
|
|
|
+ function closeMatchModal(event) {{
|
|
|
+ var modal = document.getElementById('matchModal');
|
|
|
+ if (!event || event.target === modal) {{
|
|
|
+ modal.style.display = 'none';
|
|
|
+ }}
|
|
|
+ }}
|
|
|
+
|
|
|
+ // ESC键关闭模态框
|
|
|
+ document.addEventListener('keydown', function(event) {{
|
|
|
+ if (event.key === 'Escape') {{
|
|
|
+ closeMatchModal();
|
|
|
+ }}
|
|
|
+ }});
|
|
|
+
|
|
|
function showPostDetail(element) {{
|
|
|
const postDataStr = element.dataset.postData;
|
|
|
if (!postDataStr) return;
|