引言
在前端展示层面,鸿蒙壁纸大师充分利用了鸿蒙 A2A 协议的卡片能力 ,通过 DisplayFaCard 实现丰富的交互式 UI,而非简单的文本对话。整个壁纸创作流程中,用户看到的是可点击的选择列表 、生动的进度提示 和精美的壁纸展示卡片。
本文将深入解析前端的卡片交互设计,涵盖 selectList 选择卡片、wallpaper 展示卡片、SSE 事件驱动的前端更新以及内容安全策略。

上图展示了鸿蒙壁纸大师中的两种核心卡片类型:selectList 选择卡片和 wallpaper 壁纸展示卡片,以及它们在交互流程中的切换关系
一、卡片体系总览
1.1 三种卡片类型
鸿蒙壁纸大师使用了三种卡片类型:
| 卡片类型 | 用途 | 展示阶段 |
|---|---|---|
| selectList | 交互式选择列表 | 风格选择、主题选择、场景选择 |
| wallpaper | 壁纸结果展示 | 壁纸生成完成 |
| text | 文本消息 | 闲聊、进度提示 |
核心概念 :
DisplayFaCard是鸿蒙 A2A 协议中卡片展示的统一入口,通过cardName字段区分不同卡片类型,前端根据cardName渲染对应的 UI 组件。
1.2 卡片 JSON 结构规范
所有卡片都通过 A2A 协议的 artifact-update 事件的 parts 字段传递:
json
{
"jsonrpc": "2.0",
"id": "request_id",
"result": {
"taskId": "task_id",
"kind": "artifact-update",
"append": false,
"lastChunk": true,
"final": true,
"artifact": {
"artifactId": "task_id-options",
"parts": [
{
"kind": "data",
"data": {
"cardsInfo": [
{
"cardName": "selectList",
"cardData": { ... },
"displayType": "DisplayFaCard"
}
]
}
}
]
}
},
"error": { "code": 0, "message": "success" }
}
二、selectList 选择卡片
2.1 卡片构建函数
python
# agent_routes.py
def build_style_options_card(
title: str,
options: List[str],
task_id: str,
request_id: str,
has_next_batch: bool = True,
can_replay: bool = True,
) -> dict:
"""构建风格/主题选项的卡片 SSE 事件"""
items = [{"text": opt, "value": opt} for opt in options]
return {
"jsonrpc": "2.0",
"id": request_id,
"result": {
"taskId": task_id,
"kind": "artifact-update",
"append": False,
"lastChunk": True,
"final": True,
"artifact": {
"artifactId": f"{task_id}-options",
"parts": [{
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "selectList",
"cardData": {
"title": title,
"items": items,
"canReplay": can_replay,
},
"displayType": "DisplayFaCard"
}]
}
}]
}
},
"error": {"code": 0, "message": "success"}
}
2.2 三级选择流程的卡片展示
第一级:风格选择
json
{
"cardName": "selectList",
"cardData": {
"title": "你可以从下面挑选喜欢的风格,也可以直接告诉我你想要的其他风格。",
"items": [
{"text": "极简主义", "value": "极简主义"},
{"text": "弥散风", "value": "弥散风"},
{"text": "水墨国风", "value": "水墨国风"},
{"text": "莫兰迪风", "value": "莫兰迪风"}
],
"canReplay": true
}
}
第二级:主题选择
json
{
"cardName": "selectList",
"cardData": {
"title": "选一个喜欢的主题,打造你的专属壁纸。",
"items": [
{"text": "青峦远山", "value": "青峦远山"},
{"text": "寒梅疏枝", "value": "寒梅疏枝"},
{"text": "江上孤舟", "value": "江上孤舟"},
{"text": "竹林清风", "value": "竹林清风"}
],
"canReplay": true
}
}
第三级:场景选择
json
{
"cardName": "selectList",
"cardData": {
"title": "最后,选择你的壁纸尺寸和使用场景。",
"items": [
{"text": "竖屏 · 锁屏", "value": "竖屏 · 锁屏"},
{"text": "竖屏 · 主屏", "value": "竖屏 · 主屏"},
{"text": "横屏 · 锁屏", "value": "横屏 · 锁屏"},
{"text": "横屏 · 主屏", "value": "横屏 · 主屏"}
],
"canReplay": false
}
}
2.3 卡片交互的用户体验
| 阶段 | 卡片标题 | 选项数 | 支持换一批 | 支持重选 |
|---|---|---|---|---|
| 选择风格 | 引导选择风格 | 4 | ✅ | ✅ |
| 选择主题 | 引导选择主题 | 4 | ✅ | ✅ |
| 选择场景 | 引导选择场景 | 4 | ❌ | ❌ |
交互流程:
用户看到 selectList 卡片
↓
点击某个选项(如"水墨国风")
↓
前端将选项文本作为用户消息发送
↓
服务端识别关键词,跳过 LLM 意图识别
↓
构建下一级 selectList 卡片并推送
↓
前端自动刷新卡片
2.4 快捷命令与卡片的联动
当用户输入"换一批"时,服务端重新生成当前级别的选项并重新推送卡片:
python
# agent_routes.py - 换一批处理
if "换一批" in user_text:
if flow_manager.current_step == FlowStep.SELECT_STYLE:
flow_result = flow_manager.handle_style_batch_refresh()
elif flow_manager.current_step == FlowStep.SELECT_THEME:
flow_result = await flow_manager.handle_theme_batch_refresh()
# 重新构建并推送 selectList 卡片
batch_event = build_style_options_card(
title=flow_result["guidance"],
options=flow_result["options"], # 新的随机选项
task_id=task_id,
request_id=request_id,
has_next_batch=True,
can_replay=True,
)
yield f"data: {json.dumps(batch_event, ensure_ascii=False)}\n\n"
三、wallpaper 展示卡片
3.1 壁纸结果卡片
壁纸生成完成后,服务端推送 wallpaper 卡片展示结果:
python
# services/wallpaper_service.py
# 步骤 5: 发送完成状态(卡片格式)
text_event = {
"jsonrpc": "2.0",
"id": task_id,
"result": {
"taskId": task_id,
"kind": "artifact-update",
"append": False,
"lastChunk": True,
"final": True,
"artifact": {
"artifactId": f"{task_id}-complete",
"parts": [{
"kind": "data",
"data": {
"cardsInfo": [{
"cardName": "wallpaper",
"cardData": {
"title": "壁纸生成完成",
"imageUrl": image_url,
},
"displayType": "DisplayFaCard"
}]
}
}]
}
},
"error": {"code": 0, "message": "success"}
}
3.2 壁纸卡片的呈现
生成的 JSON 使前端呈现类似以下效果:
┌─────────────────────────┐
│ ✨ 壁纸生成完成 │
│ │
│ ┌───────────────┐ │
│ │ │ │
│ │ 壁纸图片 │ │
│ │ │ │
│ └───────────────┘ │
│ │
│ 风格:水墨国风 │
│ 主题:青峦远山 │
│ │
│ [换一张] [重新开始] │
└─────────────────────────┘
3.3 与流程管理器的状态同步
python
# wallpaper_flow_service.py
def handle_generate_complete(self, image_urls: List[str]) -> Dict[str, Any]:
"""处理生成完成"""
self.current_step = FlowStep.RESULT
# 记录生成的壁纸
for url in image_urls:
self.generated_wallpapers.append({
"url": url,
"style": self.selected_style,
"theme": self.selected_theme,
})
# 扣除免费次数
if self.free_credits_remaining > 0:
self.free_credits_remaining -= 1
return {
"step": "result",
"message": "你的专属艺术壁纸已生成完成!",
"wallpapers": [
{"url": url, "style": self.selected_style, "theme": self.selected_theme}
for url in image_urls
],
"credits_remaining": self.free_credits_remaining,
"has_regenerate": self.free_credits_remaining > 0,
}
四、SSE 事件驱动的前端更新
4.1 完整事件序列
从前端视角看,一次完整的壁纸生成过程如下:
前端发送 message/stream 请求
↓(后端返回 SSE 流)
事件1: status-update → state: submitted → "正在理解您的需求..."
事件2: status-update → state: working → "正在优化描述..."
事件3: status-update → state: working → "正在生成图片..."
事件4: status-update → state: working → "正在构思画面构图..."
事件5: status-update → state: working → "正在勾勒轮廓..."
...
事件N: artifact-update → final: true → wallpaper 卡片
4.2 前端处理逻辑(示意)
typescript
// 前端 SSE 事件处理示意代码
const eventSource = new EventSource('/agent/message');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
const result = data.result;
switch (result.kind) {
case 'status-update':
// 更新进度状态
updateProgress(result.status.message.parts[0].text);
break;
case 'artifact-update':
// 处理最终产物
const cardsInfo = result.artifact.parts[0].data.cardsInfo;
cardsInfo.forEach(card => {
switch (card.cardName) {
case 'selectList':
renderSelectListCard(card.cardData);
break;
case 'wallpaper':
renderWallpaperCard(card.cardData);
break;
}
});
break;
}
};
4.3 卡片替换逻辑
每次推送新的 selectList 卡片时,前端需要替换上一张卡片:
-
append: false:替换上一张卡片(风格→主题→场景的步骤推进) -
append: true:追加到已有内容(闲聊的逐 token 输出)风格选择卡片 (append: false)
↓ 用户选择风格
主题选择卡片 (append: false) → 替换风格卡片
↓ 用户选择主题
场景选择卡片 (append: false) → 替换主题卡片
↓ 用户选择场景
进度文本 (append: false)
↓ 逐条追加
进度文本 (append: true)
...
↓
壁纸卡片 (append: false) → 替换所有内容
五、内容安全策略
5.1 肖像检测
python
# wallpaper_flow_service.py
def check_content_policy(user_text: str) -> Tuple[bool, Optional[str]]:
"""检查内容是否符合规范(肖像、文字等限制)"""
# 肖像检测关键词
portrait_keywords = [
"我", "自己", "本人", "照片", "肖像", "人脸", "头像",
"明星", "网红", "模特", "人物", "人像", "自拍",
]
# 文字/水印检测关键词
text_keywords = [
"文字", "字体", "写字", "名字", "姓名", "签名",
"水印", "logo", "LOGO", "标志", "品牌",
]
# 检测肖像请求
for keyword in portrait_keywords:
if keyword in user_text:
if any(phrase in user_text for phrase in ["像我", "我的照片", "加入", "添加", "包含"]):
return False, "抱歉呀,为了版权和隐私安全,我暂时不能生成包含人物肖像的壁纸哦~"
# 检测文字/水印请求
for keyword in text_keywords:
if keyword in user_text:
if any(phrase in user_text for phrase in ["添加", "写上", "显示", "包含", "要有"]):
return False, "抱歉,目前暂时无法在壁纸上添加文字、水印或 LOGO~"
return True, None
5.2 无关话题检测
python
# wallpaper_flow_service.py
def is_irrelevant_topic(user_text: str) -> bool:
"""判断是否是无关话题(闲聊、知识问答等)"""
# 壁纸相关关键词
wallpaper_keywords = [
"壁纸", "背景", "桌面", "风格", "主题", "画面", "图片",
"生成", "创作", "设计", "图案", "色彩", "色调",
"锁屏", "主屏", "横屏", "竖屏", "尺寸", "分辨率",
]
# 检查是否包含壁纸相关关键词
for keyword in wallpaper_keywords:
if keyword in user_text:
return False
# 常见的无关问题类型
irrelevant_patterns = [
"你是谁", "你能做什么", "天气", "新闻", "股票",
"帮我", "写代码", "翻译", "计算",
]
for pattern in irrelevant_patterns:
if pattern in user_text:
return True
return False
def get_irrelevant_response() -> str:
"""获取无关问题的统一回应"""
return "我是你的专属壁纸创作助手,只专注于为你生成精美鸿蒙壁纸哦~我们继续设计你的专属壁纸吧!"
5.3 安全策略在流程中的位置
python
# agent_routes.py - generate_sse() 中的安全检查
# 1. 内容政策检查(肖像、文字等)
policy_passed, policy_reject_reason = check_content_policy(user_text)
if not policy_passed:
# 返回拒绝卡片
reject_event = { ... "text": policy_reject_reason }
yield f"data: {json.dumps(reject_event)}\n\n"
return
# 2. 无关话题检查(闲聊、知识问答等)
if is_irrelevant_topic(user_text):
# 引导回壁纸主题
irrelevant_event = { ... "text": get_irrelevant_response() }
yield f"data: {json.dumps(irrelevant_event)}\n\n"
return
# 3. 通过检查后,继续处理正常流程
5.4 内容安全的交互设计
安全策略被触发时,用户看到的是友好的引导信息而非冷冰冰的错误提示:
| 违规类型 | 用户输入示例 | 系统回复 |
|---|---|---|
| 肖像请求 | "加入我的照片" | "为了版权和隐私安全,不能生成包含人物肖像的壁纸哦~" |
| 文字请求 | "写上'我爱你'" | "暂时无法在壁纸上添加文字、水印或 LOGO~" |
| 无关话题 | "帮我写代码" | "只专注于为你生成精美鸿蒙壁纸哦~" |
安全设计原则:内容安全策略应始终返回友好的引导信息而非冷冰冰的错误码,让用户感受到智能体的温度。同时,所有安全检查必须在进入主流程之前完成,避免浪费 API 调用资源。
六、卡片的扩展设计
6.1 卡片数据的可扩展性
当前 cardData 结构预留了扩展字段:
python
cardData = {
"title": "标题文本",
"items": [{"text": "选项", "value": "value"}], # selectList
"canReplay": True, # 支持重新选择
# 可扩展字段
"imageUrl": "...", # 壁纸展示卡片
"style": "水墨国风", # 壁纸元信息
"theme": "青峦远山",
"credits_remaining": 5, # 剩余免费次数
"has_regenerate": True, # 是否支持重新生成
}
6.2 未来可扩展的卡片类型
| 卡片类型 | 用途 | 状态 |
|---|---|---|
| imageGrid | 多张壁纸网格展示 | 未实现 |
| colorPicker | 颜色选择器 | 未实现 |
| progressBar | 自定义进度条 | 未实现 |
| shareCard | 壁纸分享卡片 | 未实现 |
七、常见问题与解决方案
在前端卡片开发中,常见问题及解决方案如下:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 卡片不显示 | cardName 或 displayType 字段错误 |
检查 JSON 结构,确保使用 DisplayFaCard |
| 卡片重叠 | append 设置为 true |
多级选择流程中使用 false |
| 选项点击无响应 | 前端未正确处理点击事件 | 确保将选项文本作为用户消息发送 |
| SSE 连接断开 | 网络超时或服务端异常 | 前端实现自动重连机制 |
| 图片加载失败 | imageUrl 无效或过期 |
检查图片 URL 的有效期 |
八、最佳实践总结
在鸿蒙壁纸大师的前端卡片交互开发中,以下最佳实践值得参考:
| 最佳实践 | 说明 | 收益 |
|---|---|---|
| 三级选择流程 | 风格→主题→场景的渐进式引导 | 降低用户决策成本 |
| 卡片替换机制 | 使用 append: false 替换上一张卡片 |
保持界面简洁 |
| SSE 事件驱动 | 实时推送进度和卡片更新 | 提升用户体验 |
| 内容安全前置 | 在主流程前完成安全检查 | 避免 API 资源浪费 |
| 友好引导设计 | 违规请求返回引导提示 | 提升用户满意度 |
| 卡片可扩展性 | 预留字段支持未来扩展 | 降低维护成本 |
开发建议 :卡片交互设计应以用户为中心,每一步的选择都应有明确的引导和反馈。同时,合理使用
append字段控制卡片的替换与追加,避免界面混乱。
九、总结
本文深入解析了鸿蒙壁纸大师的前端卡片交互体系,核心要点:
- 三种卡片类型 :
selectList(选择)、wallpaper(展示)、text(文本) - 三级选择流程:风格→主题→场景,每级通过卡片替换推进
- SSE 事件驱动:status-update 推送进度,artifact-update 推送卡片
- 内容安全策略:肖像检测、文字检测、无关话题过滤,确保合规
- 友好的交互设计:违规请求返回引导提示而非错误信息
- 卡片可扩展性:预留字段支持未来更多交互类型
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源: