| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- #!/usr/bin/env python3
- """
- Tab1内容生成器 - 选题点(灵感点、目的点、关键点)
- """
- import html as html_module
- from typing import Dict, Any, List
- def render_inspiration_card(item: Dict[str, Any], idx: int) -> str:
- """渲染灵感点卡片"""
- point_text = item.get('灵感点', '')
- description = item.get('描述', '')
- category = item.get('分类', '')
- features = item.get('提取的特征', [])
- scoring = item.get('scoring', {})
- reasoning = item.get('推理', '')
- derivation = item.get('推导说明', '')
-
- card_id = f'inspiration-{idx}'
- has_details = description or features or scoring or reasoning or derivation
-
- html = f'<div class="point-card inspiration-card" data-card-id="{card_id}">\n'
- html += '<div class="point-card-header" onclick="toggleCardDetails(\'' + card_id + '\')">\n'
- html += f'<span class="point-number">#{idx}</span>\n'
- html += f'<span class="point-text">{html_module.escape(point_text)}</span>\n'
- if category:
- html += f'<span class="point-category">{html_module.escape(category)}</span>\n'
- if has_details:
- html += '<span class="toggle-icon">▼</span>\n'
- html += '</div>\n'
-
- if has_details:
- html += f'<div class="point-card-details" id="{card_id}-details">\n'
-
- if description:
- html += '<div class="detail-section">\n'
- html += '<strong>描述:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(description)}</div>\n'
- html += '</div>\n'
-
- if reasoning:
- html += '<div class="detail-section">\n'
- html += '<strong>推理:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(reasoning)}</div>\n'
- html += '</div>\n'
-
- if derivation:
- html += '<div class="detail-section">\n'
- html += '<strong>推导说明:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(derivation)}</div>\n'
- html += '</div>\n'
-
- if features:
- html += '<div class="detail-section">\n'
- html += '<strong>提取的特征:</strong>\n'
- html += '<div class="feature-tags">\n'
- for feature in features:
- feature_name = feature.get('特征名称', '')
- weight = feature.get('权重', 0)
- dimension = feature.get('维度分类', '')
- html += f'<span class="feature-tag">{html_module.escape(feature_name)} '
- if dimension:
- html += f'<em class="feature-dimension">({html_module.escape(dimension)})</em> '
- html += f'<strong class="feature-weight">权重:{weight}</strong></span>\n'
- html += '</div>\n'
- html += '</div>\n'
-
- if scoring:
- html += '<div class="detail-section">\n'
- html += '<strong>评分:</strong>\n'
- html += '<div class="scoring-badges">\n'
- if '人设契合度' in scoring:
- html += f'<span class="scoring-badge">人设契合度: {scoring["人设契合度"]}/10</span>\n'
- if '触发可能性' in scoring:
- html += f'<span class="scoring-badge">触发可能性: {scoring["触发可能性"]}/10</span>\n'
- if '内容解释力' in scoring:
- html += f'<span class="scoring-badge">内容解释力: {scoring["内容解释力"]}/10</span>\n'
- if '总分' in scoring:
- html += f'<span class="scoring-badge total-score">总分: {scoring["总分"]}</span>\n'
- if '评分说明' in scoring:
- html += f'<div class="scoring-note">{html_module.escape(scoring["评分说明"])}</div>\n'
- html += '</div>\n'
- html += '</div>\n'
-
- html += '</div>\n'
-
- html += '</div>\n'
- return html
- def render_purpose_card(item: Dict[str, Any], idx: int) -> str:
- """渲染目的点卡片"""
- point_text = item.get('目的点', '')
- description = item.get('描述', '')
- dimension = item.get('维度', {})
- features = item.get('提取的特征', [])
- reasoning = item.get('推理', '')
-
- card_id = f'purpose-{idx}'
- has_details = description or features or reasoning
-
- html = f'<div class="point-card purpose-card" data-card-id="{card_id}">\n'
- html += '<div class="point-card-header" onclick="toggleCardDetails(\'' + card_id + '\')">\n'
- html += f'<span class="point-number">#{idx}</span>\n'
- html += f'<span class="point-text">{html_module.escape(point_text)}</span>\n'
-
- # 显示维度标签
- if dimension:
- html += '<div class="dimension-tags">\n'
- if '一级分类' in dimension:
- html += f'<span class="dimension-tag level-1">{html_module.escape(dimension["一级分类"])}</span>\n'
- if '二级分类' in dimension:
- html += f'<span class="dimension-tag level-2">{html_module.escape(dimension["二级分类"])}</span>\n'
- html += '</div>\n'
-
- if has_details:
- html += '<span class="toggle-icon">▼</span>\n'
- html += '</div>\n'
-
- if has_details:
- html += f'<div class="point-card-details" id="{card_id}-details">\n'
-
- if description:
- html += '<div class="detail-section">\n'
- html += '<strong>描述:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(description)}</div>\n'
- html += '</div>\n'
-
- if reasoning:
- html += '<div class="detail-section">\n'
- html += '<strong>推理:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(reasoning)}</div>\n'
- html += '</div>\n'
-
- if features:
- html += '<div class="detail-section">\n'
- html += '<strong>提取的特征:</strong>\n'
- html += '<div class="feature-tags">\n'
- for feature in features:
- feature_name = feature.get('特征名称', '')
- weight = feature.get('权重', 0)
- feature_class = feature.get('特征分类', '')
- html += f'<span class="feature-tag">{html_module.escape(feature_name)} '
- if feature_class:
- html += f'<em class="feature-dimension">({html_module.escape(feature_class)})</em> '
- html += f'<strong class="feature-weight">权重:{weight}</strong></span>\n'
- html += '</div>\n'
- html += '</div>\n'
-
- html += '</div>\n'
-
- html += '</div>\n'
- return html
- def render_keypoint_card(item: Dict[str, Any], idx: int, level: int = 0) -> str:
- """渲染关键点卡片(支持嵌套)"""
- point_text = item.get('关键点', '')
- description = item.get('描述', '')
- dimension = item.get('维度', '') or item.get('维度细分', '')
- dimension_category = item.get('维度大类', '')
- features = item.get('提取的特征', [])
- children = item.get('children', [])
- child_reason = item.get('作为子节点的原因', '')
-
- card_id = f'keypoint-{idx}-l{level}'
- has_details = description or features or child_reason
- has_children = len(children) > 0
-
- indent_class = f'keypoint-level-{level}' if level > 0 else ''
-
- html = f'<div class="point-card keypoint-card {indent_class}" data-card-id="{card_id}" data-level="{level}">\n'
- html += '<div class="point-card-header" onclick="toggleCardDetails(\'' + card_id + '\')">\n'
- html += f'<span class="point-number">#{item.get("候选编号", idx)}</span>\n'
- html += f'<span class="point-text">{html_module.escape(point_text)}</span>\n'
-
- # 显示维度大类标签(实质类/形式类)
- if dimension_category:
- category_label = '形式类' if dimension_category == '形式' else '实质类'
- category_class = 'dimension-category-form' if dimension_category == '形式' else 'dimension-category-substance'
- html += f'<span class="dimension-category {category_class}">{html_module.escape(category_label)}</span>\n'
-
- # 显示维度标签(维度细分)
- if dimension:
- html += f'<span class="dimension-tag keypoint-dimension">{html_module.escape(dimension)}</span>\n'
-
- if has_details or has_children:
- html += '<span class="toggle-icon">▼</span>\n'
- html += '</div>\n'
-
- if has_details or has_children:
- html += f'<div class="point-card-details" id="{card_id}-details">\n'
-
- if description:
- html += '<div class="detail-section">\n'
- html += '<strong>描述:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(description)}</div>\n'
- html += '</div>\n'
-
- if child_reason:
- html += '<div class="detail-section">\n'
- html += '<strong>作为子节点的原因:</strong>\n'
- html += f'<div class="detail-text">{html_module.escape(child_reason)}</div>\n'
- html += '</div>\n'
-
- if features:
- html += '<div class="detail-section">\n'
- html += '<strong>提取的特征:</strong>\n'
- html += '<div class="feature-tags">\n'
- for feature in features:
- feature_name = feature.get('特征名称', '')
- weight = feature.get('权重', 0)
- feature_dimension = feature.get('维度', '')
- html += f'<span class="feature-tag">{html_module.escape(feature_name)} '
- if feature_dimension:
- html += f'<em class="feature-dimension">({html_module.escape(feature_dimension)})</em> '
- html += f'<strong class="feature-weight">权重:{weight}</strong></span>\n'
- html += '</div>\n'
- html += '</div>\n'
-
- # 递归渲染子关键点
- if has_children:
- html += '<div class="detail-section keypoint-children">\n'
- html += '<strong>子关键点:</strong>\n'
- html += '<div class="keypoint-children-list">\n'
- for child_idx, child in enumerate(children, 1):
- html += render_keypoint_card(child, child_idx, level + 1)
- html += '</div>\n'
- html += '</div>\n'
-
- html += '</div>\n'
-
- html += '</div>\n'
- return html
- def generate_tab1_content(data: Dict[str, Any]) -> str:
- """生成Tab1内容:选题、灵感点、目的点、关键点"""
- html = '<div class="tab-content" id="tab1">\n'
-
- # 添加CSS样式
- html += '''
- <style>
- .point-card {
- background: white;
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- margin-bottom: 16px;
- transition: all 0.3s ease;
- overflow: hidden;
- }
-
- .point-card:hover {
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
- transform: translateY(-2px);
- }
-
- .point-card-header {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 16px 20px;
- cursor: pointer;
- user-select: none;
- border-bottom: 1px solid #f0f0f0;
- }
-
- .point-card-header:hover {
- background-color: #f8f8f8;
- }
-
- .point-number {
- color: #667eea;
- font-weight: 600;
- font-size: 14px;
- min-width: 40px;
- }
-
- .point-text {
- flex: 1;
- font-size: 16px;
- font-weight: 500;
- color: #333;
- }
-
- .point-category {
- background-color: #e3f2fd;
- color: #1976d2;
- padding: 4px 12px;
- border-radius: 12px;
- font-size: 12px;
- font-weight: 500;
- }
-
- .dimension-tags {
- display: flex;
- gap: 8px;
- }
-
- .dimension-tag {
- background-color: #f3e5f5;
- color: #7b1fa2;
- padding: 4px 12px;
- border-radius: 12px;
- font-size: 12px;
- font-weight: 500;
- }
-
- .dimension-tag.level-1 {
- background-color: #e1bee7;
- color: #6a1b9a;
- }
-
- .dimension-tag.level-2 {
- background-color: #f3e5f5;
- color: #7b1fa2;
- }
-
- .dimension-category {
- padding: 4px 12px;
- border-radius: 12px;
- font-size: 12px;
- font-weight: 600;
- }
-
- .dimension-category-form {
- background-color: #fff3e0;
- color: #e65100;
- }
-
- .dimension-category-substance {
- background-color: #e8f5e9;
- color: #2e7d32;
- }
-
- .toggle-icon {
- color: #999;
- font-size: 12px;
- transition: transform 0.3s ease;
- }
-
- .point-card.expanded .toggle-icon {
- transform: rotate(180deg);
- }
-
- .point-card-details {
- display: none;
- padding: 20px;
- background-color: #fafafa;
- }
-
- .point-card.expanded .point-card-details {
- display: block;
- }
-
- .detail-section {
- margin-bottom: 20px;
- }
-
- .detail-section:last-child {
- margin-bottom: 0;
- }
-
- .detail-section strong {
- display: block;
- color: #667eea;
- font-size: 14px;
- font-weight: 600;
- margin-bottom: 8px;
- }
-
- .detail-text {
- background-color: white;
- padding: 12px 16px;
- border-radius: 6px;
- line-height: 1.6;
- color: #444;
- font-size: 14px;
- border-left: 3px solid #667eea;
- }
-
- .feature-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 8px;
- }
-
- .feature-tag {
- background-color: #e8eaf6;
- color: #3f51b5;
- padding: 6px 12px;
- border-radius: 15px;
- font-size: 13px;
- }
-
- .feature-dimension {
- color: #7986cb;
- font-style: italic;
- }
-
- .feature-weight {
- color: #1a237e;
- margin-left: 4px;
- }
-
- .scoring-badges {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 8px;
- }
-
- .scoring-badge {
- background-color: #e0f2f1;
- color: #00695c;
- padding: 6px 12px;
- border-radius: 15px;
- font-size: 13px;
- font-weight: 500;
- }
-
- .scoring-badge.total-score {
- background-color: #667eea;
- color: white;
- font-weight: 600;
- }
-
- .scoring-note {
- margin-top: 12px;
- padding: 12px;
- background-color: #fff3cd;
- border-left: 3px solid #ffc107;
- border-radius: 4px;
- font-size: 13px;
- color: #856404;
- line-height: 1.6;
- }
-
- /* 关键点嵌套样式 */
- .keypoint-card.keypoint-level-1 {
- margin-left: 24px;
- border-left: 3px solid #4facfe;
- }
-
- .keypoint-card.keypoint-level-2 {
- margin-left: 48px;
- border-left: 3px solid #43e97b;
- }
-
- .keypoint-card.keypoint-level-3 {
- margin-left: 72px;
- border-left: 3px solid #fa709a;
- }
-
- .keypoint-children {
- margin-top: 16px;
- }
-
- .keypoint-children-list {
- margin-top: 12px;
- }
-
- .inspiration-card {
- border-left: 4px solid #667eea;
- }
-
- .purpose-card {
- border-left: 4px solid #f093fb;
- }
-
- .keypoint-card {
- border-left: 4px solid #4facfe;
- }
- </style>
- '''
-
- # 添加JavaScript
- html += '''
- <script>
- function toggleCardDetails(cardId) {
- const card = document.querySelector(`[data-card-id="${cardId}"]`);
- if (card) {
- card.classList.toggle('expanded');
- }
- }
- </script>
- '''
- # 选题描述
- if '选题描述' in data:
- topic = data['选题描述']
- html += '<div class="section">\n'
- html += '<h3>选题描述</h3>\n'
- if '主题' in topic:
- html += f'<div class="topic-theme"><strong>主题:</strong>{html_module.escape(topic["主题"])}</div>\n'
- if '描述' in topic:
- html += f'<div class="topic-desc"><strong>描述:</strong>{html_module.escape(topic["描述"])}</div>\n'
- html += '</div>\n'
- # 灵感点
- if '灵感点' in data:
- inspiration = data['灵感点']
- html += '<div class="section">\n'
- html += '<h3>灵感点</h3>\n'
- html += '<div class="point-cards-list">\n'
- if isinstance(inspiration, list):
- for idx, item in enumerate(inspiration, 1):
- html += render_inspiration_card(item, idx)
- html += '</div>\n'
- html += '</div>\n'
- # 目的点
- if '目的点' in data:
- purpose = data['目的点']
- html += '<div class="section">\n'
- html += '<h3>目的点</h3>\n'
- html += '<div class="point-cards-list">\n'
- if isinstance(purpose, dict) and 'purposes' in purpose:
- purpose_list = purpose['purposes']
- elif isinstance(purpose, list):
- purpose_list = purpose
- else:
- purpose_list = []
- if isinstance(purpose_list, list):
- for idx, item in enumerate(purpose_list, 1):
- html += render_purpose_card(item, idx)
- html += '</div>\n'
- html += '</div>\n'
- # 关键点
- if '关键点' in data:
- keypoint = data['关键点']
- html += '<div class="section">\n'
- html += '<h3>关键点</h3>\n'
- html += '<div class="point-cards-list">\n'
- # 处理关键点数据:可能是列表,也可能是包含key_points的对象
- keypoint_list = keypoint
- if isinstance(keypoint, dict) and 'key_points' in keypoint:
- keypoint_list = keypoint['key_points']
-
- if isinstance(keypoint_list, list):
- for idx, item in enumerate(keypoint_list, 1):
- html += render_keypoint_card(item, idx, 0)
- html += '</div>\n'
- html += '</div>\n'
- html += '</div>\n'
- return html
|