在openHarmony开发板上开发deepseek的可视化应用(2.对话框功能的实现)
显然,对话和消息的功能远不止这些,比如对特殊字体的特殊渲染,回答框底下的四个按钮功能......写在最后:DeepSeek 以低成本达顶尖 AI 能力,其开源模式引启行业变革,引发对创新、人才、产业格局等多方面思考 ,提升了开发效率与安全性,但同时使得开发者需要灵活使用ai赋能。时代一直在进步和变化,然而世界的底层规则却依旧,谁掌握了世界上最利好的工具,谁说话声音最大。
目录
开发准备:
参考上一篇文章并已完成对数据的处理在openHarmony开发板上开发deepseek的可视化应用(1.处理流式数据)
分析:
本文参考的功能是电脑网页版deepseek,链接如下:
首先可以看出对话框的布局是问题在右上侧,而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赋能。时代一直在进步和变化,然而世界的底层规则却依旧,谁掌握了世界上最利好的工具,谁说话声音最大。
更多推荐
所有评论(0)