feat(core): 消息中心对齐设计稿 · 处理记录(真实数据)+ 字号/搜索框/页脚 + 归档接活
- ops/views.py: 通知 metadata 写入真实 timeline(项目/资产/计费/欢迎,真时间戳),create_once 给旧通知补齐 timeline - messages.tsx/css: 新增「处理记录」区块(读 metadata.timeline)+ 补回 .msg-timeline/.msg-step/.msg-log 样式;搜索框结构回设计稿 .msg-search;mono 字号对齐 10.5/10px;空态图标色 token 化 - 归档按钮接活:api.archiveNotification + App 装配 onArchive + 详情页脚归档(后端落 archived_at + 本地乐观移除/扣计数) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -40,14 +40,28 @@ def project_priority(project):
|
||||
return Notification.Priority.INFO
|
||||
|
||||
|
||||
def _step_time(dt):
|
||||
"""处理记录单步时间:今天只显示 HH:MM,跨天加 MM-DD(对齐设计稿的 mono 时间列)。"""
|
||||
if dt is None:
|
||||
return ""
|
||||
local = timezone.localtime(dt)
|
||||
now = timezone.localtime()
|
||||
return local.strftime("%H:%M") if local.date() == now.date() else local.strftime("%m-%d %H:%M")
|
||||
|
||||
|
||||
def ensure_team_notifications(team, user):
|
||||
def create_once(dedupe_key, **payload):
|
||||
Notification.objects.get_or_create(
|
||||
obj, created = Notification.objects.get_or_create(
|
||||
team=team,
|
||||
recipient=user,
|
||||
dedupe_key=dedupe_key,
|
||||
defaults=payload,
|
||||
)
|
||||
# 已存在的旧通知补齐「处理记录」timeline(早期建的没存),让历史消息也能显示处理记录
|
||||
timeline = (payload.get("metadata") or {}).get("timeline")
|
||||
if not created and timeline and not (obj.metadata or {}).get("timeline"):
|
||||
obj.metadata = {**(obj.metadata or {}), "timeline": timeline}
|
||||
obj.save(update_fields=["metadata", "updated_at"])
|
||||
|
||||
create_once(
|
||||
"system:welcome",
|
||||
@@ -61,6 +75,7 @@ def ensure_team_notifications(team, user):
|
||||
owner_label="系统",
|
||||
cost_label="-",
|
||||
related_url="settings.html#sec-notify",
|
||||
metadata={"timeline": [[_step_time(timezone.now()), "团队接入 AirShelf,真实消息中心已启用"]]},
|
||||
)
|
||||
|
||||
for project in Project.objects.filter(team=team).select_related("product", "created_by").order_by("-updated_at")[:5]:
|
||||
@@ -78,7 +93,15 @@ def ensure_team_notifications(team, user):
|
||||
owner_label=project.created_by.username if project.created_by_id else "成员",
|
||||
cost_label="-",
|
||||
related_url=f"pipeline.html?project_id={project.id}",
|
||||
metadata={"status": project.status, "current_stage": project.current_stage},
|
||||
metadata={
|
||||
"status": project.status,
|
||||
"current_stage": project.current_stage,
|
||||
"timeline": [
|
||||
[_step_time(project.created_at), f"项目「{project.name}」创建"],
|
||||
[_step_time(project.updated_at), f"推进至 {project_stage_label(project)}"],
|
||||
[_step_time(project.updated_at), f"当前状态 · {project.get_status_display()}"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
for asset in Asset.objects.filter(team=team).select_related("created_by").order_by("-updated_at")[:3]:
|
||||
@@ -94,7 +117,15 @@ def ensure_team_notifications(team, user):
|
||||
owner_label=asset.created_by.username if asset.created_by_id else "成员",
|
||||
cost_label="-",
|
||||
related_url="library.html",
|
||||
metadata={"asset_id": str(asset.id), "category": asset.category, "asset_type": asset.asset_type},
|
||||
metadata={
|
||||
"asset_id": str(asset.id),
|
||||
"category": asset.category,
|
||||
"asset_type": asset.asset_type,
|
||||
"timeline": [
|
||||
[_step_time(asset.created_at), f"{asset.get_category_display()} · {asset.get_asset_type_display()} 生成"],
|
||||
[_step_time(asset.updated_at), "通过基础审核,写入资产库"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
account, _ = CreditAccount.objects.get_or_create(team=team)
|
||||
@@ -111,6 +142,12 @@ def ensure_team_notifications(team, user):
|
||||
owner_label="系统",
|
||||
cost_label=f"¥{account.balance:.2f}",
|
||||
related_url="account.html",
|
||||
metadata={
|
||||
"timeline": [
|
||||
[_step_time(timezone.now()), f"余额降至 ¥{account.balance:.2f},低于预警线 ¥100"],
|
||||
[_step_time(timezone.now()), "已发送站内预警通知"],
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user