几个方案来回选不定?做一个随时切换的候选推荐页

候选对比页面需要让大卡片和下方列表保持同一选择。页面只保存 currentIndex,当前标题、分数与颜色都从 items[currentIndex] 读取;列表点击和"换一个结果"最终都只修改这个索引。

typescript 复制代码
interface PickItem {
  id: number
  title: string
  score: number
  color: string
}

@State currentIndex: number = 0
@State favorites: number[] = []
@State pickCount: number = 0

private current(): PickItem {
  return this.items[this.currentIndex]
}

轮换逻辑对数组长度取模,到最后一项后回到第一项。

typescript 复制代码
private choiceById(id: number): PickItem | undefined {
  return this.items.find((choice: PickItem) =>
    choice.id === id
  )
}

private requireChoice(id: number): PickItem {
  const target: PickItem | undefined = this.choiceById(id)
  if (!target) {
    throw new Error('Choice not found: ' + id)
  }
  return target
}

保留计数只在需要记录操作次数时才有意义。

typescript 复制代码
private pick(): void {
  this.pickCount += 1
  this.currentIndex = this.pickCount % this.items.length
}

目前手动点击列表只更新 currentIndex,不会同步 pickCount。例如先手动选中第三项,再点击"换一个结果",结果仍按旧计数计算,可能跳回第二项。

typescript 复制代码
private replaceChoice(
  id: number,
  updater: (source: PickItem) => PickItem
): void {
  this.items = this.items.map((choice: PickItem) =>
    choice.id === id ? updater(choice) : choice
  )
}

private updateChoiceMessage(id: number): void {
  const target: PickItem | undefined = this.choiceById(id)
  this.message = target ? '状态已更新' : '目标记录不存在'
}

收藏保存候选ID,不保存索引。数组重新排序时,ID仍能识别原项目。

typescript 复制代码
private favorite(): void {
  const id: number = this.current().id
  this.favorites = this.favorites.includes(id)
    ? this.favorites.filter((value: number) => value !== id)
    : [...this.favorites, id]
}

当前分数是预设值,页面还没有比较维度与评分输入,因此它展示的是候选浏览和收藏流程,不是完整决策模型。加入维度后,应保存每个方案在各维度的分值和权重,再派生总分;总分不应手动重复录入。

typescript 复制代码
private removeChoice(id: number): void {
  const before: number = this.items.length
  this.items = this.items.filter((choice: PickItem) =>
    choice.id !== id
  )
  this.message = this.items.length < before
    ? '记录已删除'
    : '没有找到要删除的记录'
}

private hasChoice(id: number): boolean {
  return this.items.some((choice: PickItem) => choice.id === id)
}

大卡片和候选列表都直接读取当前索引。手动点击列表修改 currentIndex,按钮则调用 pick()。

typescript 复制代码
Text(this.current().title)
  .fontSize(31)
  .fontWeight(FontWeight.Bold)

Text(`推荐度 ${this.current().score}`)

ForEach(this.items, (item: PickItem, index: number) => {
  Row({ space: 11 }) {
    Circle().fill(item.color)
    Text(item.title).layoutWeight(1)
    Text(item.score.toString())
  }
  .backgroundColor(
    this.currentIndex === index ? '#EEF0FF' : Color.White
  )
  .onClick(() => {
    this.currentIndex = index
  })
})

当前存在一处状态分叉:手动点击只改 currentIndex,pickCount 没有同步。先点第三项再点"换一个",结果仍根据旧计数计算。删除重复计数、直接基于当前索引前进会更稳定。

typescript 复制代码
private choiceKey(choice: PickItem): string {
  return String(choice.id)
}

private allChoiceKeys(): string[] {
  return this.items.map((choice: PickItem) =>
    this.choiceKey(choice)
  )
}

private hasDuplicateChoiceKey(): boolean {
  const keys: string[] = this.allChoiceKeys()
  return new Set(keys).size !== keys.length
}

收藏状态只影响当前卡片文案和顶部数量,不会改变候选排序。

typescript 复制代码
Text(
  this.favorites.includes(this.current().id)
    ? '♥ 已收藏'
    : '♡ 收藏这个结果'
)
  .onClick(() => {
    this.favorite()
  })

Text(`收藏 ${this.favorites.length}`)

收藏数组使用展开和过滤返回新数组,ArkUI可以观察引用变化。候选ID与索引职责不同:ID用于跨排序识别记录,索引用于当前固定数组定位。编辑能力加入后,应尽量把当前选择也迁移到ID。

所有选择入口最终只改一个索引

候选卡片点击和"换一个"按钮都更新 currentIndex。当前结果、卡片高亮和收藏按钮读取同一索引:

typescript 复制代码
private current(): PickItem {
  return this.items[this.currentIndex]
}

private pick(): void {
  this.currentIndex = (this.currentIndex + 1) % this.items.length
  this.message = `已选择:${this.current().title}`
}

private favorite(): void {
  const id: number = this.current().id
  this.favorites = this.favorites.includes(id)
    ? this.favorites.filter((item: number) => item !== id)
    : [...this.favorites, id]
}

如果按钮单独保存一份"推荐结果",列表点击另存一份"当前项",两个入口迟早会显示不同内容。现在索引是唯一选择状态,按钮只是按固定规则计算下一个索引。

typescript 复制代码
private filterChoice(
  predicate: (source: PickItem) => boolean
): PickItem[] {
  return this.items.filter((choice: PickItem) =>
    predicate(choice)
  )
}

private countChoice(
  predicate: (source: PickItem) => boolean
): number {
  return this.filterChoice(predicate).length
}

删除当前项时选择相邻候选,数组为空时 current() 返回可空结果并进入空状态。

顺序轮换与随机选择的差异

当前 pick() 是可预测轮换,每个候选都会依次出现,适合"换一个"。如果界面文案改成随机,就应使用真正的随机索引,并决定是否允许连续两次相同。候选大于一条时可以从除当前项外的范围抽取,只有一条时保持原项并提示。

typescript 复制代码
private copyChoiceList(): PickItem[] {
  return this.items.map((choice: PickItem) => ({
    ...choice
  }))
}

private restoreChoiceList(snapshot: PickItem[]): void {
  this.items = snapshot.map((choice: PickItem) => ({
    ...choice
  }))
  this.message = '数据已恢复'
}

点击列表属于明确选择,不应同时推动随机历史。收藏也不改变当前项,只更新收藏集合。把三类事件分开以后,消息文字才能准确说明发生的是主动选择、换项还是收藏切换。

候选编辑与持久化

新增候选先校验空名称,生成稳定 ID。标题可以按需求允许重复,例如两个同名餐厅位于不同地点;唯一性仍依靠 ID。编辑名称不改变收藏关系,删除时同步移除收藏 ID,避免收藏计数包含不存在记录。

typescript 复制代码
private firstChoice(): PickItem | undefined {
  return this.items.length > 0 ? this.items[0] : undefined
}

private lastChoice(): PickItem | undefined {
  const index: number = this.items.length - 1
  return index >= 0 ? this.items[index] : undefined
}

private isChoiceListEmpty(): boolean {
  return this.items.length === 0
}

空数组时 pick() 不访问索引,一条记录时换项保持原值,多条记录的轮换经过末项后准确回到首项。这些用例能同时覆盖两个选择入口的同步问题。

候选编辑后仍按 ID 选择

排序、删除和收藏都使用稳定对象身份:

typescript 复制代码
private current(): PickItem | undefined {
  return this.items.find((item: PickItem) =>
    item.id === this.currentId
  )
}

private remove(id: number): void {
  const index: number = this.items.findIndex(
    (item: PickItem) => item.id === id
  )
  this.items = this.items.filter((item: PickItem) => item.id !== id)
  this.favorites = this.favorites.filter((value: number) => value !== id)
  if (id === this.currentId) {
    this.currentId = this.items[Math.min(index, this.items.length - 1)]?.id ?? -1
  }
}

删除当前项时选择相邻候选,收藏集合也不会留下失效 ID。

相关推荐
郝学胜-神的一滴1 小时前
AI 编程智能体 03:拆解当下主流智能体能力
开发语言·c++·人工智能·python·程序人生·游戏
一条破秋裤1 小时前
Linux 线程同步:读写锁
java·linux·jvm
言乐61 小时前
Python文字审核模型
开发语言·windows·python·django·pygame
蜗牛互联网1 小时前
从Barclays扩大Claude部署看受监管软件工程的责任重分配
java·大数据·人工智能·后端·软件工程
天远API1 小时前
零信任架构实战:基于天远股权穿透构建自动化供应链授信穿透网关
java·人工智能·架构·自动化
最强小杰2 小时前
claude-opus-5.5 API 接入教程:anthropic-version 头、tools 定义、流式事件格式三个常见问题全部梳理,收藏备用
java·服务器·网络·ai
Cc.Y2 小时前
Java零基础入门:方法(函数)深度掌握
java·开发语言
2501_933670792 小时前
2027届大数据管理与应用求职数字化转型顾问:项目、SQL、业务理解怎么排优先级
数据库·sql
老王爱玩车2 小时前
工具函数——清空输入缓冲区
c语言·开发语言·学习