马一丁

Modify SWOT analysis

@@ -1177,15 +1177,34 @@ class HTMLRenderer: @@ -1177,15 +1177,34 @@ class HTMLRenderer:
1177 1177
1178 def _render_swot_table(self, block: Dict[str, Any]) -> str: 1178 def _render_swot_table(self, block: Dict[str, Any]) -> str:
1179 """ 1179 """
1180 - 渲染四象限的SWOT专用表格,兼顾HTML与PDF的可读性。 1180 + 渲染四象限的SWOT分析,同时生成两种布局:
  1181 + 1. 卡片布局(用于HTML网页显示)- 圆角矩形四象限
  1182 + 2. 表格布局(用于PDF导出)- 结构化表格,支持分页
1181 1183
1182 PDF分页策略: 1184 PDF分页策略:
1183 - - 每个S/W/O/T象限内部禁止分页(break-inside: avoid)  
1184 - - 允许在象限之间分页  
1185 - - 卡片标题与第一个象限尽量保持在一起 1185 + - 使用表格形式,每个S/W/O/T象限为独立表格区块
  1186 + - 允许在不同象限之间分页
  1187 + - 每个象限内的条目尽量保持在一起
1186 """ 1188 """
1187 title = block.get("title") or "SWOT 分析" 1189 title = block.get("title") or "SWOT 分析"
1188 summary = block.get("summary") 1190 summary = block.get("summary")
  1191 +
  1192 + # ========== 卡片布局(HTML用)==========
  1193 + card_html = self._render_swot_card_layout(block, title, summary)
  1194 +
  1195 + # ========== 表格布局(PDF用)==========
  1196 + table_html = self._render_swot_pdf_table_layout(block, title, summary)
  1197 +
  1198 + # 返回包含两种布局的容器
  1199 + return f"""
  1200 + <div class="swot-container">
  1201 + {card_html}
  1202 + {table_html}
  1203 + </div>
  1204 + """
  1205 +
  1206 + def _render_swot_card_layout(self, block: Dict[str, Any], title: str, summary: str | None) -> str:
  1207 + """渲染SWOT卡片布局(用于HTML网页显示)"""
1189 quadrants = [ 1208 quadrants = [
1190 ("strengths", "优势 Strengths", "S", "strength"), 1209 ("strengths", "优势 Strengths", "S", "strength"),
1191 ("weaknesses", "劣势 Weaknesses", "W", "weakness"), 1210 ("weaknesses", "劣势 Weaknesses", "W", "weakness"),
@@ -1197,7 +1216,6 @@ class HTMLRenderer: @@ -1197,7 +1216,6 @@ class HTMLRenderer:
1197 items = self._normalize_swot_items(block.get(key)) 1216 items = self._normalize_swot_items(block.get(key))
1198 caption_text = f"{len(items)} 条要点" if items else "待补充" 1217 caption_text = f"{len(items)} 条要点" if items else "待补充"
1199 list_html = "".join(self._render_swot_item(item) for item in items) if items else '<li class="swot-empty">尚未填入要点</li>' 1218 list_html = "".join(self._render_swot_item(item) for item in items) if items else '<li class="swot-empty">尚未填入要点</li>'
1200 - # 第一个象限添加特殊类以便与标题保持在一起  
1201 first_cell_class = " swot-cell--first" if idx == 0 else "" 1219 first_cell_class = " swot-cell--first" if idx == 0 else ""
1202 cells_html += f""" 1220 cells_html += f"""
1203 <div class="swot-cell swot-cell--pageable {css}{first_cell_class}" data-swot-key="{key}"> 1221 <div class="swot-cell swot-cell--pageable {css}{first_cell_class}" data-swot-key="{key}">
@@ -1221,7 +1239,7 @@ class HTMLRenderer: @@ -1221,7 +1239,7 @@ class HTMLRenderer:
1221 </div> 1239 </div>
1222 """ 1240 """
1223 return f""" 1241 return f"""
1224 - <div class="swot-card"> 1242 + <div class="swot-card swot-card--html">
1225 <div class="swot-card__head"> 1243 <div class="swot-card__head">
1226 <div>{title_html}{summary_html}</div> 1244 <div>{title_html}{summary_html}</div>
1227 {legend} 1245 {legend}
@@ -1230,6 +1248,121 @@ class HTMLRenderer: @@ -1230,6 +1248,121 @@ class HTMLRenderer:
1230 </div> 1248 </div>
1231 """ 1249 """
1232 1250
  1251 + def _render_swot_pdf_table_layout(self, block: Dict[str, Any], title: str, summary: str | None) -> str:
  1252 + """
  1253 + 渲染SWOT表格布局(用于PDF导出)
  1254 +
  1255 + 设计说明:
  1256 + - 整体为一个大表格,包含标题行和4个象限区域
  1257 + - 每个象限区域有自己的子标题行和内容行
  1258 + - 使用合并单元格来显示象限标题
  1259 + - 通过CSS控制分页行为
  1260 + """
  1261 + quadrants = [
  1262 + ("strengths", "S", "优势 Strengths", "swot-pdf-strength", "#1c7f6e"),
  1263 + ("weaknesses", "W", "劣势 Weaknesses", "swot-pdf-weakness", "#c0392b"),
  1264 + ("opportunities", "O", "机会 Opportunities", "swot-pdf-opportunity", "#1f5ab3"),
  1265 + ("threats", "T", "威胁 Threats", "swot-pdf-threat", "#b36b16"),
  1266 + ]
  1267 +
  1268 + # 标题和摘要
  1269 + summary_row = ""
  1270 + if summary:
  1271 + summary_row = f"""
  1272 + <tr class="swot-pdf-summary-row">
  1273 + <td colspan="4" class="swot-pdf-summary">{self._escape_html(summary)}</td>
  1274 + </tr>"""
  1275 +
  1276 + # 生成四个象限的表格内容
  1277 + quadrant_tables = ""
  1278 + for idx, (key, code, label, css_class, color) in enumerate(quadrants):
  1279 + items = self._normalize_swot_items(block.get(key))
  1280 +
  1281 + # 生成每个象限的内容行
  1282 + items_rows = ""
  1283 + if items:
  1284 + for item_idx, item in enumerate(items):
  1285 + item_title = item.get("title") or item.get("label") or item.get("text") or "未命名要点"
  1286 + item_detail = item.get("detail") or item.get("description") or ""
  1287 + item_evidence = item.get("evidence") or item.get("source") or ""
  1288 + item_impact = item.get("impact") or item.get("priority") or ""
  1289 + item_score = item.get("score")
  1290 +
  1291 + # 构建详情内容
  1292 + detail_parts = []
  1293 + if item_detail:
  1294 + detail_parts.append(item_detail)
  1295 + if item_evidence:
  1296 + detail_parts.append(f"佐证:{item_evidence}")
  1297 + detail_text = "<br/>".join(detail_parts) if detail_parts else "-"
  1298 +
  1299 + # 构建标签
  1300 + tags = []
  1301 + if item_impact:
  1302 + tags.append(f'<span class="swot-pdf-tag">{self._escape_html(item_impact)}</span>')
  1303 + if item_score not in (None, ""):
  1304 + tags.append(f'<span class="swot-pdf-tag swot-pdf-tag--score">评分 {self._escape_html(item_score)}</span>')
  1305 + tags_html = " ".join(tags)
  1306 +
  1307 + # 第一行需要合并象限标题单元格
  1308 + if item_idx == 0:
  1309 + rowspan = len(items)
  1310 + items_rows += f"""
  1311 + <tr class="swot-pdf-item-row {css_class}">
  1312 + <td rowspan="{rowspan}" class="swot-pdf-quadrant-label {css_class}">
  1313 + <span class="swot-pdf-code">{code}</span>
  1314 + <span class="swot-pdf-label-text">{self._escape_html(label.split()[0])}</span>
  1315 + </td>
  1316 + <td class="swot-pdf-item-num">{item_idx + 1}</td>
  1317 + <td class="swot-pdf-item-title">{self._escape_html(item_title)}</td>
  1318 + <td class="swot-pdf-item-detail">{detail_text}</td>
  1319 + <td class="swot-pdf-item-tags">{tags_html}</td>
  1320 + </tr>"""
  1321 + else:
  1322 + items_rows += f"""
  1323 + <tr class="swot-pdf-item-row {css_class}">
  1324 + <td class="swot-pdf-item-num">{item_idx + 1}</td>
  1325 + <td class="swot-pdf-item-title">{self._escape_html(item_title)}</td>
  1326 + <td class="swot-pdf-item-detail">{detail_text}</td>
  1327 + <td class="swot-pdf-item-tags">{tags_html}</td>
  1328 + </tr>"""
  1329 + else:
  1330 + # 没有内容时显示占位
  1331 + items_rows = f"""
  1332 + <tr class="swot-pdf-item-row {css_class}">
  1333 + <td class="swot-pdf-quadrant-label {css_class}">
  1334 + <span class="swot-pdf-code">{code}</span>
  1335 + <span class="swot-pdf-label-text">{self._escape_html(label.split()[0])}</span>
  1336 + </td>
  1337 + <td class="swot-pdf-item-num">-</td>
  1338 + <td colspan="3" class="swot-pdf-empty">暂无要点</td>
  1339 + </tr>"""
  1340 +
  1341 + # 每个象限作为一个独立的tbody,便于分页控制
  1342 + quadrant_tables += f"""
  1343 + <tbody class="swot-pdf-quadrant {css_class}">
  1344 + {items_rows}
  1345 + </tbody>"""
  1346 +
  1347 + return f"""
  1348 + <div class="swot-pdf-wrapper">
  1349 + <table class="swot-pdf-table">
  1350 + <caption class="swot-pdf-caption">{self._escape_html(title)}</caption>
  1351 + <thead class="swot-pdf-thead">
  1352 + <tr>
  1353 + <th class="swot-pdf-th-quadrant">象限</th>
  1354 + <th class="swot-pdf-th-num">序号</th>
  1355 + <th class="swot-pdf-th-title">要点</th>
  1356 + <th class="swot-pdf-th-detail">详细说明</th>
  1357 + <th class="swot-pdf-th-tags">影响/评分</th>
  1358 + </tr>
  1359 + {summary_row}
  1360 + </thead>
  1361 + {quadrant_tables}
  1362 + </table>
  1363 + </div>
  1364 + """
  1365 +
1233 def _normalize_swot_items(self, raw: Any) -> List[Dict[str, Any]]: 1366 def _normalize_swot_items(self, raw: Any) -> List[Dict[str, Any]]:
1234 """将SWOT条目规整为统一结构,兼容字符串/对象两种写法""" 1367 """将SWOT条目规整为统一结构,兼容字符串/对象两种写法"""
1235 normalized: List[Dict[str, Any]] = [] 1368 normalized: List[Dict[str, Any]] = []
@@ -3215,26 +3348,121 @@ table th {{ @@ -3215,26 +3348,121 @@ table th {{
3215 color: var(--swot-muted); 3348 color: var(--swot-muted);
3216 opacity: 0.7; 3349 opacity: 0.7;
3217 }} 3350 }}
3218 -/* PDF/导出时的SWOT专用布局,支持分页且避免圆角框重叠 */  
3219 -body.exporting .swot-legend {{  
3220 - display: none !important;  
3221 -}}  
3222 -body.exporting .swot-grid {{  
3223 - display: flex;  
3224 - flex-direction: column;  
3225 - gap: 16px; 3351 +
  3352 +/* ========== SWOT PDF表格布局样式(默认隐藏)========== */
  3353 +.swot-pdf-wrapper {{
  3354 + display: none;
3226 }} 3355 }}
3227 -body.exporting .swot-cell {{ 3356 +
  3357 +/* SWOT PDF表格样式定义(用于PDF渲染时显示) */
  3358 +.swot-pdf-table {{
3228 width: 100%; 3359 width: 100%;
3229 - height: auto;  
3230 - page-break-inside: avoid; 3360 + border-collapse: collapse;
  3361 + margin: 20px 0;
  3362 + font-size: 13px;
  3363 + table-layout: fixed;
  3364 +}}
  3365 +.swot-pdf-caption {{
  3366 + caption-side: top;
  3367 + text-align: left;
  3368 + font-size: 1.15rem;
  3369 + font-weight: 700;
  3370 + padding: 12px 0;
  3371 + color: var(--text-color);
  3372 +}}
  3373 +.swot-pdf-thead th {{
  3374 + background: #f8f9fa;
  3375 + padding: 10px 8px;
  3376 + text-align: left;
  3377 + font-weight: 600;
  3378 + border: 1px solid #dee2e6;
  3379 + color: #495057;
  3380 +}}
  3381 +.swot-pdf-th-quadrant {{ width: 80px; }}
  3382 +.swot-pdf-th-num {{ width: 50px; text-align: center; }}
  3383 +.swot-pdf-th-title {{ width: 22%; }}
  3384 +.swot-pdf-th-detail {{ width: auto; }}
  3385 +.swot-pdf-th-tags {{ width: 100px; text-align: center; }}
  3386 +.swot-pdf-summary {{
  3387 + padding: 12px;
  3388 + background: #f8f9fa;
  3389 + color: #666;
  3390 + font-style: italic;
  3391 + border: 1px solid #dee2e6;
  3392 +}}
  3393 +.swot-pdf-quadrant {{
3231 break-inside: avoid; 3394 break-inside: avoid;
  3395 + page-break-inside: avoid;
3232 }} 3396 }}
3233 -body.exporting .swot-cell--first {{  
3234 - page-break-before: avoid;  
3235 - break-before: avoid; 3397 +.swot-pdf-quadrant-label {{
  3398 + text-align: center;
  3399 + vertical-align: middle;
  3400 + padding: 12px 8px;
  3401 + font-weight: 700;
  3402 + border: 1px solid #dee2e6;
  3403 + writing-mode: horizontal-tb;
  3404 +}}
  3405 +.swot-pdf-quadrant-label.swot-pdf-strength {{ background: rgba(28,127,110,0.15); color: #1c7f6e; border-left: 4px solid #1c7f6e; }}
  3406 +.swot-pdf-quadrant-label.swot-pdf-weakness {{ background: rgba(192,57,43,0.12); color: #c0392b; border-left: 4px solid #c0392b; }}
  3407 +.swot-pdf-quadrant-label.swot-pdf-opportunity {{ background: rgba(31,90,179,0.12); color: #1f5ab3; border-left: 4px solid #1f5ab3; }}
  3408 +.swot-pdf-quadrant-label.swot-pdf-threat {{ background: rgba(179,107,22,0.12); color: #b36b16; border-left: 4px solid #b36b16; }}
  3409 +.swot-pdf-code {{
  3410 + display: block;
  3411 + font-size: 1.5rem;
  3412 + font-weight: 800;
  3413 + margin-bottom: 4px;
  3414 +}}
  3415 +.swot-pdf-label-text {{
  3416 + display: block;
  3417 + font-size: 0.75rem;
  3418 + font-weight: 600;
  3419 + letter-spacing: 0.02em;
  3420 +}}
  3421 +.swot-pdf-item-row td {{
  3422 + padding: 10px 8px;
  3423 + border: 1px solid #dee2e6;
  3424 + vertical-align: top;
  3425 +}}
  3426 +.swot-pdf-item-row.swot-pdf-strength td {{ background: rgba(28,127,110,0.03); }}
  3427 +.swot-pdf-item-row.swot-pdf-weakness td {{ background: rgba(192,57,43,0.03); }}
  3428 +.swot-pdf-item-row.swot-pdf-opportunity td {{ background: rgba(31,90,179,0.03); }}
  3429 +.swot-pdf-item-row.swot-pdf-threat td {{ background: rgba(179,107,22,0.03); }}
  3430 +.swot-pdf-item-num {{
  3431 + text-align: center;
  3432 + font-weight: 600;
  3433 + color: #6c757d;
  3434 +}}
  3435 +.swot-pdf-item-title {{
  3436 + font-weight: 600;
  3437 + color: #212529;
  3438 +}}
  3439 +.swot-pdf-item-detail {{
  3440 + color: #495057;
  3441 + line-height: 1.5;
  3442 +}}
  3443 +.swot-pdf-item-tags {{
  3444 + text-align: center;
  3445 +}}
  3446 +.swot-pdf-tag {{
  3447 + display: inline-block;
  3448 + padding: 3px 8px;
  3449 + border-radius: 4px;
  3450 + font-size: 0.75rem;
  3451 + background: #e9ecef;
  3452 + color: #495057;
  3453 + margin: 2px;
3236 }} 3454 }}
3237 -/* 打印模式下的SWOT分页控制 */ 3455 +.swot-pdf-tag--score {{
  3456 + background: #fff3cd;
  3457 + color: #856404;
  3458 +}}
  3459 +.swot-pdf-empty {{
  3460 + text-align: center;
  3461 + color: #adb5bd;
  3462 + font-style: italic;
  3463 +}}
  3464 +
  3465 +/* 打印模式下的SWOT分页控制(保留卡片布局的打印支持) */
3238 @media print {{ 3466 @media print {{
3239 .swot-card {{ 3467 .swot-card {{
3240 break-inside: auto; 3468 break-inside: auto;
@@ -3244,29 +3472,7 @@ body.exporting .swot-cell--first {{ @@ -3244,29 +3472,7 @@ body.exporting .swot-cell--first {{
3244 break-after: avoid; 3472 break-after: avoid;
3245 page-break-after: avoid; 3473 page-break-after: avoid;
3246 }} 3474 }}
3247 - .swot-grid {{  
3248 - display: flex;  
3249 - flex-direction: column;  
3250 - gap: 16px;  
3251 - }}  
3252 - .swot-cell {{  
3253 - break-inside: avoid;  
3254 - page-break-inside: avoid;  
3255 - width: 100%;  
3256 - }}  
3257 - .swot-cell--first {{  
3258 - break-before: avoid;  
3259 - page-break-before: avoid;  
3260 - }}  
3261 - .swot-cell__meta {{  
3262 - break-after: avoid;  
3263 - page-break-after: avoid;  
3264 - }}  
3265 - .swot-list {{  
3266 - break-inside: avoid;  
3267 - page-break-inside: avoid;  
3268 - }}  
3269 - .swot-item {{ 3475 + .swot-pdf-quadrant {{
3270 break-inside: avoid; 3476 break-inside: avoid;
3271 page-break-inside: avoid; 3477 page-break-inside: avoid;
3272 }} 3478 }}
@@ -1049,78 +1049,193 @@ body {{ @@ -1049,78 +1049,193 @@ body {{
1049 min-height: 400px; 1049 min-height: 400px;
1050 }} 1050 }}
1051 1051
1052 -/* ========== SWOT PDF分页优化 ========== */  
1053 -/* 核心策略:S/W/O/T四个象限各自内部禁止分页,但允许象限之间分页 */ 1052 +/* ========== SWOT PDF表格布局 ========== */
  1053 +/* 核心策略:PDF中使用表格形式而非卡片形式,更适合分页 */
1054 1054
1055 -/* 隐藏四象限标注图例 */  
1056 -.swot-legend {{ 1055 +/* 隐藏HTML卡片布局,显示PDF表格布局 */
  1056 +.swot-card--html {{
1057 display: none !important; 1057 display: none !important;
1058 }} 1058 }}
1059 1059
1060 -/* SWOT卡片容器:允许内部分页 */  
1061 -.swot-card {{  
1062 - break-inside: auto !important;  
1063 - page-break-inside: auto !important;  
1064 - margin: 20px 0; 1060 +.swot-pdf-wrapper {{
  1061 + display: block !important;
  1062 + margin: 24px 0;
  1063 +}}
  1064 +
  1065 +/* PDF表格整体样式 */
  1066 +.swot-pdf-table {{
  1067 + width: 100% !important;
  1068 + border-collapse: collapse !important;
  1069 + font-size: 11px !important;
  1070 + table-layout: fixed !important;
  1071 + background: white;
1065 }} 1072 }}
1066 1073
1067 -/* 卡片头部(标题+摘要):避免紧跟其后分页,尽量与第一个象限保持在一起 */  
1068 -.swot-card__head {{ 1074 +/* 表格标题 */
  1075 +.swot-pdf-caption {{
  1076 + caption-side: top !important;
  1077 + text-align: left !important;
  1078 + font-size: 16px !important;
  1079 + font-weight: 700 !important;
  1080 + padding: 12px 0 !important;
  1081 + color: #1a1a1a !important;
  1082 + border-bottom: 2px solid #333 !important;
  1083 + margin-bottom: 8px !important;
  1084 +}}
  1085 +
  1086 +/* 表头样式 */
  1087 +.swot-pdf-thead {{
1069 break-after: avoid !important; 1088 break-after: avoid !important;
1070 page-break-after: avoid !important; 1089 page-break-after: avoid !important;
1071 - break-inside: avoid !important;  
1072 - page-break-inside: avoid !important;  
1073 }} 1090 }}
1074 1091
1075 -/* 网格容器:PDF模式下使用纵向flex布局,允许子元素间分页 */  
1076 -.swot-grid {{  
1077 - display: flex !important;  
1078 - flex-direction: column !important;  
1079 - gap: 16px !important;  
1080 - break-inside: auto !important;  
1081 - page-break-inside: auto !important; 1092 +.swot-pdf-thead th {{
  1093 + background: #f0f0f0 !important;
  1094 + padding: 10px 8px !important;
  1095 + text-align: left !important;
  1096 + font-weight: 600 !important;
  1097 + border: 1px solid #ccc !important;
  1098 + color: #333 !important;
  1099 + font-size: 11px !important;
  1100 +}}
  1101 +
  1102 +.swot-pdf-th-quadrant {{ width: 70px !important; }}
  1103 +.swot-pdf-th-num {{ width: 40px !important; text-align: center !important; }}
  1104 +.swot-pdf-th-title {{ width: 20% !important; }}
  1105 +.swot-pdf-th-detail {{ width: auto !important; }}
  1106 +.swot-pdf-th-tags {{ width: 80px !important; text-align: center !important; }}
  1107 +
  1108 +/* 摘要行 */
  1109 +.swot-pdf-summary {{
  1110 + padding: 10px 12px !important;
  1111 + background: #f8f8f8 !important;
  1112 + color: #555 !important;
  1113 + font-style: italic !important;
  1114 + border: 1px solid #ccc !important;
  1115 + font-size: 11px !important;
1082 }} 1116 }}
1083 1117
1084 -/* 每个SWOT象限单元格:禁止内部分页,允许前后分页 */  
1085 -.swot-cell {{ 1118 +/* 每个象限区块 - 核心分页控制 */
  1119 +.swot-pdf-quadrant {{
1086 break-inside: avoid !important; 1120 break-inside: avoid !important;
1087 page-break-inside: avoid !important; 1121 page-break-inside: avoid !important;
  1122 +}}
  1123 +
  1124 +/* 允许在不同象限之间分页 */
  1125 +.swot-pdf-quadrant + .swot-pdf-quadrant {{
1088 break-before: auto; 1126 break-before: auto;
1089 page-break-before: auto; 1127 page-break-before: auto;
1090 - break-after: auto;  
1091 - page-break-after: auto;  
1092 - width: 100% !important;  
1093 - max-width: 100% !important;  
1094 - flex: none !important;  
1095 - min-height: auto !important;  
1096 - height: auto !important;  
1097 - box-sizing: border-box;  
1098 }} 1128 }}
1099 1129
1100 -/* 第一个象限:避免在标题后立即分页 */  
1101 -.swot-cell--first {{  
1102 - break-before: avoid !important;  
1103 - page-break-before: avoid !important; 1130 +/* 象限标签单元格 */
  1131 +.swot-pdf-quadrant-label {{
  1132 + text-align: center !important;
  1133 + vertical-align: middle !important;
  1134 + padding: 12px 6px !important;
  1135 + font-weight: 700 !important;
  1136 + border: 1px solid #ccc !important;
  1137 + width: 70px !important;
1104 }} 1138 }}
1105 1139
1106 -/* 象限内的meta区域(图标+标题):避免被分页切开 */  
1107 -.swot-cell__meta {{  
1108 - break-inside: avoid !important;  
1109 - page-break-inside: avoid !important;  
1110 - break-after: avoid !important;  
1111 - page-break-after: avoid !important; 1140 +/* 四个象限的颜色主题 */
  1141 +.swot-pdf-quadrant-label.swot-pdf-strength {{
  1142 + background: #e8f5f2 !important;
  1143 + color: #1c7f6e !important;
  1144 + border-left: 4px solid #1c7f6e !important;
  1145 +}}
  1146 +.swot-pdf-quadrant-label.swot-pdf-weakness {{
  1147 + background: #fdeaea !important;
  1148 + color: #c0392b !important;
  1149 + border-left: 4px solid #c0392b !important;
  1150 +}}
  1151 +.swot-pdf-quadrant-label.swot-pdf-opportunity {{
  1152 + background: #e8f0fa !important;
  1153 + color: #1f5ab3 !important;
  1154 + border-left: 4px solid #1f5ab3 !important;
  1155 +}}
  1156 +.swot-pdf-quadrant-label.swot-pdf-threat {{
  1157 + background: #fdf3e6 !important;
  1158 + color: #b36b16 !important;
  1159 + border-left: 4px solid #b36b16 !important;
1112 }} 1160 }}
1113 1161
1114 -/* 条目列表:允许列表整体分页 */  
1115 -.swot-list {{  
1116 - break-inside: avoid !important;  
1117 - page-break-inside: avoid !important; 1162 +/* 象限代码字母 */
  1163 +.swot-pdf-code {{
  1164 + display: block !important;
  1165 + font-size: 20px !important;
  1166 + font-weight: 800 !important;
  1167 + margin-bottom: 2px !important;
1118 }} 1168 }}
1119 1169
1120 -/* 单个条目:避免被分页切开 */  
1121 -.swot-item {{  
1122 - break-inside: avoid !important;  
1123 - page-break-inside: avoid !important; 1170 +/* 象限标签文字 */
  1171 +.swot-pdf-label-text {{
  1172 + display: block !important;
  1173 + font-size: 9px !important;
  1174 + font-weight: 600 !important;
  1175 + letter-spacing: 0.02em !important;
  1176 +}}
  1177 +
  1178 +/* 数据行 */
  1179 +.swot-pdf-item-row td {{
  1180 + padding: 8px 6px !important;
  1181 + border: 1px solid #ddd !important;
  1182 + vertical-align: top !important;
  1183 + font-size: 11px !important;
  1184 + line-height: 1.4 !important;
  1185 +}}
  1186 +
  1187 +/* 行背景色 */
  1188 +.swot-pdf-item-row.swot-pdf-strength td {{ background: #f7fbfa !important; }}
  1189 +.swot-pdf-item-row.swot-pdf-weakness td {{ background: #fef9f9 !important; }}
  1190 +.swot-pdf-item-row.swot-pdf-opportunity td {{ background: #f7f9fc !important; }}
  1191 +.swot-pdf-item-row.swot-pdf-threat td {{ background: #fdfbf7 !important; }}
  1192 +
  1193 +/* 序号单元格 */
  1194 +.swot-pdf-item-num {{
  1195 + text-align: center !important;
  1196 + font-weight: 600 !important;
  1197 + color: #888 !important;
  1198 + width: 40px !important;
  1199 +}}
  1200 +
  1201 +/* 要点标题 */
  1202 +.swot-pdf-item-title {{
  1203 + font-weight: 600 !important;
  1204 + color: #222 !important;
  1205 +}}
  1206 +
  1207 +/* 详情说明 */
  1208 +.swot-pdf-item-detail {{
  1209 + color: #444 !important;
  1210 + line-height: 1.5 !important;
  1211 +}}
  1212 +
  1213 +/* 标签单元格 */
  1214 +.swot-pdf-item-tags {{
  1215 + text-align: center !important;
  1216 +}}
  1217 +
  1218 +/* 标签样式 */
  1219 +.swot-pdf-tag {{
  1220 + display: inline-block !important;
  1221 + padding: 2px 6px !important;
  1222 + border-radius: 3px !important;
  1223 + font-size: 9px !important;
  1224 + background: #e9ecef !important;
  1225 + color: #495057 !important;
  1226 + margin: 1px !important;
  1227 +}}
  1228 +
  1229 +.swot-pdf-tag--score {{
  1230 + background: #fff3cd !important;
  1231 + color: #856404 !important;
  1232 +}}
  1233 +
  1234 +/* 空数据提示 */
  1235 +.swot-pdf-empty {{
  1236 + text-align: center !important;
  1237 + color: #999 !important;
  1238 + font-style: italic !important;
1124 }} 1239 }}
1125 1240
1126 {optimized_css} 1241 {optimized_css}