Showing
2 changed files
with
413 additions
and
92 deletions
| @@ -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} |
-
Please register or login to post a comment