fix(ohos): 设置页插件/工具数不显示 + 聊天自动滚底时机

设置页计数(实测:后端返回 plugins=35/tools=260,卡片却一直显示 '-'):
- 根因是 ArkUI 的 @Builder 按值传参是“快照”语义——父组件因
  @StorageProp 变化重渲染时不会用新值重跑 builder,数值永远停在
  首次渲染的 0。把 KPI 小卡从 @Builder 方法改为独立 @Component
  (@Prop 单向下发),父组件重渲染时子组件拿到新值并重绘。
- 已上模拟器验证:设置页显示 插件 35 / 工具 260。

聊天自动滚动:
- scrollToBottom 原来只在 50ms 后滚一次;长历史/长思考卡的布局在
  消息数组更新后的若干帧才稳定,一次滚动会落在“当时”的底部,
  最后一条被输入区挡住。改为 50ms 与 260ms 各滚一次,480ms 后
  恢复正常滚动态。
This commit is contained in:
JianFeeeee
2026-09-15 11:56:18 +08:00
parent baddaf387e
commit 307a6faee7
2 changed files with 60 additions and 4 deletions

View File

@ -69,14 +69,20 @@ export struct ChatStream {
private scrollToBottom(): void {
this.autoScrolling = true;
// 滚两次:内容高度是在消息数组更新后的若干帧内才逐步确定的。
// 长历史/长思考卡布局慢,只滚一次会落在“当时”的底部 —— 实测初次加载
// 历史时最后一条被输入区挡住,再手动上滑还能露出更多内容。
setTimeout(() => {
this.scroller.scrollEdge(Edge.Bottom);
}, 50);
setTimeout(() => {
this.scroller.scrollEdge(Edge.Bottom);
}, 260);
setTimeout(() => {
this.autoScrolling = false;
this.navHidden = false;
navBar.setVisible(true);
}, 450);
}, 480);
}
/**

View File

@ -120,10 +120,15 @@ export struct StatusSummaryCard {
.alignItems(VerticalAlign.Center)
.transition(TransitionEffect.OPACITY.combine(TransitionEffect.translate({ y: 12 })).animation({ duration: ANIM_ENTER, curve: Curve.EaseOut }))
// 能力计数:图标 + 数字,只保留真正会变的两项(插件 / 工具)
// 能力计数:图标 + 数字,只保留真正会变的两项(插件 / 工具)
//
// 必须是**子组件**@Prop 单向下发)而不是本组件里的 @Builder
// ArkUI 的 @Builder 按值传参是“快照”语义,父组件重渲染时
// 不会用新值重跑 builder —— 实测K_PLUGINS 已经是 35
// 但 @Builder 里画的还是首次的 0永远显示 '-'。
Row({ space: 10 }) {
this.kpiTile($r('app.media.ic_plug'), '插件', this.plugins, COLOR_ACCENT)
this.kpiTile($r('app.media.ic_tool'), '工具', this.tools, COLOR_CYAN)
KpiTile({ icon: $r('app.media.ic_plug'), label: '插件', value: this.plugins, tint: COLOR_ACCENT })
KpiTile({ icon: $r('app.media.ic_tool'), label: '工具', value: this.tools, tint: COLOR_CYAN })
}
.width('100%')
.margin({ top: 14 })
@ -214,6 +219,51 @@ export struct StatusSummaryCard {
}
}
/**
* 能力计数小卡(插件 / 工具)。
*
* 单独成组件而非 @Builder@Builder 的按值参数不会随父组件重渲染而刷新,
* 数值会永远停在首次渲染的 0。@Prop 是单向下发,父组件因 @StorageProp
* 变化重渲染时,子组件拿到新值并重绘。
*/
@Component
struct KpiTile {
@StorageProp('themeIsDark') private isDark: boolean = true;
@Prop icon: Resource = $r('app.media.ic_plug');
@Prop label: string = '';
@Prop value: number = 0;
@Prop tint: string = '';
build() {
Row({ space: 8 }) {
Image(this.icon)
.width(16)
.height(16)
.fillColor(this.tint)
.draggable(false)
Column({ space: 1 }) {
Text(this.value > 0 ? this.value.toString() : '-')
.fontSize(17)
.fontWeight(FontWeight.Bold)
.fontColor(this.palette().textPrimary)
Text(this.label)
.fontSize(11)
.fontColor(this.palette().textMuted)
}
.alignItems(HorizontalAlign.Start)
}
.layoutWeight(1)
.padding({ left: 12, right: 12, top: 10, bottom: 10 })
.borderRadius(RADIUS_SM)
.backgroundColor(this.palette().bgHover)
.alignItems(VerticalAlign.Center)
}
private palette(): ThemePalette {
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
}
}
/**
* 运行状态明细:设置页「运行状态」二级页面的内容。
*