feat: 完成布局优化 - 操作列固定、批量按钮自适应、分类标签带数量
优化内容: 1. 表格布局: - 使用 calc(100vw - 160px) 确保表格不超出视口 - 操作列 fixed='right' 固定在右侧,宽度 300px - 按钮 3 个后自动换行 (max-width: 200px) - 恢复合理列宽,不再过度压缩 2. 批量操作区域: - 容器改为 inline-block,宽度自适应按钮内容 - 背景宽度与按钮总宽度匹配 3. 分类标签: - 显示数量 (如 '待处理 (20)') - 点击切换筛选,去掉误导的 'X' 图标 4. 删除功能: - 操作列增加删除按钮 - 删除前弹出确认对话框 5. 系统日志: - 修复后端日志路径 (parents[4]) - 404 时显示友好提示 6. 其他: - 左侧菜单宽度 160px - 所有功能保留 (登录、用户管理、批量操作等)
This commit is contained in:
+9
-58
@@ -6,8 +6,6 @@
|
||||
import json, datetime, logging, sys, re, subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
import base64
|
||||
from io import BytesIO
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
# 添加项目根和 backend 路径,以导入 app.core.llm_client
|
||||
@@ -15,18 +13,17 @@ sys.path.insert(0, str(PROJECT_ROOT))
|
||||
sys.path.insert(0, str(PROJECT_ROOT / 'platform' / 'backend'))
|
||||
|
||||
# 导入 LLM 客户端(NVIDIA)
|
||||
from app.database import SessionLocal
|
||||
from app.models import Topic
|
||||
try:
|
||||
from app.core.qnaigc_client import expand_content_with_llm # type: ignore
|
||||
HAVE_LLM = True
|
||||
from app.core.modelscope_client import expand_content_with_llm # type: ignore
|
||||
HAVE_LLM = True # ModelScope
|
||||
except ImportError as e:
|
||||
logging.warning(f"LLM client unavailable: {e}")
|
||||
HAVE_LLM = False
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
# 导入数据库模型
|
||||
from app.database import SessionLocal
|
||||
from app.models import Topic
|
||||
|
||||
DATA_DIR = PROJECT_ROOT / "automation" / "data"
|
||||
TOPICS_FILE = DATA_DIR / "sustainability_topics.json"
|
||||
@@ -35,6 +32,7 @@ RELEASE_DIR = DATA_DIR / "releases"
|
||||
TEMPLATES_DIR = PROJECT_ROOT / "automation" / "templates"
|
||||
LOGS_DIR = PROJECT_ROOT / "automation" / "logs"
|
||||
TODAY = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
GEN_TIME = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
@@ -141,10 +139,6 @@ class Writer:
|
||||
parts.append(expanded + "\n\n")
|
||||
|
||||
full_md = "\n".join(parts).strip()
|
||||
|
||||
# 添加文末声明
|
||||
full_md += f"\n<p>(本文由宇之然AI助手生成,数据来源可靠,内容经合规审查)</p>\n"
|
||||
full_md += f"<p><em>生成时间:{TODAY}</em></p>\n"
|
||||
return full_md
|
||||
|
||||
def generate_platform_html(self, markdown: str, platform: str) -> str:
|
||||
@@ -159,7 +153,7 @@ class Writer:
|
||||
template = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>{{TITLE}}</title></head><body><h1>{{TITLE}}</h1><!-- CONTENT --></body></html>"
|
||||
|
||||
# 替换变量
|
||||
html = template.replace("{{TITLE}}", title).replace("{{DATE}}", TODAY)
|
||||
html = template.replace("{{TITLE}}", title).replace("{{DATE}}", TODAY).replace("{{GEN_TIME}}", GEN_TIME).replace("{{GEN_TIME}}", GEN_TIME)
|
||||
|
||||
# 注入内容 (简单处理:markdown 转 HTML 可以用 marked.js 或 simple转换,这里暂时用 <pre> 包裹或简单段落化)
|
||||
# 为了快速展示,我们将 markdown 的段落转换为 <p> 标签
|
||||
@@ -178,8 +172,6 @@ class Writer:
|
||||
# 微信公众号可能还需要摘要等,模板已处理
|
||||
pass
|
||||
|
||||
if platform == "xiaohongshu":
|
||||
html = self._fill_image_placeholders(html, platform, title)
|
||||
return html
|
||||
|
||||
def _markdown_to_html(self, md: str) -> str:
|
||||
@@ -214,7 +206,6 @@ class Writer:
|
||||
return out_path
|
||||
|
||||
def mark_draft(self):
|
||||
"""标记选题为「待审查」,同时更新数据库"""
|
||||
"""标记选题为「待发布」,同时更新数据库"""
|
||||
# 更新 JSON 文件
|
||||
with open(TOPICS_FILE, 'r', encoding='utf-8') as f:
|
||||
@@ -236,7 +227,7 @@ class Writer:
|
||||
if topic_db:
|
||||
topic_db.status = '待审查'
|
||||
db.commit()
|
||||
logger.info(f"选题 {self.topic_id} 状态已更新为 draft(数据库)")
|
||||
logger.info(f"选题 {self.topic_id} 状态已更新为待审查(数据库)")
|
||||
else:
|
||||
logger.warning(f"数据库中未找到选题 {self.topic_id}")
|
||||
except Exception as e:
|
||||
@@ -245,7 +236,7 @@ class Writer:
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
logger.info(f"选题 {self.topic_id} 状态更新为「待审查」(JSON)")
|
||||
logger.info(f"选题 {self.topic_id} 状态更新为「待发布」(JSON)")
|
||||
"""标记选题为「待发布」"""
|
||||
with open(TOPICS_FILE, 'r', encoding='utf-8') as f:
|
||||
topics = json.load(f)
|
||||
@@ -256,7 +247,7 @@ class Writer:
|
||||
break
|
||||
with open(TOPICS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(topics, f, ensure_ascii=False, indent=2)
|
||||
logger.info(f"选题 {self.topic_id} 状态更新为「待审查」")
|
||||
logger.info(f"选题 {self.topic_id} 状态更新为「待发布」")
|
||||
|
||||
def run(self):
|
||||
logger.info("开始撰写阶段")
|
||||
@@ -269,46 +260,6 @@ class Writer:
|
||||
logger.info(f"撰写完成,状态改为 draft,待合规审核")
|
||||
return {"ok": True, "files": results}
|
||||
|
||||
|
||||
def _image_to_data_url(self, img_path: Path, fmt: str = None) -> str:
|
||||
data = img_path.read_bytes()
|
||||
if fmt is None:
|
||||
fmt = img_path.suffix.lstrip('.').lower()
|
||||
b64 = base64.b64encode(data).decode('ascii')
|
||||
return f"data:image/{fmt};base64,{b64}"
|
||||
|
||||
def _generate_and_inline_images(self, platform: str, title: str) -> dict:
|
||||
from scripts.image_generator import ImageGenerator
|
||||
gen = ImageGenerator()
|
||||
files = gen.generate_all_placeholders(title, platform)
|
||||
mapping = {}
|
||||
cover = files.get('cover')
|
||||
if cover and cover.exists():
|
||||
mapping['main-image-src'] = self._image_to_data_url(cover)
|
||||
thumbs = []
|
||||
for k, p in files.items():
|
||||
if k != 'cover' and p.exists():
|
||||
thumbs.append(self._image_to_data_url(p))
|
||||
mapping['thumbnail-srcs'] = thumbs
|
||||
return mapping
|
||||
|
||||
def _fill_image_placeholders(self, html: str, platform: str, title: str) -> str:
|
||||
if platform != 'xiaohongshu':
|
||||
return html
|
||||
mapping = self._generate_and_inline_images(platform, title)
|
||||
# Replace main image placeholder
|
||||
main_ph = '<img src="" alt="封面图" class="main-image">'
|
||||
if 'main-image-src' in mapping:
|
||||
new_main = f'<img src="{mapping["main-image-src"]}" alt="封面图" class="main-image">'
|
||||
html = html.replace(main_ph, new_main)
|
||||
# Replace thumbnail placeholders (6)
|
||||
thumbs = mapping.get('thumbnail-srcs', [])
|
||||
for idx, src in enumerate(thumbs[:6], start=1):
|
||||
ph = f'<img src="" alt="图{idx}" class="thumbnail">'
|
||||
new_thumb = f'<img src="{src}" alt="图{idx}" class="thumbnail">'
|
||||
html = html.replace(ph, new_thumb)
|
||||
return html
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
Reference in New Issue
Block a user