• Lv0
    粉丝0

积分6 / 贡献0

提问0答案被采纳0文章3

[经验分享] [OpenHarmony学习计划] 亲戚称谓计算器 + LLM 原创

lishengzxc 显示全部楼层 发表于 2024-6-23 21:19:44

0030086000741113711.20240623203749.20672203875181820224293013290081.png

概述

本项目是是学习 https://developer.huawei.com/consumer/cn/codelabsPortal/getstarted/101718800110527001 后的一份练习 demo

实现了以下特性:

接下来简要描述下开发流程,源码可以直接查看:https://gitee.com/lishengzxc/ohms-next-relationship

开发流程

参考三层架构准备项目目录

0030086000741113711.20240623204240.44787044389701801371695480030290.png

准备 UI

拆分 UI,实现计算显示部分组件 Display.ets 和 键盘部分 Ctrl.ets

Display.ets

result 和 calcString 类似于 react 的 props 接受外部传入的值

@Component
export struct Display {
  @Prop result: string = '';
  @Prop calcString: string = '';

  build() {
    Column() {
      Column() {
        Text(this.result)
          .fontSize(25)
          .padding({ left: 16, right: 16 })
          .width('100%')
          .height('40%')
          .textAlign(TextAlign.End)
      }

      Column() {
        Text(this.calcString)
          .fontSize(35)
          .width('100%')
          .height('60%')
          .padding({ left: 16, right: 16 })
          .textAlign(TextAlign.End)
          .borderWidth({ bottom: 1 })
          .borderColor('#ccc')

      }

    }.width('100%').height('25%')
  }
}

Ctrl.ets

使用 Grid 组件布局:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-layout-development-create-grid-V5

需要注意 = 按钮需要占用 2 行;

按钮点击的时候执行父组件传入的方法,用来接受用户点了什么按钮

@Component
export struct Ctrl {
  @Prop loading: boolean = false;
  btns: string[][] = [
    ['Item', '父', '父亲'],
    ['Item', '母', '母亲'],
    ['Function', '回退', 'back', 'gray'],
    ['Function', '清空', 'clear', 'black'],
    ['Item', '兄', '哥哥'],
    ['Item', '姐', '姐姐'],
    ['Item', '夫', '老公'],
    ['Item', '妻', '老婆'],
    ['Item', '弟', '弟弟'],
    ['Item', '妹', '妹妹'],
    ['Function', '互换', 'change', 'blue'],
    ['Function', '=', '=', 'red'],
    ['Item', '子', '儿子'],
    ['Item', '女', '女儿'],
    ['Function', '?', '?']
  ];
  onBtnClick?: (data: string[]) => void
  layoutOptions: GridLayoutOptions = {
    regularSize: [1, 1],
    onGetRectByIndex: (index: number) => {
      if (index === 11) {
        // 控制 = 按钮占有 2 行 
        return [2, 3, 2, 1]
      }

      return [index / 4 - 1, index / 4 - 1, 1, 1];
    }
  }

  build() {
    Grid(undefined, this.layoutOptions) {
      ForEach(this.btns, (data: string[]) => {
        GridItem() {

          if (data[1] === '=') {
            Button(data[1])
              .type(ButtonType.Normal)
              .onClick(() => {
                if (this.loading) return;
                this.onBtnClick?.(data)
              })
              .width('100%')
              .height('100%')
              .borderRadius(20)
              .opacity(this.loading ? 0.3 : 1)
              .backgroundColor(data[3])

          } else {
            Button(data[1])
              .type(ButtonType.Normal)
              .onClick(() => {
                if (this.loading) return;
                this.onBtnClick?.(data)
              })
              .width('100%')
              .height('100%')
              .borderRadius(20)
              .backgroundColor(data[3])
          }
        }
      }, (data: string) => data[2])
    }
    .columnsGap(10)
    .rowsGap(10)
    .rowsTemplate('1fr 1fr 1fr 1fr')
    .padding(10)
    .columnsTemplate('1fr 1fr 1fr 1fr')
    .width('100%')
    .height('75%')
  }
}

组合他们

<pre><br class="Apple-interchange-newline"/></pre>

import { Display } from '../view/Display';
import { Ctrl } from '../view/Ctrl';
import { llm } from '@ohos/commons';

@Preview
@Component
export struct Calculator {
  @State result: string = '';
  @State calc: string[] = [];
  @State loading: boolean = false;
  onBtnClick: (data: string[]) => string | void = (data: string[]) => {
    const type = data[0];
    const value = data[2];

    if (type === 'Function') {
      if (value === 'back') {
        this.calc.pop();
      }
      if (value === '=') {
        this.onCalc();
      }
      if (value === 'clear') {
        this.calc = [];
        this.result = '';
      }

    }

    if (type === 'Item') {
      this.calc.push(value);
    }
  }

  onCalc() {
    if (!this.calc.length) return;
    if (this.calc.length === 1) {
      this.result = this.calc[0]
    }

    this.askLLM();
  }

  askLLM() {
    this.loading = true;

    llm({
      system: '你是一个亲戚称谓计算器,请根据我的提问回复我 应该称呼对方为什么,比如爸爸的爸爸,请回复我 {\"data\":\"爷爷\"}',
      query: this.calc.join('的'),
      appKey: 'sk-xxx', // 请使用真实的大模型 appkey
    }).then((res) => {
      console.log('llm', res);

      this.calc = [JSON.parse(res as string).data]
      this.result = this.calc[0];
      this.loading = false;
    })

  }

  build() {
    Column() {
      Display({ result: this.result, calcString: this.calc.join('的') })
      Ctrl({ onBtnClick: this.onBtnClick, loading: this.loading })
    }.width('100%').height('100%')
  }
}

如何计算

我没有使用以下常见的本地称谓树的方式来表达称谓的数据结构

{
  "爸爸": {
    "爸爸": "爷爷",
    "妈妈": "奶奶",
    "哥哥": "伯父",
    "弟弟": "叔叔",
    "姐姐": "姑妈",
    "妹妹": "姑妈",
    "丈夫": "未知",
    "妻子": "妈妈",
    "儿子": { "older": "哥哥", "middle": "我", "younger": "弟弟" },
    "女儿": { "older": "姐姐", "middle": "我", "younger": "妹妹" }
  }
}

而是为了实践网络请求的场景,直接问大模型拿到结果

准备大模型账号

我使用的是通义千问,这个不特别展开如何申请账号,可以自己搜索阿里云-百炼自己摸索下

封装请求方法

import axios, { AxiosError, AxiosResponse } from '@ohos/axios'

interface iParams {
  system: string,
  query: string,
  appKey: string,

}

interface iMsg {
  role: string,
  content: string
}

interface iInput {
  messages: iMsg[]
}

interface iData {
  model: string,
  input: iInput
}

interface iOutput {
  finish_reason: string,
  text: string
}

interface iRes {
  output: iOutput
}

export default (params: iParams) => {

  return axios.post<string, AxiosResponse<iRes>, iData>('https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation', {
    "model": "qwen-max",
    "input": {
      "messages": [
        {
          "role": "system",
          "content": params.system
        },
        {
          "role": "user",
          "content": params.query
        }
      ]
    },
  }, {
    headers: {
      'Authorization': `Bearer ${params.appKey}`,
      'Content-Type': 'application/json'
    }
  })
    .then((response: AxiosResponse<iRes>) => {
      console.info('axios success', JSON.stringify(response));
      return response.data.output.text;
    })
    .catch((error: AxiosError) => {
      console.info('axios error', JSON.stringify(error));
    });
}

请求大模型

llm({
      system: '你是一个亲戚称谓计算器,请根据我的提问回复我 应该称呼对方为什么,比如爸爸的爸爸,请回复我 {\"data\":\"爷爷\"}',
      query: this.calc.join('的'),
      appKey: 'sk-xxx'
    })

构建运行

0030086000741113711.20240623210338.41016412977744940621048475927143.gif

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部