目录

开发准备:

分析:

具体实现:

1.对话框Dialog

Dialog组件实现代码如下:

关键步骤分析:

2.Message组件

Message组件实现代码如下

关键步骤分析:

总结


开发准备:

参考上一篇文章并已完成对数据的处理在openHarmony开发板上开发deepseek的可视化应用(1.处理流式数据)

分析:

本文参考的功能是电脑网页版deepseek,链接如下:

https://chat.deepseek.com/

首先可以看出对话框的布局是问题在右上侧,而deepseek回答在左下侧并带有头像。其次deepseek的消息区域在思考过程中呈现loading效果如下:

并且在一个页面支持多条问题及回答,保留历史问答。最后当对话框内容随着消息的增多溢出可视区域时,滚动条会自动跟随到底部。

大致功能分析完毕,接下来看具体实现过程。

具体实现:

这里我分成了两个组件,一个是对话框的组件Dialog和它的子组件Message,我们把一条对话纪录封装成一个Message组件

1.对话框Dialog

我们首先要实现的是对话框,在Dialog组件中我们只需要实现对Message组件的循环渲染,对消息触底时滚动条跟随触底的处理。

Dialog组件实现代码如下:
import { MessageBox } from './MessageBox'
import { ChatMessage } from '../model/Type'


@Component
export struct DialogBox {
  // 使用Scroller控制滚动位置
  private scroller: Scroller = new Scroller()
  // 消息输入框内容
  @StorageProp("sendMessage") sendMessage: string = ''
  //消息列表
  @StorageLink("messageList") @Watch('MesChange') messageList: ChatMessage[] = []

  aboutToAppear(): void {
    this.MesChange()
  }
  //滚动条随消息生成自动滚到底部
  MesChange(){
    setTimeout(()=>{
      this.scroller.scrollEdge(Edge.Bottom)
    },20)
    }
  build() {
    Column() {
      Scroll(this.scroller) {
        List() {
          ForEach(this.messageList, (item: ChatMessage) => {
            ListItem() {
              MessageBox({ responseMessage: item.content, question: item.question, done: item.done })
            }
          })
        }
        .divider({ strokeWidth: 0 }) // 去除列表项分割线
        .width("100%")
      }
      .scrollBar(BarState.Off) // 隐藏滚动条
    }.width("80%").layoutWeight(1)
  }
}


//此处放../model/Type下的ChatMessage 非本组件代码
export  interface ChatMessage {
  question:string //问题
  content: string, //回答内容
  done:boolean    //回答结束标记
  timestamp?: number //时间戳
}

关键步骤分析:

对于实现消息触底功能的代码逻辑如下:

1.使用Scroll组件实现滚动效果

2.用@watch装饰器监听消息数组messageList的变化(messageList的获取方式会放在本系列下一篇InputBox输入框实现来写

3.实现监听函数MesChange(),利用scroll中的scrollEdge方法实现滚动条触底效果(方法具体使用参考openharmony文档

注意:在生命周期函数aboutToApper()里也应该调用一次MesChange方法以保证页面刚打开时滚动条触底。

接下来是Message组件的实现

2.Message组件

在Message组件中,首先布局方面要实现右侧问题,左侧回答的效果,这一步我们可以用justifyContent(FlexAlign.End/FlexAlign.Start)来实现,而loading效果我则是封装了一个loading组件,又通过一个isloading变量来判断是否为loading状态。

Message组件实现代码如下
import { LoadingComponent } from './LoadingIndicator'

@Component
export struct MessageBox {
  @Prop responseMessage: string
  @StorageProp("isLoading") isLoading: boolean = false
  @Prop done:boolean
  @Prop question:string
  build() {
    Column() {
      // 根据消息类型选择布局方向
      Column({ space: 10 }) {
        // 用户消息(右侧)
        Row() {
          // 头像或时间戳可以在这里添加
          Text(this.question)
            .padding(10)
            .backgroundColor("#0084FF")
            .fontColor(Color.White)
            .borderRadius(10)
        }.width("100%")
        .justifyContent(FlexAlign.End)
        // 机器人消息(左侧)
        Row() {
          //回复
          Row() {
            Image($r('app.media.deepseek')).width(30)

            if (this.isLoading&& !this.done) {
              //加载状态
              LoadingComponent()
            }
            else {
              Column() {
                Text(this.responseMessage).padding(10).backgroundColor("#F5F5F5").borderRadius(10)
              }.flexShrink(1)
            }


          }.width("100%")
        }.width("100%")
        .justifyContent(FlexAlign.Start)
      }
      .width("100%")
      .padding(10)



    }
  }
}

其中loadingComponent组件代码如下:

@Component
export struct LoadingComponent {
  @State rotateAngle: number = 0; // 控制旋转角度

  // 旋转动画
  private startRotation() {
    setInterval(() => {
      this.rotateAngle += 10; // 每次旋转10度
      if (this.rotateAngle >= 360) {
        this.rotateAngle = 0;
      }
    }, 50); // 控制旋转速度
  }

  aboutToAppear() {
    this.startRotation();
  }

  build() {
    // 使用 Image 组件实现旋转动画
    Image($r('app.media.loading')) // 替换为你的加载图标资源
      .width(30)
      .height(30)
      .rotate({ angle: this.rotateAngle, centerX: '50%', centerY: '50%' });
  }
}
关键步骤分析:

对于回答消息的处理渲染逻辑如下:

1.通过isloading和传过来的item元素中的done属性进行双重判断从而达到渲染效果的不同

2.而loading组件主要是通过改变图片的角度来实现

总结

显然,对话和消息的功能远不止这些,比如对特殊字体的特殊渲染,回答框底下的四个按钮功能......

写在最后:DeepSeek 以低成本达顶尖 AI 能力,其开源模式引启行业变革,引发对创新、人才、产业格局等多方面思考 ,提升了开发效率与安全性,但同时使得开发者需要灵活使用ai赋能。时代一直在进步和变化,然而世界的底层规则却依旧,谁掌握了世界上最利好的工具,谁说话声音最大。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐