diff --git a/lawrisk/api/v2.py b/lawrisk/api/v2.py index f30eaab..383c8d5 100644 --- a/lawrisk/api/v2.py +++ b/lawrisk/api/v2.py @@ -56,6 +56,7 @@ from lawrisk.services.auth_service import ( ) from lawrisk.services.template_service import ( get_permit_template_path, + get_permit_sample_path, get_permit_template_metadata, overwrite_permit_template, ) @@ -1078,6 +1079,45 @@ def admin_permit_import_template(): return jsonify({"success": False, "message": "模板文件暂时无法下载"}), 500 +@v2_bp.route('/admin/permit-import/sample', methods=['GET']) +def admin_permit_import_sample(): + """Provide the Excel import sample file for download.""" + sample_path = get_permit_sample_path() + + if not os.path.exists(sample_path): + return jsonify({"success": False, "message": "样表文件不存在,请联系管理员"}), 404 + + try: + return send_file( + sample_path, + as_attachment=True, + download_name='风险提示表(仅销售预包装食品备案,市场监管部门)(样表).xlsx', + ) + except Exception as exc: + print(f"admin_permit_import_sample error: {exc}") + return jsonify({"success": False, "message": "样表文件暂时无法下载"}), 500 + + +@v2_bp.route('/admin/images/', methods=['GET']) +def admin_image(filename): + """Serve admin UI image files.""" + # 安全检查:只允许特定的文件名,防止路径遍历攻击 + allowed_files = {'empty_table.png', 'sample_table.png'} + if filename not in allowed_files: + return jsonify({"success": False, "message": "文件不存在"}), 404 + + image_path = os.path.join(_project_root(), 'static', 'images', filename) + + if not os.path.exists(image_path): + return jsonify({"success": False, "message": f"图片文件 {filename} 不存在"}), 404 + + try: + return send_file(image_path, mimetype='image/png') + except Exception as exc: + print(f"admin_image error for {filename}: {exc}") + return jsonify({"success": False, "message": "图片文件暂时无法加载"}), 500 + + def _build_import_preview_response(session_token: str): """Internal helper to build preview response JSON.""" try: diff --git a/lawrisk/services/template_service.py b/lawrisk/services/template_service.py index 8deef8f..a097668 100644 --- a/lawrisk/services/template_service.py +++ b/lawrisk/services/template_service.py @@ -10,6 +10,7 @@ from typing import Any, Dict PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "data", "template") PERMIT_TEMPLATE_FILENAME = "风险提示表 模板.xlsx" +PERMIT_SAMPLE_FILENAME = "风险提示表(仅销售预包装食品备案,市场监管部门)(样表).xlsx" TEMPLATE_META_FILENAME = "template_meta.json" @@ -18,6 +19,11 @@ def get_permit_template_path() -> str: return os.path.join(TEMPLATE_DIR, PERMIT_TEMPLATE_FILENAME) +def get_permit_sample_path() -> str: + """Return the absolute path to the permit import sample file.""" + return os.path.join(TEMPLATE_DIR, PERMIT_SAMPLE_FILENAME) + + def _meta_path() -> str: return os.path.join(TEMPLATE_DIR, TEMPLATE_META_FILENAME) diff --git a/static/db_admin.html b/static/db_admin.html index 048307d..5467b78 100644 --- a/static/db_admin.html +++ b/static/db_admin.html @@ -2770,6 +2770,63 @@ 📥 许可导入 + + +
+
+
+ 💡 + 导入模板与样表示例 +
+
+ + 📥 + 下载导入模板 + + + 📥 + 下载样表 + +
+
+
+
+
+ 1 + 空表 (空模板) +
+
+ 空表 +
+
+
+
+ 2 + 样表示例 (含填报说明) +
+
+ 样表示例 +
+
+
+
+
📄
@@ -4495,13 +4552,6 @@ html += '

📄 上传 Excel

'; html += '
'; html += ''; - html += ''; if (state.sessionId) { const sheetCount = state.sheetSummaries ? state.sheetSummaries.length : 0; html += `
当前会话:${escapeHtml(state.filename || '(未命名)')} | Sheet ${sheetCount} 个 | 风险 ${state.totalRows || 0} 条
`; @@ -4578,36 +4628,6 @@ html += '
正在准备预览数据...
'; } - // 添加样表示例 - html += '
'; - html += '
💡 导入模板与样表示例
'; - html += getSampleExcelHtml(); - html += '
'; - - function getSampleExcelHtml() { - return ` -
-
-
- 1 - 空表 (空模板) -
-
- 空表 -
-
-
-
- 2 - 样表示例 (含填报说明) -
-
- 样表示例 -
-
-
`; - } - const nextDisabled = !state.sessionId || state.selectedSheets.size === 0 || state.uploading || state.previewLoading; html += '
'; html += '
提示:系统将根据配置规则自动匹配主题,进入下一步预览结果。
'; @@ -7659,6 +7679,7 @@ 许可(备案)事项名称 实施层级 + 提示条款数量 @@ -7676,10 +7697,19 @@ ? "background: #e0f2fe; color: #0369a1;" // Blue for Municipal : "background: #fee2e2; color: #b91c1c;"; // Red for District + // Add risk count display logic + const riskCount = typeof p.risk_count === 'number' ? p.risk_count : 0; + const riskBadgeStyle = riskCount > 0 + ? "background: #fef3c7; color: #92400e;" // Amber - has risks + : "background: #f3f4f6; color: #6b7280;"; // Gray - no risks + html += ` ${escapeHtml(p.permit_name)} ${implLevel} + + ${riskCount} + `; });