1. HTTP 数据请求

news2025/2/28 18:41:02

相关资源:

  1. 图片素材📎图片素材.zip

接口文档

1. HTTP 数据请求

什么是HTTP数据请求:

(鸿蒙)应用软件可以通过(鸿蒙)系统内置的 http 模块 和 Axios,通过 HTTP 协议服务器进行通讯

学习核心Http请求技术:

  1. Http模块 - 属于鸿蒙内置模块,安全性较高,如果是银行项目都是Http模块
  2. Axios模块(第三方) - C端(B端)项目可以用开源的axios模块
  3. 鸿蒙、android,iOS 的原生App开发无需考虑跨域问题

2. http模块基本用法

咱们来看看 http 模块如何使用

传送门

// 1. 导入模块
import { http } from '@kit.NetworkKit'

// 2. 创建请求对象
const request = http.createHttp();

// 3. 调用request方法进行服务器请求
request.request('Url地址?key1=value1&key2=value2',{
  //GET(默认请求方式)、POST、PUT、DELETE,
  method:http.RequestMethod.GET,  // 如果是Get之外的请求->必填
  //OBJECT:服务器返回数据类型自动转换成对象(方便调用)
  // STRING:服务器返回的数据是一个字符串
  expectDataType:http.HttpDataType.OBJECT, // 必填
  // 设置请求头为json格式
  header:{  // 选填:Get请求有参数时+POST+PUT请求必填,其他选填
    'Content-Type':'application/json'
  },
  // 用来给Get,POST,PUT携带参数用
  // Get请求:  url?key=value
  // POST,PUT请求:在请求体中携带
  // 选填:Get请求有参数时+POST+PUT请求必填,其他选填
  extraData:{
    // 'key':'value'  //具体值来自接口文档
    pname:'湖南省'
  }
})
  .then((res:http.HttpResponse)=>{
    // 成功响应
    console.log(JSON.stringify(res.result))
  })
  .catch((err:Error)=>{
    // 失败处理
    console.log(JSON.stringify(err))
  })

注意:

  1. 预览器 和 模拟器 以及真机都可以发送请求
    1. 预览器无需配置网络权限即可成功发送请求
    2. 【模拟器】和【真机】需要配置网络权限才能成功发送请求

  1. 配置权限 -> 详细权限设置,可以参考 声明权限 权限列表
    1. 需要在项目的src/main/module.json5(模块配置)下添加如下代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ]
  }
}

2.1. Get请求演示

获取一条随机笑话:https://api-vue-base.itheima.net/api/joke

import { http } from '@kit.NetworkKit'

@Entry
@Component
struct Notebook_use {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(20)
        Button('看笑话')
          .onClick(() => {
            const req = http.createHttp()
            req.request('https://api-vue-base.itheima.net/api/joke',{
              method: http.RequestMethod.GET,
              expectDataType:http.HttpDataType.STRING
            })
              .then((res: http.HttpResponse) => {
                // AlertDialog.show({ message: JSON.stringify(res,null,2) })
                this.message = res.result.toString()
              })
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

获取新闻列表数据:https://hmajax.itheima.net/api/news

import { http } from '@kit.NetworkKit'

interface NewsResponse {
  message: string
  data: News[]
}

interface News {
  id: number
  title: string
  source: string
  cmtcount: number
  img: string
  time: string
}

@Entry
@Component
struct Day01_02_URL {
  @State data: NewsResponse | null = null

  build() {
    Column() {

      Button('获取新闻列表数据')
        .onClick(() => {
          //   1. 创建请求对象
          const req = http.createHttp()
          //   2. 发送请求获取服务器数据
          //   http://hmajax.itheima.net/api/news
          req.request('http://hmajax.itheima.net/api/news', {
            expectDataType: http.HttpDataType.OBJECT
          })
            .then(res => {
              console.log(JSON.stringify(res.result))
              this.data = res.result as NewsResponse
            })
        })
      // Text(JSON.stringify(this.data,null,2))
    }
    .height('100%')
    .width('100%')
  }
}

地址:http://hmajax.itheima.net/api/city

说明获取某个省所有的城市查询

参数名:pname

说明: 传递省份或直辖市名,比如 北京、广东省…

import { http } from '@kit.NetworkKit'

@Entry
@Component
struct Index {
  build() {
    Column() {
      Button('获取城市信息')
        .onClick(() => {
          const req = http.createHttp()
          req.request('http://hmajax.itheima.net/api/city', {
            expectDataType:http.HttpDataType.OBJECT,
            extraData:{
              pname:'广东省'
            }
          })
            .then(res=>{
              console.log(JSON.stringify(res.result))
            })
            .catch((err:Error)=>{
              console.log(JSON.stringify(err,null,2))
            })
        })
    }
    .height('100%')
    .width('100%')
  }
}

2.2. POST请求演示

注册用户:接口文档


@Entry
@Component
struct Day01_05_SubmitData {
  @State username: string = ''
  @State password: string = ''

  build() {
    Column({ space: 15 }) {
      Text('请求方法和数据提交')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      // $$this.username 双向数据绑定
      TextInput({ text: $$this.username, placeholder: '用户名' })
      TextInput({ text: $$this.password, placeholder: '密码' })
        .type(InputType.Password)

      Button('注册')
        .width('100%')
        .onClick(() => {
         
      Button('登录')
        .width('100%')
        .onClick(() => {
         
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}
import http from '@ohos.net.http'

const req = http.createHttp()

@Entry
@Component
struct Day01_05_SubmitData {
  @State username: string = 'itheima522'
  @State password: string = '123456'

  build() {
    Column({ space: 15 }) {
      Text('请求方法和数据提交')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      TextInput({ text: $$this.username, placeholder: '用户名' })
      TextInput({ text: $$this.password, placeholder: '密码' })
        .type(InputType.Password)

      Button('注册')
        .width('100%')
        .onClick(() => {
          req.request('http://hmajax.itheima.net/api/register', {
            method: http.RequestMethod.POST,
            expectDataType:http.HttpDataType.OBJECT,
            header: {
              contentType: 'application/json'
            },
            extraData: {
              username: this.username,
              password: this.password
            }
          })
            .then(res => {
              AlertDialog.show({ message:JSON.stringify(res.result,null,2) })
            })
        })     
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

2.3. 模拟器和真机网络权限配置

预览器的网络请求无需配置网络请求权限

模拟器和真机必须要配置网络权限才能进行网络请求

如果没有配置网络请求权限,则在请求后会出现如下错误:

如何配置网络权限?

在项目中找到entry/src/main/module.json5 文件,在里面配置网络请求

权限列表

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ],
    "extensionAbilities": [
      {
        "name": "EntryBackupAbility",
        "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
        "type": "backup",
        "exported": false,
        "metadata": [
          {
            "name": "ohos.extension.backup",
            "resource": "$profile:backup_config"
          }
        ]
      }
    ]
  }
}

3. 案例-开心一笑

接下来完成一个案例,需求:

  1. 默认获取若干条笑话,并渲染到页面上
  2. 下拉刷新
  3. 触底加载更多
  4. 点击返回顶部

3.1. 前置知识

3.1.1. Refresh下拉刷新容器组件

作用:可以实现下拉刷新功能

@Entry
@Component
struct Index {
  // 1. 控制Refresh组件的下拉动画效果的,true:打开下拉刷新效果 false:关闭下拉刷新效果
  @State isrefreshing: boolean = false

  build() {
    Column() {
      Refresh({ refreshing: $$this.isrefreshing }) {
        List({ space: 5 }) {
          ForEach([1, 2, 3, 4, 5, 6, 7, 8], () => {
            ListItem() {
              Row() {
              }.height(100).width('100%').backgroundColor(Color.Pink)
            }
          })
        }
      }
      // 只要一下拉就会触发这个事件
      .onRefreshing(() => {
        // AlertDialog.show({ message: 'ok' })
        setTimeout(() => {
          this.isrefreshing = false // 关闭刷新
        }, 3000)

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

3.1.2. LoadingProgress正在加载中的动画图标

    LoadingProgress()
          .height(50)
          .color(Color.Red)

3.1.3. List的触底事件 .onReachEnd()

作用:可以实现List列表数据触底时加载

@Entry
@Component
struct Index {
  @State isLoding: boolean = false

  build() {
    Column() {

      List({ space: 5 }) {
        ForEach([1, 2, 3, 4, 5, 6, 7, 8], () => {
          ListItem() {
            Row() {
            }.height(100).width('100%').backgroundColor(Color.Pink)
          }
        })

        // 加一个正在加载中的动画
        ListItem() {
          LoadingProgress()
            .height(50)
        }.width('100%')
      }
      // onReachEnd当List的滚动条滚动到底部的时候会触发
      // ✨✨问题:鼠标一抖动触发了
      /* 解决方案:设置一个状态变量初始值为false,
       在onReachEnd中加一个if判断 -> 方法体里面将这个变量的值设置为true,
       直到服务器返回了数据后,
       重置这个变量的值为false

       ✨✨onReachEnd特点:页面加载的时候从服务器获取到数据之后会自动触发
       */
      .onReachEnd(() => {
        // 我们可以发请求拿新数据
        if (this.isLoding == false) {
          this.isLoding = true

          setTimeout(() => {
            AlertDialog.show({ message: '触底加载' })
            this.isLoding = false
          }, 3000)
        }
      })
    }
    .height('100%')
    .width('100%')
  }
}

3.2. 基本结构

/**
 * 1. 默认加载
 * 2. 下拉刷新
 * 3. 触底加载更多
 * 4. 点击返回顶部
 * */
@Entry
@Component
struct Day01_07_Jokes {
  @State jokes: string [] = ['笑话 1']

  build() {
      Column() {
        // 顶部
        this.HeaderBuilder()
        // 笑话列表
        List({ space: 10 }) {
          ForEach(this.jokes, (joke: string) => {
            ListItem() {
              Column({ space: 10 }) {
                Text('笑话标题')
                  .fontSize(20)
                  .fontWeight(600)
                Row({ space: 15 }) {
                  titleIcon({ icon: $r('app.media.ic_public_time'), info: '2024-1-1' })
                  titleIcon({ icon: $r('app.media.ic_public_read'), info: '阅读(6666)' })
                  titleIcon({ icon: $r('app.media.ic_public_comments'), info: '评论(123)' })
                }

                Text(joke)
                  .fontSize(15)
                  .fontColor(Color.Gray)
              }
              .width('100%')
              .alignItems(HorizontalAlign.Start)
              .padding(20)

            }
            .borderRadius(10)
            .backgroundColor(Color.White)
            .shadow({ radius: 2, color: Color.Gray })
          })

        }
        .padding(10)
        .layoutWeight(1)

      }
      .width('100%')
      .height('100%')
      .backgroundColor('#f6f6f6')
  }

  @Builder
  HeaderBuilder() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .width(25);

      Image($r('app.media.ic_public_joke_logo'))
        .width(30)

      Image($r('app.media.ic_public_search'))
        .width(30);
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .height(60)
    .padding(10)
    .border({ width: { bottom: 2 }, color: '#f0f0f0' })
    .backgroundColor(Color.White)
  }
}

@Component
struct titleIcon {
  icon: ResourceStr = ''
  info: string = ''

  build() {
    Row() {
      Image(this.icon)
        .width(15)
        .fillColor(Color.Gray)
      Text(this.info)
        .fontSize(14)
        .fontColor(Color.Gray)
    }
  }
}

3.3. 默认获取若干条笑话

核心步骤:

  1. 生命周期函数中获取数据
    1. aboutToAppear
    2. http 模块获取笑话,若干条

请求地址:https://api-vue-base.itheima.net/api/joke/list

参数:num

完整url:https://api-vue-base.itheima.net/api/joke/list?num=获取条数

  1. 将获取到的数据转换格式并渲染
    1. as 类型

import { http } from '@kit.NetworkKit'

export interface iItem {
  code: number;
  data: string[];
  msg: string;
}

/**
 * 1. 默认加载 -> 加载五条数据 -> aboutToAppear() -> getList()
 * 2. 下拉刷新
 * 3. 触底加载更多
 * 4. 点击返回顶部
 * */
@Entry
@Component
struct Day01_07_Jokes {
  @State jokes: string [] = ['笑话 1']

  aboutToAppear(): void {
    this.getList()
  }

  // 请求笑话数据
  async getList() {
    const req = http.createHttp()

    try {
      const res = await req.request('https://api-vue-base.itheima.net/api/joke/list', {
        expectDataType: http.HttpDataType.OBJECT,
        extraData: {
          num: '5'
        }
      })

      // 将res.reslut这个Object对象as成真正的接口类型,才能使用里面的属性
      const obj = res.result as iItem
      this.jokes = obj.data

      // AlertDialog.show({ message: JSON.stringify(obj.data, null, 2) })
    } catch (err) {
      AlertDialog.show({ message: '网络错误' })
    }
  }

  build() {
    Column() {
      // 顶部
      this.HeaderBuilder()
      // 笑话列表
      List({ space: 10 }) {
        ForEach(this.jokes, (joke: string) => {
          ListItem() {
            Column({ space: 10 }) {
              Text('笑话标题')
                .fontSize(20)
                .fontWeight(600)
              Row({ space: 15 }) {
                titleIcon({ icon: $r('app.media.ic_public_time'), info: '2024-1-1' })
                titleIcon({ icon: $r('app.media.ic_public_read'), info: '阅读(6666)' })
                titleIcon({ icon: $r('app.media.ic_public_comments'), info: '评论(123)' })
              }

              Text(joke)
                .fontSize(15)
                .fontColor(Color.Gray)
            }
            .width('100%')
            .alignItems(HorizontalAlign.Start)
            .padding(20)

          }
          .borderRadius(10)
          .backgroundColor(Color.White)
          .shadow({ radius: 2, color: Color.Gray })
        })

      }
      .padding(10)
      .layoutWeight(1)

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f6f6f6')
  }

  @Builder
  HeaderBuilder() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .width(25);

      Image($r('app.media.ic_public_joke_logo'))
        .width(30)

      Image($r('app.media.ic_public_search'))
        .width(30);
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .height(60)
    .padding(10)
    .border({ width: { bottom: 2 }, color: '#f0f0f0' })
    .backgroundColor(Color.White)
  }
}

@Component
struct titleIcon {
  icon: ResourceStr = ''
  info: string = ''

  build() {
    Row() {
      Image(this.icon)
        .width(15)
        .fillColor(Color.Gray)
      Text(this.info)
        .fontSize(14)
        .fontColor(Color.Gray)
    }
  }
}

3.4. 下拉刷新

核心步骤:

  1. 通过 Refresh组件实现下拉效果
  2. 在 onRefreshing 中重新获取数据替换默认值即可
  3. 抽取获取笑话的方法,在 2 个地方调用即可
    1. aboutToAppear
    2. onRefreshing
      1. 延迟关闭下拉刷新的效果

/**
 * 1. 默认加载
 * 2. 下拉刷新
 * 3. 触底加载更多
 * 4. 点击返回顶部
 * */
import { http } from '@kit.NetworkKit'

interface iData {
  code: number
  msg: string
  data: string[]
}

@Entry
@Component
struct Day01_07_Jokes {
  @State jokes: string [] = ['笑话 1']
  @State isrefreshing:boolean = false

  // 1. 页面渲染完成后调用声明周期方法
  aboutToAppear(): void {
    this.getList()
      .then(res => {
        let objData: iData = JSON.parse(res.result.toString())
        this.jokes = objData.data
      })
  }

  // 获取笑话数据
  getList() {
    //  请求服务器数据
    const req = http.createHttp()
    // 返回对象
    return req.request('https://api-vue-base.itheima.net/api/joke/list?num=5')
  }

  build() {
    Column() {
      // 顶部
      this.HeaderBuilder()

      // 笑话列表
      Refresh({refreshing:$$this.isrefreshing}) {
        List({ space: 10 }) {
          ForEach(this.jokes, (joke: string) => {
            ListItem() {
              Column({ space: 10 }) {
                Text(joke.slice(0, 10))// 从字符串中截取前若干个文字
                  .fontSize(20)
                  .fontWeight(600)
                Row({ space: 15 }) {
                  titleIcon({ icon: $r('app.media.ic_public_time'), info: '2024-1-1' })
                  titleIcon({ icon: $r('app.media.ic_public_read'), info: '阅读(6666)' })
                  titleIcon({ icon: $r('app.media.ic_public_comments'), info: '评论(123)' })
                }

                Text(joke)
                  .fontSize(15)
                  .fontColor(Color.Gray)
              }
              .width('100%')
              .alignItems(HorizontalAlign.Start)
              .padding(20)

            }
            .borderRadius(10)
            .backgroundColor(Color.White)
            .shadow({ radius: 2, color: Color.Gray })
          })

        }
        .padding(10)
        .layoutWeight(1)
      }
      .onRefreshing(()=>{
        // 重新请求服务器获取新数据
        this.getList()
          .then(res=>{
            // 刷新列表
            let objData: iData = JSON.parse(res.result.toString())
            this.jokes = objData.data

            //服务器返回数据后,关闭下拉加载功能
            this.isrefreshing = false 
          })
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f6f6f6')
  }

  @Builder
  HeaderBuilder() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .width(25);

      Image($r('app.media.ic_public_joke_logo'))
        .width(30)

      Image($r('app.media.ic_public_search'))
        .width(30);
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .height(60)
    .padding(10)
    .border({ width: { bottom: 2 }, color: '#f0f0f0' })
    .backgroundColor(Color.White)
  }
}

@Component
struct titleIcon {
  icon: ResourceStr = ''
  info: string = ''

  build() {
    Row() {
      Image(this.icon)
        .width(15)
        .fillColor(Color.Gray)
      Text(this.info)
        .fontSize(14)
        .fontColor(Color.Gray)
    }
  }
}

3.5. 触底加载更多

核心步骤:

  1. 在 onReachEnd 事件中加载更多数据,list 组件
    1. 重新获取数据
    2. 和本地的合并到一起
    3. this.jokes.push(...['笑话 1',’笑话 2‘])
  1. 获取到的数据和本地数据合并
/**
 * 1. 默认加载
 * 2. 下拉刷新
 * 3. 触底加载更多
 * 4. 点击返回顶部
 * */
import { http } from '@kit.NetworkKit'

interface iData {
  code: number
  msg: string
  data: string[]
}

@Entry
@Component
struct Day01_07_Jokes {
  @State jokes: string [] = []
  @State isrefreshing: boolean = false
  @State isLoadMore: boolean = false //控制触底加载

  // 1. 页面渲染完成后调用声明周期方法
  aboutToAppear(): void {
    // this.getList()
    //   .then(res => {
    //     let objData: iData = JSON.parse(res.result.toString())
    //     this.jokes = objData.data
    //   })
  }

  // 获取笑话数据
  getList() {
    //  请求服务器数据
    const req = http.createHttp()
    // 返回对象
    return req.request('https://api-vue-base.itheima.net/api/joke/list?num=5')
  }

  build() {
    Column() {
      // 顶部
      this.HeaderBuilder()

      // 笑话列表
      Refresh({ refreshing: $$this.isrefreshing }) {
        List({ space: 10 }) {
          ForEach(this.jokes, (joke: string) => {
            ListItem() {
              Column({ space: 10 }) {
                Text(joke.slice(0, 10))// 从字符串中截取前若干个文字
                  .fontSize(20)
                  .fontWeight(600)
                Row({ space: 15 }) {
                  titleIcon({ icon: $r('app.media.ic_public_time'), info: '2024-1-1' })
                  titleIcon({ icon: $r('app.media.ic_public_read'), info: '阅读(6666)' })
                  titleIcon({ icon: $r('app.media.ic_public_comments'), info: '评论(123)' })
                }

                Text(joke)
                  .fontSize(15)
                  .fontColor(Color.Gray)
              }
              .width('100%')
              .alignItems(HorizontalAlign.Start)
              .padding(20)

            }
            .borderRadius(10)
            .backgroundColor(Color.White)
            .shadow({ radius: 2, color: Color.Gray })
          })

          // TODO:触底加载更多的动画提示
          ListItem() {
            LoadingProgress()
              .height(50)
          }
          .width('100%')
        }
        .onReachEnd(() => {
          if (this.isLoadMore == false) {
            this.isLoadMore = true
            //  发送新的请求获取新数据新数据应该是追加到this.jokes的后面-> push
            this.getList()
              .then(res => {
                let objData: iData = JSON.parse(res.result.toString())
                // 将服务器新返回的数据展开之后追加原数组的后边
                this.jokes.push(...objData.data)

                this.isLoadMore = false
              })
          }
        })
        .padding(10)
        .layoutWeight(1)
      }
      .onRefreshing(() => {
        // 请求服务器获取新数据
        this.getList()
          .then(res => {
            let objData: iData = JSON.parse(res.result.toString())
            this.jokes = objData.data

            //等服务器返回数据后关闭下拉加载功能
            this.isrefreshing = false
          })

      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f6f6f6')
  }

  @Builder
  HeaderBuilder() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .width(25);

      Image($r('app.media.ic_public_joke_logo'))
        .width(30)

      Image($r('app.media.ic_public_search'))
        .width(30);
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .height(60)
    .padding(10)
    .border({ width: { bottom: 2 }, color: '#f0f0f0' })
    .backgroundColor(Color.White)
  }
}

@Component
struct titleIcon {
  icon: ResourceStr = ''
  info: string = ''

  build() {
    Row() {
      Image(this.icon)
        .width(15)
        .fillColor(Color.Gray)
      Text(this.info)
        .fontSize(14)
        .fontColor(Color.Gray)
    }
  }
}

3.6. 点击【顶部栏】返回顶部

核心步骤:

  1. 【顶部栏】点击事件中通过 Scroller控制器 scrollEdge 方法滚到顶部
    1. 通过控制器的方法,滚到顶部即可
/**
 * 1. 默认加载
 * 2. 下拉刷新
 * 3. 触底加载更多
 * 4. 点击返回顶部
 * */
import { http } from '@kit.NetworkKit'

interface iData {
  code: number
  msg: string
  data: string[]
}

@Entry
@Component
struct Day01_07_Jokes {
  @State jokes: string [] = []
  @State isrefreshing: boolean = false
  @State isLoadMore: boolean = false //控制触底加载
  listScroller = new ListScroller()

  // 1. 页面渲染完成后调用声明周期方法
  aboutToAppear(): void {
    // this.getList()
    //   .then(res => {
    //     let objData: iData = JSON.parse(res.result.toString())
    //     this.jokes = objData.data
    //   })
  }

  // 获取笑话数据
  getList() {
    //  请求服务器数据
    const req = http.createHttp()
    // 返回对象
    return req.request('https://api-vue-base.itheima.net/api/joke/list?num=5')
  }

  build() {
    Column() {
      // 顶部
      this.HeaderBuilder()

      // 笑话列表
      Refresh({ refreshing: $$this.isrefreshing }) {
        List({ space: 10, scroller: this.listScroller }) {
          ForEach(this.jokes, (joke: string) => {
            ListItem() {
              Column({ space: 10 }) {
                Text(joke.slice(0, 10))// 从字符串中截取前若干个文字
                  .fontSize(20)
                  .fontWeight(600)
                Row({ space: 15 }) {
                  titleIcon({ icon: $r('app.media.ic_public_time'), info: '2024-1-1' })
                  titleIcon({ icon: $r('app.media.ic_public_read'), info: '阅读(6666)' })
                  titleIcon({ icon: $r('app.media.ic_public_comments'), info: '评论(123)' })
                }

                Text(joke)
                  .fontSize(15)
                  .fontColor(Color.Gray)
              }
              .width('100%')
              .alignItems(HorizontalAlign.Start)
              .padding(20)

            }
            .borderRadius(10)
            .backgroundColor(Color.White)
            .shadow({ radius: 2, color: Color.Gray })
          })

          // TODO:触底加载更多的动画提示
          ListItem() {
            LoadingProgress()
              .height(50)
          }
          .width('100%')
        }
        .onReachEnd(() => {
          if (this.isLoadMore == false) {
            this.isLoadMore = true
            //  发送新的请求获取新数据新数据应该是追加到this.jokes的后面-> push
            this.getList()
              .then(res => {
                let objData: iData = JSON.parse(res.result.toString())
                // 将服务器新返回的数据展开之后追加原数组的后边
                this.jokes.push(...objData.data)

                this.isLoadMore = false
              })
          }
        })
        .padding(10)
        .layoutWeight(1)
      }
      .onRefreshing(() => {
        // 请求服务器获取新数据
        this.getList()
          .then(res => {
            let objData: iData = JSON.parse(res.result.toString())
            this.jokes = objData.data

            //等服务器返回数据后关闭下拉加载功能
            this.isrefreshing = false
          })

      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f6f6f6')
  }

  @Builder
  HeaderBuilder() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .width(25);

      Image($r('app.media.ic_public_joke_logo'))
        .width(30)

      Image($r('app.media.ic_public_search'))
        .width(30);
    }
    .onClick(() => {
      //   点击滚动到顶部
      this.listScroller.scrollEdge(Edge.Top)
    })
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .height(60)
    .padding(10)
    .border({ width: { bottom: 2 }, color: '#f0f0f0' })
    .backgroundColor(Color.White)
  }
}

@Component
struct titleIcon {
  icon: ResourceStr = ''
  info: string = ''

  build() {
    Row() {
      Image(this.icon)
        .width(15)
        .fillColor(Color.Gray)
      Text(this.info)
        .fontSize(14)
        .fontColor(Color.Gray)
    }
  }
}

4. 请求库-axios

除了原生的请求库http以外,还可以使用 第三方请求库axios来进行网络请求,为我们提供第二种http请求方式

axios文档传送门

4.1. 基本用法

4.1.1. 下载axios

作为一个第三方库,使用的时候需要先完成下包的操作

打开终端执行命令

# 安装
ohpm i @ohos/axios

# 卸载
ohpm uninstall @ohos/axios

ohpm 是一个包管理工具,用来管理鸿蒙提供的第三方模块

如果无法执行命令,或执行失败,可以使用如下方式来完成安装:

4.1.2. axios完成POST请求

基本语法格式:

// 1. 导入axios
// AxiosError:异常时的数据类型
// 正常时的数据类型AxiosResponse 是一个泛型类型
import axios, { AxiosError, AxiosResponse } from '@ohos/axios'

// 2. 创建axios的对象实例
const req = axios.create()

// 3. 发送POST请求,并提交数据给服务器
const res:AxiosResponse<响应数据类型> = 
  await req<响应数据类型(可以写null), AxiosResponse<响应数据类型>, 请求体数据类型>({
    method: 'POST',  // 请求方法
    url: 'https://hmajax.itheima.net/api/login',  //服务器url地址
    data: { // 请求体数据
      username: '黑马no1hello',
      password: '123456'
    }
  })

登录接口传送门

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

// 1. 利用axios.create创建请求对象
const reqeust = axios.create({
  baseURL: 'https://hmajax.itheima.net/'
})

export interface iRes {
  /**
   * 业务状态码, 10000成功, 10004-账号/密码未携带
   */
  code: number;

  /**
   * 响应数据
   */
  data: object;

  /**
   * 响应消息
   */
  message: string;
}

export interface iBody {
  /**
   * 密码, 最少6位
   */
  password: string;

  /**
   * 用户名, 最少8位,中英文和数字组成,
   */
  username: string;
}


@Entry
@Component
struct Index {
  build() {
    Column() {
      Button('post请求测试')
        .onClick(async () => {
          //   2. 发起请求
          try {
            const res: AxiosResponse<iRes> = await reqeust<null, AxiosResponse<iRes>, iBody>({
              method: 'POST',
              url: 'api/login',
              data: {
                username: '黑马no1hello',
                password: '123456'
              }
            })

            AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })
          } catch (err) {
            // 将err异常对象强制转换成 AxiosError
            // response就是异常的响应对象
            const error: AxiosError = err as AxiosError
            AlertDialog.show({ message: JSON.stringify(error.response, null, 2) })
          }
        })
    }
    .height('100%')
    .width('100%')
    .backgroundColor(Color.Pink)

  }
}
  1. 请求体有数据提交时,泛型参数 3,需要设置为【请求体】的格式,可以从 apifox 直接c+v

4.1.3. axios完成GET请求

一级商品(无参数) : 接口文档传送门

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

export interface ApifoxModel {
  /**
   * 响应数据
   */
  data: Datum[];

  /**
   * 响应消息
   */
  message: string;
}

export interface Datum {
  /**
   * 顶级分类id
   */
  id: string;

  /**
   * 顶级分类名字
   */
  name: string;

  /**
   * 顶级分类图片
   */
  picture: string;
}

@Entry
@Component
struct Index {
  build() {
    Column() {

      Button('axios.get')
        .onClick(async () => {
          // 1. 创建请求实例
          const req = axios.create()

          // 2. async 和 await方式发送get请求(不带参数)
          try{
            let res: AxiosResponse<ApifoxModel> = await req({
              method: 'get',
              url: 'https://hmajax.itheima.net/api/category/top'
            })
            AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })
          } catch (err) {
            let errObj: AxiosError = err
            AlertDialog.show({ message: '出错了:' + JSON.stringify(errObj.message, null, 2) })
          }

          // 3. .then().catch()方式发送请求(不带参数)
          // req.request({
          //   method: 'get',
          //   url: 'https://hmajax.itheima.net/api/category/top1'
          // })
          //   .then((res: AxiosResponse<ApifoxModel>) => {
          //     AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })
          //   })
          //   .catch((err: AxiosError) => {
          //     AlertDialog.show({ message: '出错了:' + JSON.stringify(err, null, 2) })
          //   })

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

  }
}

get 请求传递的查询参数

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

export interface ApifoxModel {
  /**
   * 响应数据
   */
  data: Data;
  /**
   * 响应消息
   */
  message: string;
}

/**
 * 响应数据
 */
export interface Data {
  /**
   * 顶级分类下属二级分类数组
   */
  children: Child[];
  /**
   * 顶级分类id
   */
  id: string;
  /**
   * 顶级分类名字
   */
  name: string;
  /**
   * 顶级分类图片
   */
  picture: string;
}

export interface Child {
  /**
   * 二级分类id
   */
  id: string;
  /**
   * 二级分类名字
   */
  name: string;
  /**
   * 二级分类图片
   */
  picture: string;
}

@Entry
@Component
struct Index {
  build() {
    Column() {

      Button('axios.get')
        .onClick(async () => {
          // 1. 创建请求实例
          const req = axios.create()

          // 2. async 和 await方式发送get请求(带参数)
          try{
            let res: AxiosResponse<ApifoxModel> = await req({
              method: 'get',
              url: 'https://hmajax.itheima.net/api/category/sub',
              params:{
                id:'1005000'
              }
            })

            // 写法2:
          let res: AxiosResponse<ApifoxModel> = await req.get(
              'https://hmajax.itheima.net/api/category/sub', {
              params: {
                id: '1005000'
              }
            }
            )
            AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })
          } catch (err) {
            let errObj: AxiosError = err
            AlertDialog.show({ message: '出错了:' + JSON.stringify(errObj.message, null, 2) })
          }

          // 3. .then().catch()方式发送请求(带参数)
          // req.request({
          // method: 'get',
          // url: 'https://hmajax.itheima.net/api/category/sub',
          // params:{
          //   id:'1005000'
          // }
          // })
          //   .then((res: AxiosResponse<ApifoxModel>) => {
          //     AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })
          //   })
          //   .catch((err: AxiosError) => {
          //     AlertDialog.show({ message: '出错了:' + JSON.stringify(err, null, 2) })
          //   })

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

  }
}

4.2. axios基地址配置

我们可以使用自定义配置新建一个实例

// 基于配置,返回一个 axios 的实例
const req =  axios.create({
  // 基地址,后续请求的时候这部分可以省略
  baseURL:'https://hmajax.itheima.net'
})
// get 请求 直接写 url 即可
 let res: AxiosResponse<ApifoxModel> = await req({
  method: 'get',
  url: '/api/category/top'
})
AlertDialog.show({ message: JSON.stringify(res) })

import axios, { AxiosResponse } from '@ohos/axios'
const req = axios.create({
  // ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
  // 设置请求url的基地址,它的值一般填写后台数据接口服务器的域名
  // 注意点,最后要不要带 / 要看下边req.request()中的url中首字符是否有/
  // 如果有,不带,否则带
  //✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
  baseURL:'https://hmajax.itheima.net'
})

export interface iRes {
  /**
   * 响应数据
   */
  data: Datum[];

  /**
   * 响应消息
   */
  message: string;
}

export interface Datum {
  /**
   * 顶级分类id
   */
  id: string;

  /**
   * 顶级分类名字
   */
  name: string;

  /**
   * 顶级分类图片
   */
  picture: string;
}

@Entry
@Component
struct Index {
  build() {
    Column({ space: 100 }) {
      Button('axios发送不带参数的请求')
        .onClick(async () => {
          // const req = axios.create()

          try {
            // 此处编写代码
            let res: AxiosResponse<iRes> = await req({
              url: '/api/category/top',  //由于设置了基地址,所以此处只需要配置请求路径即可
              method: 'get', //默认值,可以省略
            })

            AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })

          } catch (err) {
            AlertDialog.show({ message: JSON.stringify(err, null, 2) })
          }

        })
      Button('axios发送带参数的请求').onClick(async () => {
        // const req = axios.create()

        try {
          // 此处编写代码
          let res: AxiosResponse<iRes> = await req({
            url: '/api/city',  //由于设置了基地址,所以此处只需要配置请求路径即可
            method: 'get', //默认值,可以省略
            // get请求的参数是通过固定的属性:params来进行携带
            // post请求的参数是固定通过:data来携带的
            params: {
              pname: '广东省'  // 使用axios中文参数值再内部回自动转成url编码,服务器能够正常响应数据
            }
          })

          AlertDialog.show({ message: JSON.stringify(res.data, null, 2) })

        } catch (err) {
          AlertDialog.show({ message: JSON.stringify(err, null, 2) })
        }
      })
    }
    .height('100%')
    .width('100%')
  }
}

注意:axios和内置的 http 模块在异常的处理上略有不同:

  1. 内置的 http 模块,http 状态码为异常时,不会响应为错误
  2. axios对于 http 状态码的错误(2xx 以外),会作为异常处理

5. 综合案例-我的书架-axios

图片素材

我的书架.zip

接口文档

接下来咱们使用 axios 来完成书架案例

5.1. 获取图书列表

5.1.1. 静态结构

@Entry
@Component
struct Index {
  // 1. 我的书架
  @Builder
  MyBook() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .height(20)
      Text('我的书架')
        .fontSize(20)
        .fontWeight(800)
      Image($r('app.media.ic_public_add'))
        .height(20)
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding(10)
    .border({ width: { bottom: 1 }, color: { bottom: 'rgba(0,0,0,0.2)' } })
  }

  // 2. 书籍列表
  @Builder
  BookList() {
    List() {
      ListItem() {
        // 布局
        Row({ space: 10 }) {
          Image($r('app.media.ic_public_cover'))
            .width(100)

          Column({ space: 25 }) {
            Column() {
              Text('书名:')
                .width('100%')
                .fontSize(20)
                .fontWeight(800)
              Text('作者:')
                .width('100%')
                .fontSize(14)
                .fontWeight(600)
                .fontColor('rgba(0,0,0,0.4)')
                .padding({ top: 5 })
            }

            Text('出版社:')
              .width('100%')
              .fontWeight(600)
              .fontColor('rgba(0,0,0,0.4)')
          }
          .layoutWeight(1)
        }
        .padding(10)
      }
      .swipeAction({
        end: this.delBuilder()
      })
    }
  }

  // 3. 删除书籍的构建函数
  @Builder
  delBuilder() {
    Column(){
      Text('删除')
        .backgroundColor(Color.Red)
        .fontColor(Color.White)
        .height('100%')
        .width(60)
        .textAlign(TextAlign.Center)
    }
    .padding(10)
  }

  build() {
    Column() {
      // 1. 我的书架
      this.MyBook()

      // 2. 书籍列表
      this.BookList()
    }
    .height('100%')
    .width('100%')
  }
}

5.1.2. 准备类型模块并导出

由于图书的增,改,列表,详情接口返回的数据对象是一样的。所以我们可以在这些业务中可以共用同一个interface类型,因此,需要进行模块封装并导出

export interface iBookResponse {
  /**
   * 响应数组
   */
  data: iBookInfo[];
  /**
   * 响应消息
   */
  message: string;
}


// 按需导出
export interface iBookInfo {
  /**
   * 图书作者
   */
  author: string;
  /**
   * 图书名字
   */
  bookname: string;
  /**
   * 图书id
   */
  id: number;
  /**
   * 图书出版社
   */
  publisher: string;
}

5.1.3. 获取图书数据并渲染

import axios, { AxiosResponse } from '@ohos/axios'
import { iBookInfo, iBookResponse } from './models'
import { router } from '@kit.ArkUI'

const req = axios.create()

@Entry
@Component
struct Index {
  @State bookList: iBookInfo[] = []

  aboutToAppear(): void {
    this.getBookList()
  }

  async getBookList() {
    try {
      let res: AxiosResponse<iBookResponse> =
        await req.request({
          url: 'https://hmajax.itheima.net/api/books',
          params:{
            creator:'ivan'
          }
        })

      this.bookList = res.data.data

    } catch (err) {
     AlertDialog.show({message:JSON.stringify(err,null,2)})
    }
  }

  build() {
    Column() {
      // 1. 我的书架
      this.MyBook()

      // 2. 书籍列表
      this.BookList()
    }
    .height('100%')
    .width('100%')
  }

  // 1. 我的书架
  @Builder
  MyBook() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .height(20)
      Text('我的书架')
        .fontSize(20)
        .fontWeight(800)
      Image($r('app.media.ic_public_add'))
        .height(20)
        .onClick(()=>{
          router.pushUrl({url:'pages/axiosbooks/addBook'})
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding(10)
    .border({ width: { bottom: 1 }, color: { bottom: 'rgba(0,0,0,0.2)' } })
  }

  // 2. 书籍列表
  @Builder
  BookList() {
    List() {
      ForEach(this.bookList, (item: iBookInfo) => {
        ListItem() {
          // 布局
          Row({ space: 10 }) {
            Image($r('app.media.ic_public_cover'))
              .width(100)

            Column({ space: 25 }) {
              Column() {
                Text('书名:'+item.bookname)
                  .width('100%')
                  .fontSize(20)
                  .fontWeight(800)
                Text('作者:'+item.author)
                  .width('100%')
                  .fontSize(14)
                  .fontWeight(600)
                  .fontColor('rgba(0,0,0,0.4)')
                  .padding({ top: 5 })
              }

              Text('出版社:')
                .width('100%')
                .fontWeight(600)
                .fontColor('rgba(0,0,0,0.4)')
            }
            .layoutWeight(1)
          }
          .padding(10)
        }
        .swipeAction({
          end: this.delBuilder()
        })
      })
    }
  }

  // 3. 删除书籍的构建函数
  @Builder
  delBuilder() {
    Column() {
      Text('删除')
        .backgroundColor(Color.Red)
        .fontColor(Color.White)
        .height('100%')
        .width(60)
        .textAlign(TextAlign.Center)
    }
    .padding(10)
  }
}

5.2. 新增图书

5.2.1. 静态结构

// 导入创建者
import { creator } from './models'

@Entry
@Component
struct addPage {
  @State bookname: string = ''
  @State author: string = ''
  @State publisher: string = ''

  build() {
    Navigation() {

      Column({ space: 10 }) {
        Row() {
          Text('图书名称:')
          TextInput({ placeholder: '请输入图书名字', text: $$this.bookname })
        }

        Row() {
          Text('图书作者:')
          TextInput({ placeholder: '请输入作者名字', text: $$this.author })
        }

        Row() {
          Text('图书出版社:')
          TextInput({ placeholder: '请输入出版社名字', text: $$this.publisher })
        }

        Button({ type: ButtonType.Normal }) {
          Text('保存')
            .fontColor(Color.White)
            .fontWeight(800)
        }
        .width('100%')
        .height(40)
        .borderRadius(8)

      }
      .height('100%')
      .width('100%')
      .padding(10)
    }
    .title('新增图书')
    .titleMode(NavigationTitleMode.Mini)
  }
}

5.2.2. 新增逻辑实现

// 导入创建者
import axios, { AxiosResponse } from '@ohos/axios'
import { creator, iBookInfoDetial } from './models'
import { promptAction, router } from '@kit.ArkUI';

// POST请求体数据类型
export interface iReqeustBody {
  /**
   * 新增图书作者
   */
  author: string;

  /**
   * 新增图书名字
   */
  bookname: string;

  /**
   * 新增图书创建者,自己的外号,和获取图书时的外号相同
   */
  creator: string;

  /**
   * 新增图书出版社
   */
  publisher: string;
}

@Entry
@Component
struct addPage {
  @State bookname: string = ''
  @State author: string = ''
  @State publisher: string = ''

  async addBook() {
    //  1. 非空验证 -> 轻提示用户
    if (this.bookname == '' || this.author == '' || this.publisher == '') {
      promptAction.showToast({ message: '图书名,作者,出版社均非空' })
      return //阻止下面代码继续执行
    }

    const req = axios.create()
    let res: AxiosResponse<iBookInfoDetial> = await req.request<null, AxiosResponse<iBookInfoDetial>, iReqeustBody>({
      method: 'POST',
      url: 'https://hmajax.itheima.net/api/books',
      data: {
        bookname: this.bookname,
        author: this.author,
        publisher: this.publisher,
        creator: creator
      }
    })
    promptAction.showToast({ message: res.data.message })
    router.back()
  }

  build() {
    Navigation() {

      Column({ space: 10 }) {
        Row() {
          Text('图书名称:')
          TextInput({ placeholder: '请输入图书名字', text: $$this.bookname })
        }

        Row() {
          Text('图书作者:')
          TextInput({ placeholder: '请输入作者名字', text: $$this.author })
        }

        Row() {
          Text('图书出版社:')
          TextInput({ placeholder: '请输入出版社名字', text: $$this.publisher })
        }

        Button({ type: ButtonType.Normal }) {
          Text('保存')
            .fontColor(Color.White)
            .fontWeight(800)
        }
        .width('100%')
        .height(40)
        .borderRadius(8)
        .onClick(() => {
          this.addBook()
        })

      }
      .height('100%')
      .width('100%')
      .padding(10)
    }
    .title('新增图书')
    .titleMode(NavigationTitleMode.Mini)
  }
}

5.3. 删除图书

import axios, { AxiosResponse } from '@ohos/axios'
import { iBookInfo, iBookResponse } from './models'
import { promptAction, router } from '@kit.ArkUI'

const req = axios.create()

@Entry
@Component
struct Index {
  @State bookList: iBookInfo[] = []

  onPageShow(): void {
    this.getBookList()
  }

  async getBookList() {
    try {
      let res: AxiosResponse<iBookResponse> =
        await req.request({
          url: 'https://hmajax.itheima.net/api/books',
          params: {
            creator: 'ivan'
          }
        })

      this.bookList = res.data.data

    } catch (err) {
      AlertDialog.show({ message: JSON.stringify(err, null, 2) })
    }
  }

  build() {
    Column() {
      // 1. 我的书架
      this.MyBook()

      // 2. 书籍列表
      this.BookList()
    }
    .height('100%')
    .width('100%')
  }

  // 1. 我的书架
  @Builder
  MyBook() {
    Row() {
      Image($r('app.media.ic_public_drawer_filled'))
        .height(20)
      Text('我的书架')
        .fontSize(20)
        .fontWeight(800)
      Image($r('app.media.ic_public_add'))
        .height(20)
        .onClick(() => {
          router.pushUrl({ url: 'pages/axiosbooks/addBook' })
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding(10)
    .border({ width: { bottom: 1 }, color: { bottom: 'rgba(0,0,0,0.2)' } })
  }

  // 2. 书籍列表
  @Builder
  BookList() {
    List() {
      ForEach(this.bookList, (item: iBookInfo) => {
        ListItem() {
          // 布局
          Row({ space: 10 }) {
            Image($r('app.media.ic_public_cover'))
              .width(100)

            Column({ space: 25 }) {
              Column() {
                Text('书名:' + item.bookname)
                  .width('100%')
                  .fontSize(20)
                  .fontWeight(800)
                Text('作者:' + item.author)
                  .width('100%')
                  .fontSize(14)
                  .fontWeight(600)
                  .fontColor('rgba(0,0,0,0.4)')
                  .padding({ top: 5 })
              }

              Text('出版社:' + item.publisher)
                .width('100%')
                .fontWeight(600)
                .fontColor('rgba(0,0,0,0.4)')
            }
            .layoutWeight(1)
          }
          .padding(10)
          .onClick(() => {
            router.pushUrl({ url: 'pages/axiosbooks/editBook', params: { bookid: item.id } })
          })
        }
        .swipeAction({
          end: this.delBuilder(item.id)
        })
      })
    }
  }

  // 3. 删除书籍的构建函数
  @Builder
  delBuilder(id: number) {
    Column() {
      Text('删除')
        .backgroundColor(Color.Red)
        .fontColor(Color.White)
        .height('100%')
        .width(60)
        .textAlign(TextAlign.Center)
    }
    .padding(10)
    .onClick(() => {
      promptAction.showDialog({
        title: '删除提示',
        message: '确认删除图书吗?',
        buttons: [
          {
            text: '取消',
            color: '#000'
          },
          {
            text: '确认',
            color: '#ff27a1d7'
          },
        ]
      })
        .then(async res => {
          // res返回的内容:{index:0} 取消按钮被点击, {index:1} 确认按钮被点击
          // AlertDialog.show({ message: JSON.stringify(res, null, 2) })
          if (res.index == 1) {
            try {
              // 此处编写代码
              let res: AxiosResponse<iBookResponse> = await req.request({
                method: 'delete',
                url: 'https://hmajax.itheima.net/api/books/' + id
              })

              promptAction.showToast({ message: res.data.message })
              this.getBookList()

            } catch (err) {
              AlertDialog.show({ message: JSON.stringify(err, null, 2) })
            }
          }
        })
    })
  }
}

5.4. 编辑图书

5.4.1. 回显图书信息

// 导入创建者
import { creator, iBookInfoDetial } from './models'
import { router } from '@kit.ArkUI'
import axios, { AxiosResponse } from '@ohos/axios'

interface iRouterParams {
  bookid: number
}

const req = axios.create()

@Entry
@Component
struct addPage {
  @State bookname: string = ''
  @State author: string = ''
  @State publisher: string = ''
  bookid: number = 0

  aboutToAppear(): void {
    let routerObj = router.getParams() as iRouterParams
    this.bookid = routerObj.bookid

    this.loadBook()
  }

  // 1. 回显图书数据
  async loadBook() {
    try {
      // 此处编写代码
      let res: AxiosResponse<iBookInfoDetial> = await req.request({
        url: 'https://hmajax.itheima.net/api/books/' + this.bookid
      })

      this.bookname = res.data.data.bookname
      this.author = res.data.data.author
      this.publisher = res.data.data.publisher

    } catch (err) {
      console.error('err--->', JSON.stringify(err).slice(0,800))
    }
  }

  build() {
    Navigation() {

      Column({ space: 10 }) {
        Row() {
          Text('图书名称:')
          TextInput({ placeholder: '请输入图书名字', text: $$this.bookname })
        }

        Row() {
          Text('图书作者:')
          TextInput({ placeholder: '请输入作者名字', text: $$this.author })
        }

        Row() {
          Text('图书出版社:')
          TextInput({ placeholder: '请输入出版社名字', text: $$this.publisher })
        }

        Button({ type: ButtonType.Normal }) {
          Text('保存')
            .fontColor(Color.White)
            .fontWeight(800)
        }
        .width('100%')
        .height(40)
        .borderRadius(8)

      }
      .height('100%')
      .width('100%')
      .padding(10)
    }
    .title('编辑  图书')
    .titleMode(NavigationTitleMode.Mini)
  }
}

5.4.2. 修改图书信息

// 导入创建者
import { creator, iBookInfoDetial } from './models'
import { promptAction, router } from '@kit.ArkUI'
import axios, { AxiosResponse } from '@ohos/axios'
import { iReqeustBody } from './addBook'

interface iRouterParams {
  bookid: number
}

const req = axios.create()

@Entry
@Component
struct addPage {
  @State bookname: string = ''
  @State author: string = ''
  @State publisher: string = ''
  bookid: number = 0

  aboutToAppear(): void {
    let routerObj = router.getParams() as iRouterParams
    this.bookid = routerObj.bookid

    this.loadBook()
  }

  // 1. 回显图书数据
  async loadBook() {
    try {
      // 此处编写代码
      let res: AxiosResponse<iBookInfoDetial> = await req.request({
        url: 'https://hmajax.itheima.net/api/books/' + this.bookid
      })

      this.bookname = res.data.data.bookname
      this.author = res.data.data.author
      this.publisher = res.data.data.publisher

    } catch (err) {
      console.error('err--->', JSON.stringify(err).slice(0, 800))
    }
  }

  // 2. 修改图书
  async saveBook() {
    try {
      // 此处编写代码
      let res: AxiosResponse<iBookInfoDetial> =
        await req.request<null, AxiosResponse<iBookInfoDetial>, iReqeustBody>({
          method: 'put',
          url: 'https://hmajax.itheima.net/api/books/' + this.bookid,
          data: {
            bookname: this.bookname,
            author: this.author,
            publisher: this.publisher,
            creator: creator
          }
        })

      promptAction.showToast({ message: res.data.message })
      router.back()

    } catch (err) {
      // console.log('编辑图书失败--->', JSON.stringify(err).slice(0, 800))
      AlertDialog.show({message:JSON.stringify(err,null,2)})
    }
  }

  build() {
    Navigation() {

      Column({ space: 10 }) {
        Row() {
          Text('图书名称:')
          TextInput({ placeholder: '请输入图书名字', text: $$this.bookname })
        }

        Row() {
          Text('图书作者:')
          TextInput({ placeholder: '请输入作者名字', text: $$this.author })
        }

        Row() {
          Text('图书出版社:')
          TextInput({ placeholder: '请输入出版社名字', text: $$this.publisher })
        }

        Button({ type: ButtonType.Normal }) {
          Text('保存')
            .fontColor(Color.White)
            .fontWeight(800)
        }
        .width('100%')
        .height(40)
        .borderRadius(8)
        .onClick(()=>{
          this.saveBook()
        })

      }
      .height('100%')
      .width('100%')
      .padding(10)
    }
    .title('编辑  图书')
    .titleMode(NavigationTitleMode.Mini)
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VSCode轻松调试运行.Net 8.0 Web API项目

1.背景 我一直都是用VS来开发.NetCore项目的&#xff0c;用的比较顺手&#xff0c;也习惯了。看其他技术文章有介绍VS Code更轻量&#xff0c;更方便。所以我专门花时间来使用VS Code&#xff0c;看看它是如何调试代码、如何运行.Net 8.0 WebAPI项目。这篇文章是一个记录的过程…

2024年国赛高教杯数学建模D题反潜航空深弹命中概率问题解题全过程文档及程序

2024年国赛高教杯数学建模 D题 反潜航空深弹命中概率问题 原题再现 应用深水炸弹&#xff08;简称深弹&#xff09;反潜&#xff0c;曾是二战时期反潜的重要手段&#xff0c;而随着现代军事技术的发展&#xff0c;鱼雷已成为现代反潜作战的主要武器。但是&#xff0c;在海峡或…

obj离线加载(vue+threejs)+apk方式浏览

demo需求&#xff1a;移动端&#xff0c;实现obj本地离线浏览 结合需求&#xff0c;利用&#xff08;vue2threejs173&#xff09;进行obj的加载&#xff0c;然后采用apk方式&#xff08;hbuilderX打包发布&#xff09;移动端浏览&#xff1b; https://github.com/bianbian886/…

关于mysql 表中字段存储JSON对象对JSON对象中的bolean字段进行查询的方式

业务场景如题 JSON对象为 表为客诉表中的 发现利用原有的xml中的 and a1.order_list ->‘$[*].isZg’ request.isZg 后续发现需要更改为有效 本文作为自己日常工作记录用&#xff0c;有遇到相同问题的可以作为参考。

Kylin麒麟操作系统 | 系统监控

以下所使用的环境为&#xff1a; 虚拟化软件&#xff1a;VMware Workstation 17 Pro 麒麟系统版本&#xff1a;Kylin-Server-V10-SP3-2403-Release-20240426-x86_64 一、系统状态查询工具 1. 静态显示系统进程信息ps ps命令会生成一个静态列表&#xff0c;列表中显示的进程其…

vLLM服务设置开机自启动(Linux)

要在开机时进入指定的 conda 环境并启动此 vllm 服务&#xff0c;您可以通过以下步骤设置一个 systemd 服务来自动执行脚本。 一、第一步&#xff1a;创建一个启动脚本 1.打开终端并创建启动脚本&#xff0c;例如 /home/username/start_vllm.sh&#xff08;请替换 username 为…

几个api

几个api 原型链 可以阅读此文 Function instanceof Object // true Object instanceof Function // true Object.prototype.isPrototypeOf(Function) // true Function.prototype.isPrototypeOf(Object) // true Object.__proto__ Function.prototype // true Function.pro…

数字IC后端设计实现OCC(On-chip Clock Controller)电路介绍及时钟树综合案例

数字IC后端时钟树综合专题&#xff08;OCC电路案例分享&#xff09; 复杂时钟设计时钟树综合(clock tree synthesis)常见20个典型案例 1、什么是OCC&#xff1f; 片上时钟控制器(On-chip Clock Controllers &#xff0c;OCC)&#xff0c;也称为扫描时钟控制器(Scan Clock Con…

Trae根据原型设计稿生成微信小程序密码输入框的踩坑记录

一、需求描述 最近经常使用Trae生成一些小组件和功能代码&#xff08;对Trae赶兴趣的可以看之前的文章《TraeAi上手体验》&#xff09;&#xff0c;刚好在用uniapp开发微信小程序时需要开发一个输入密码的弹框组件&#xff0c;于是想用Trae来实现。原型设计稿如下&#xff1a;…

华为AP 4050DN-HD的FIT AP模式改为FAT AP,家用FAT基本配置

在某鱼买了两台华为AP 4050DN-HD , AP是二手的 , 在AC上上过线 , 所以就不能开机自选为FIP模式了 我没有AC无线控制器 , 就是买一个自己玩 , AP又是FIT瘦AP模式 ,所以我就想把AP的瘦AP模式改为FAT胖AP模式 1. 准备工作 1.1下载好对应软件&#xff0c;进入到 企业业务网站去下…

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局

文章目录 简介一、自定义背景图布局1.1 效果预览1.2 实现思路1.3 custom-page 组件全量代码1.4 页面使用 二、普通页面布局2.1 效果预览2.2 实现思路2.3 公共样式部分2.4 页面使用 三、分页表单页面布局3.1 效果预览3.2 实现思路3.3 页面代码 简介 开发工具&#xff1a;VsCode…

虚拟机缩放比例问题处理

上班打开虚拟机的样子。 最开始判断可能是vmtools 异常重启安装后发现没有效果 通过 xrandr 功能查询显示器信息获取显示器名 设置显示器 同时设置分辨率 也可以同时设置刷新率 注意下图中设置的关键字

【Python 入门基础】—— 人工智能“超级引擎”,AI界的“瑞士军刀”,

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

DeepSeek-R1-Zero:基于基础模型的强化学习

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 DeepSeek大模型技术系列四DeepSeek大模型技术系列四》DeepSeek-…

(dp 买入股票的最佳时机)leetcode 121

题目 题解的dp数组 0列是负数&#xff0c;这里我改成正数不再相加而是相减获取利润 class Solution { public:int maxProfit(vector<int>& prices) {int nprices.size();vector<vector<int>>dp(n,vector<int>(2));dp[0][0]prices[0];dp[0][1]0;//0…

由 Mybatis 源码畅谈软件设计(三):简单查询 SQL 执行流程

大家好&#xff0c;我是 方圆。SQL 查询是 Mybatis 中的核心流程&#xff0c;本节我们来介绍简单 SQL 的执行流程&#xff0c;过程会比较长&#xff0c;期间会认识很多重要的组件&#xff0c;比如 SqlSession、四大处理器&#xff08;Executor、StatementHandler、ParameterHan…

项目实践 之 pdf简历的解析和填充(若依+vue3)

文章目录 环境背景最终效果前端讲解左侧模块解析右侧上传模块解析前端步骤 后端讲解代码前端 环境背景 若依前后端分离框架 vue最后边附有代码哦 最终效果 前端讲解 左侧模块解析 1、左侧表单使用el-form 注意&#xff1a; 1、prop出现的字段&#xff0c;需要保证是该类所…

lowagie(itext)老版本手绘PDF,包含页码、水印、图片、复选框、复杂行列合并、行高设置等。

入口类&#xff1a;exportPdf package xcsy.qms.webapi.service;import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.common.utils.StringUtils; import com.ibm.icu.text.RuleBasedNumberFormat; import com.lowagie…

第002文-kali虚拟机安全与网络配置

1、kali系统介绍 kali是一个基于Linux kernel的操作系统&#xff0c;由BackTrack(简称BT)发展而来。BackTrack是2006年推出的一个用于渗透测试及黑客攻防的专用平台&#xff0c;基于Knoppix(linux的一个发行版)开发。BackTrack版本周期&#xff1a;2006年的起始版本BackTrack …

软件工程复试专业课-软件生命周期

文章目录 软件过程模型瀑布模型模型图特点优缺点改进后的瀑布模型 快速原型模型模型图优缺点 增量模型&#xff08;迭代-递增模型&#xff09;原型图与瀑布和快速原型的区别优缺点风险更大的增量模型 螺旋模型简介模型图优缺点 喷泉模型模型图优缺点 编码修补模型敏捷过程优缺点…