1. HTTP 数据请求

相关资源:

  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()
      1. 下拉刷新
      1. 触底加载更多
      1. 点击返回顶部
    • */
      @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. 默认加载
      1. 下拉刷新
      1. 触底加载更多
      1. 点击返回顶部
    • */
      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. 默认加载
      1. 下拉刷新
      1. 触底加载更多
      1. 点击返回顶部
    • */
      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. 默认加载
      1. 下拉刷新
      1. 触底加载更多
      1. 点击返回顶部
    • */
      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)
  }
}
相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax