经过上周的学习与基础框架的实现后,这周我主要完成了项目前端组件库的核心模块设计与开发,为后续世界观选择、角色创建及主对话交互提供统一且稳定的界面基础。

 设计目标

目标 描述
高复用性 组件可在多个页面中灵活调用,减少重复代码
配置灵活 通过 props 暴露丰富的配置项,适应不同场景
状态独立 组件内部仅维护自身 UI 状态,业务数据由父组件注入
视觉统一 沿用项目设计语言,保持一致的视觉风格
交互友好 提供加载状态、错误反馈、动画过渡等细节体验
可测试性 组件职责单一,便于单元测试

设计原则

  1. 单一职责原则:每个组件只做一件事,如果一个组件变得过于复杂,就考虑拆分成更小的子组件。

  2. Props 向下,Events 向上:数据从父组件流入,事件从子组件流出,便于调试和维护。

  3. 受控组件模式:对于关键状态(如选中的角色、展开的面板),采用受控模式——状态由父组件管理,子组件通过事件通知父组件更新。

  4. 渐进式增强:基础功能可用,高级功能视情况选配

下图展示了我项目中主要实现的,在后续的各个界面中使用到的组件:

组件功能详解

1. CharacterCard.vue - 角色卡片组件

用途:展示单个角色的信息卡片,用于角色列表或角色详情页

功能

  • 展示角色名称、状态、身份标签

  • 显示好感度进度条和数值(0-100)

  • 支持选中高亮状态

  • 悬停显示操作按钮(对话、详情)

  • 根据关系类型(盟友/敌对/隐藏)显示不同边框色

使用场景

角色选择界面

主聊天页面的侧边角色列表

状态面板中的角色详情展示

Props 配置

props: {
  character: Object,           // 角色数据
  isActive: Boolean,           // 是否选中
  showRelationship: Boolean,   // 是否显示关系值
  showActions: Boolean,        // 是否显示操作按钮
  relationType: String         // ally, rival, hidden, neutral
}

关系值映射

关系值 显示文本 颜色 进度条色
≥80 挚友 #10b981 绿色渐变
60-79 友好 #10b981 绿色渐变
40-59 中立 #f59e0b 橙色渐变
20-39 冷淡 #f97316 橙红渐变
<20 敌对 #ef4444 红色渐变

2. EventNotification.vue - 事件通知组件

用途:显示游戏中发生的各种事件通知(剧情推进、关系变化、任务更新等)

主要功能

  • 支持不同类型事件的不同样式(success / warning / danger / info)

  • 自动淡出消失或手动关闭

  • 显示事件图标、标题和描述

  • 支持动画效果(滑入/淡入)

  • 可配置显示时长,查看详情等功能

使用场景

  • 主聊天页面右侧事件面板

  • 全局浮动通知(类似Toast)

  • 剧情关键节点提示

使用示例:

<EventNotification 
  :event="{
    title: '关系变化',
    description: '将军对你的好感度上升了10点',
    type: 'success',
    duration: 4000,
    action: { text: '查看', handler: () => {} }
  }"
  @close="handleClose"
/>

3. LoadingSpinner.vue - 加载动画组件

用途:显示数据加载中的等待动画

主要功能

  • 旋转动画效果

  • 可配置大小、颜色

  • 支持全屏遮罩模式

  • 可选显示加载文字

使用场景

  • AI生成剧情时的等待状态

  • 发送消息后等待响应

  • 页面数据加载中

  • 切换场景时的过渡效果

4. MessageBubble.vue - 消息气泡组件 

用途:显示聊天对话中的单条消息

功能

  • 区分用户消息(右对齐)和 AI/NPC 消息(左对齐)

  • 显示发送者头像、名称、发送时间

  • 支持消息类型:普通文本、系统消息、动作描述

  • 支持剧情选项按钮(多条可选回复)

  • 长文本自动换行,支持 Emoji

使用场景

  • 主聊天对话区域

  • 任何需要显示对话记录的地方

Props 定义

props: {
  message: {
    type: Object,
    required: true,
    default: () => ({
      id: null,
      type: 'text',
      role: 'user',     // user, ai, system
      sender: '',
      avatar: '',
      content: '',
      timestamp: new Date(),
      options: []       // [{ text, action }]
    })
  }
}

5. RelationshipGraph.vue - 人物关系图组件 

用途:可视化展示角色之间的三层社交关系网络

三层关系定义

关系类型 说明 颜色 图标
盟友 与玩家关系良好,愿意提供帮助 绿色 (#10b981) 🤝
敌对 与玩家关系紧张,可能产生冲突 红色 (#ef4444) ⚔️
隐藏 表面中立,暗中有特殊联系 紫色 (#8b5cf6) 🕯️

主要功能

  • 可视化展示角色之间的关系网络

  • 区分三种关系类型

  • 支持缩放、拖拽查看

  • 点击节点查看关系详情

  • 动态更新关系值

使用场景

  • 主聊天页面右侧关系网面板

  • 状态面板中的关系分析页

  • 角色详情页的关系图谱

核心算法:

采用简化的辐射式布局:

  1. 玩家角色位于画布中心

  2. 三层关系分布在三个方向(0°、120°、240°)

  3. 同一类型内的多个角色围绕该方向扇形展开

generateGraph() {
  const center = { x: 250, y: 200, name: this.centerCharacter }
  const radius = 120
  const angles = [0, 120, 240]  // 三个方向
  
  this.nodes = [center]
  this.edges = []
  
  Object.entries(this.relationsData).forEach(([type, relations], idx) => {
    relations.forEach((rel, relIdx) => {
      const angle = (angles[idx % angles.length] + relIdx * 30) * Math.PI / 180
      const x = 250 + radius * Math.cos(angle)
      const y = 200 + radius * Math.sin(angle)
      this.nodes.push({ name: rel.name, x, y, type })
      this.edges.push({ from: center, to: { x, y }, color: colors[type] })
    })
  })
}

6. StatusPanel.vue - 状态面板组件

用途:展示玩家和所有角色的详细状态信息

主要功能

  • 玩家属性展示(如生命、精力、声望等)

  • 所有角色关系值列表(后续可能实现排序筛选功能)

  • 角色详细背景故事展示(由AI给各个角色生成)

  • 当前生效的Buff/Debuff效果

  • 游戏进度统计(完成的任务、解锁的剧情)

使用场景

  • 点击"查看状态面板"按钮弹出的模态框

  • 游戏暂停菜单中的状态查看

遇到的问题与解决方案

EventNotification 自动消失与用户交互冲突

现象:用户正在阅读通知时,通知自动消失,体验不佳

解决方案

  • 添加悬停暂停功能(@mouseenter="pauseTimer" / @mouseleave="resumeTimer"

  • 延长默认显示时间至 5 秒

  • 重要事件不自动消失(duration: 0)

LoadingSpinner 在全屏模式下遮罩层级问题

现象:全屏遮罩被其他元素覆盖

解决方案

.loading-spinner.fullscreen {
  position: fixed;
  z-index: 9999;  /* 确保最高层级 */
  backdrop-filter: blur(4px);  /* 毛玻璃效果 */
}

关系图数据格式不统一

现象:关系数据可能是是字符串数组,也可能是是对象数组,就会导致组件渲染失败。

原因分析:由于目前我们前后端分开设计,数据格式尚未完全稳定统一,目前采取的是模拟数据。

解决方案:在组件内部增加归一化函数,兼容多种格式:

normalizeRelations(list, type) {
  if (!list || !Array.isArray(list)) return []
  
  return list.map(item => {
    if (typeof item === 'string') {
      // 字符串格式:转换为对象
      return { name: item, type, value: 50, description: '' }
    } else if (typeof item === 'object' && item.name) {
      // 对象格式:直接使用,补充默认值
      return { ...item, type, value: item.value || 50 }
    }
    return null
  }).filter(Boolean)
}

通过上述工作的实现,我计划在下周将上述组件集成到前端vue各个主界面中,并视情况不断优化组件,实现更完备的功能。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐