鸿蒙【云端一体化】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
开发云函数调用云函数
在这里插入图片描述

开发云函数

新建项目和应用,开通云函数服务(AGC)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
开通云函数服务
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用端云一体化模板创建应用 (DevEco Studio)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

新建云函数(DevEcoStudio) 】

在这里插入图片描述

编写云函数代码(DevEco Studio)

在这里插入图片描述

部署云函数(DevEco Studio)

在这里插入图片描述

配置和测试(AGC)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

端侧调用云函数

添加依赖

在这里插入图片描述

初始化AGConnect

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

ArkTS 界面开发

在这里插入图片描述

ArkTS 调用云函数

云函数,要在真机或者模拟器才能用
在这里插入图片描述

云函数开发细节

传参问题

端侧输入姓名,云测根据传来的参数,进行处理,告诉端侧显示不同的欢迎词

端侧

云测
我们要知道端侧传过来什么,就要知道event对象里有什么
查文档
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了程序健壮,我们可以把先判断有没有body,没有怎么处理,或者捕获异常

环境变量

如果我有一些配置项,那么他们容易改变,那我不应该把它写死在程序代码之中,而是把这些配置项呢,给他以环境变量的方式提供
这样就能保证配置项将来改动了,代码不用动

env系统提供的和我们将来可以自定义环境变量
在这里插入图片描述

在这里插入图片描述
你可以点击这个新增变量来输入变量名变量值
一定要点击保存它才能生效啊

云函数如果它重新部署的话
那么这个上次配置的环境变量会被清空
所以呢我们先去改动一下云函数的代码,把它重新部署之后,然后咱们回过头来再去配置这个环境变量好

在这里插入图片描述

流量治理

负载均衡

云函数的一个好处就是它是按量计费,如果你对这个原函数啊并没有使用,那它是不收费的
云函数运行之前,它会把它部署到一个虚拟机实例上
我已经有一段时间没有去调用我的hello,他就把我那个实例销毁了这个时候是不计费的
那将来我对这个原函数一旦要产生调用,它才会动态的创建一个实例,并且把原函数部署上去,这时候才会产生费用啊
如果这个并发量比较大,那云函数这边它是如何去分配,请求到这每个实例的
这就涉及到了负载均衡

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

重试

云函数的调用是有可能超时的

在这里插入图片描述
那么如果你不希望这个函数执行一次就失败,你还想救一下它
我们可以通过流量治理中有一个重视的功能,在函数超时失败之后呢,可以重新发起一次对云函数的调用,如果这次调用能成功,那我们就不用返回失败的信息,还是可以返回成功的信息啊

在这里插入图片描述
重试功能呢其实它也分了三种策略

第一种重策略叫做zero
它的意思呢就是说一旦函数发现了它调用失败,那我就立刻发起重视,它中间不会等待

第二种从事策略叫做constant
当你的函数调用失败了之后,他不会说立刻去发起这个重试,他要间隔两秒之后再发起重试

第三种从策略呢叫做jittered
它其实跟我们前面第二种啊比较像,
都是在每次重试之间会有一个时间间隔,不过它的时间间隔不像我们constant,它的时间间隔是一个常量的时间值,它是一个变化的时间值
每次这个时间间隔是以指数方式增长的啊

zero
在这里插入图片描述
在这里插入图片描述

constant
在这里插入图片描述

在这里插入图片描述

jittered

在这里插入图片描述

熔断

就跟我们日常生活中的保险丝的作用有点像,保险丝呢它在大电流来了以后,保险丝是不是就烧断了
流量治理中的熔断,它的目的也是类似的,比如说我的云函数,它的调用很多次都出错了,那我继续提供原函数服务已经没有意义了
这时候呢我就采用熔断,熔断一旦发生,那么原函数呢就暂停对外的服务了,也就是这个原函数不可用了,
但是原函数不可用是避免了更大
比如说像雪崩事故的发生好

这是熔断的目的啊

在这里插入图片描述
它的配置项其实就这么三项
刚开始这个开关没有开的时候,那函数呢在任意时刻都可以对外提供服务
如果我们把这个熔断开关打开,那它就会在满足了某些条件之后触发熔断,一旦函数熔断发生,那么这个函数就不能对外提供服务了

在这里插入图片描述

认证服务

开通认证服务

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用认证组件进行认证

在这里插入图片描述
在这里插入图片描述
第一步登录页面
在这里插入图片描述
第二步,登录成功后
在这里插入图片描述
在这里插入图片描述
至此整个登录流程跑通了

在这里插入图片描述

认证流程(1) -上报认证数据

认证凭据,根据你的认证方式不同而不同
比如说呢我们之前用的是手机认证
那手机验证的话,当我点击登录按钮之后,是不是让你输入手机号和验证码,那这个手机号和验证码
就是我们手机认证方式下它的认证凭据
那如果你采用的是邮箱认证呢
那这个认证据就是你的邮件地址和
邮件里收到的认证码
在这里插入图片描述
第一次使用认证服务那服务器这边是不是应该还没有这个用户信息
服务器就创建你的服务信息

在这里插入图片描述

认证流程(2) -获取用户信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自行实现登录需求操作

在这里插入图片描述
在这里插入图片描述



@Entry
@Component
struct MyLoginCustom {

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
          Button('获取验证码')
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')

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

自行实现登录倒计时初步实现、在这里插入图片描述



@Entry
@Component
struct MyLoginCustom {
  @State countDown: number = 10
  intervalId: number = 0
  @State verifyCodeButtonText: string = '获取验证码'

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
          Button(this.verifyCodeButtonText)
            .onClick(()=> {
              this.intervalId = setInterval(() => {
                this.verifyCodeButtonText = `${this.countDown} s`
                if(this.countDown<0){
                  clearInterval(this.intervalId)
                  this.countDown = 10
                  this.intervalId = 0
                  this.verifyCodeButtonText = '获取验证码'
                  return
                }
                this.countDown--
              },1000)
            })
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')

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

自行实现登录倒计时细节调整



@Entry
@Component
struct MyLoginCustom {
  @State countDown: number = 10
  intervalId: number = 0
  @State verifyCodeButtonText: string = '获取验证码'
  @State verifyCodeButtonEnable: boolean = true

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
          Button(this.verifyCodeButtonText)
            .enabled(this.verifyCodeButtonEnable)  // 当点击了,之后按钮不可用
            .onClick(()=> {
              this.verifyCodeButtonText = `${this.countDown} s`
              this.countDown--
              this.verifyCodeButtonEnable = false
              this.intervalId = setInterval(() => {
                this.verifyCodeButtonText = `${this
                if(this.countDown<0){
                  clearInterval(this.intervalId)
                  this.countDown = 10
                  this.intervalId = 0
                  this.verifyCodeButtonText = '获取验证码'
                  this.verifyCodeButtonEnable = true  // 倒计时结束之后就可用了
                  return
                }
                this.countDown--
              },1000)
            })
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')

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

封装成函数



@Entry
@Component
struct MyLoginCustom {
  @State countDown: number = 10
  intervalId: number = 0
  @State verifyCodeButtonText: string = '获取验证码'
  @State verifyCodeButtonEnable: boolean = true

  waiting() {
    this.verifyCodeButtonText = `${this.countDown} s`
       this.countDown--
       this.verifyCodeButtonEnable = false
       this.intervalId = setInterval(() => {
         this.verifyCodeButtonText = `${this
         if(this.countDown<0){
           clearInterval(this.intervalId)
           this.countDown = 10
           this.intervalId = 0
           this.verifyCodeButtonText = '获取验证码'
           this.verifyCodeButtonEnable = true  // 倒计时结束之后就可用了
           return
         }
         this.countDown--
       },1000)
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
          Button(this.verifyCodeButtonText)
            .enabled(this.verifyCodeButtonEnable)  // 当点击了,之后按钮不可用
            .onClick(()=> {
              this.waiting()
            })
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')

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

自行实现登录发送验证码

在这里插入图片描述

在这里插入图片描述

import cloud, { VerifyCodeAction } from '@hw-agconnect/cloud'
import hilog from '@ohos.hilog'
import router from '@ohos.router'

@Entry
@Component
struct MyLoginCustom {
  @State countDown: number = 10
  intervalId: number = 0
  @State verifyCodeButtonText: string = '获取验证码'
  @State verifyCodeButtonEnable: boolean = true
  @State phoneNumber: string = ''

  waiting() {
    this.verifyCodeButtonText = `${this.countDown} s`
       this.countDown--
       this.verifyCodeButtonEnable = false
       this.intervalId = setInterval(() => {
         this.verifyCodeButtonText = `${this
         if(this.countDown<0){
           clearInterval(this.intervalId)
           this.countDown = 10
           this.intervalId = 0
           this.verifyCodeButtonText = '获取验证码'
           this.verifyCodeButtonEnable = true  // 倒计时结束之后就可用了
           return
         }
         this.countDown--
       },1000)
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
            .onChange(value => {
              this.phoneNumber = value
              })
          Button(this.verifyCodeButtonText)
            .enabled(this.verifyCodeButtonEnable)  // 当点击了,之后按钮不可用
            .onClick(async ()=> {
              this.waiting()
              try {
                 await cloud.auth().requestVerifyCode({
                   verifyCodeType: {
                     kind: 'phone',
                     phoneNumber: this.phoneNumber,
                     countryCode: '86'
                   },
                   action: VerifyCodeAction.REGISTER_LOGIN,
                   lang: 'zh_CN',
                   sendInterval: 10
                 })
                 hilog.info(0, 'VerifyCode', 'Success')
               } catch (e) {
                 // 弹窗提示错误 
                 AlertDialog.show({ title: '错误', message: '验证码发送失败' })

                 hilog.error(0, 'VerifyCode', JSON.stringify(e))
               }
            })
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')

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

自行实现登录登录

在这里插入图片描述
在这里插入图片描述

import cloud, { VerifyCodeAction } from '@hw-agconnect/cloud'
import hilog from '@ohos.hilog'
import router from '@ohos.router'

@Entry
@Component
struct MyLoginCustom {
  @State countDown: number = 10
  intervalId: number = 0
  @State verifyCodeButtonText: string = '获取验证码'
  @State verifyCodeButtonEnable: boolean = true
  @State phoneNumber: string = ''
  @State verifyCode: string = ''

  waiting() {
    this.verifyCodeButtonText = `${this.countDown} s`
       this.countDown--
       this.verifyCodeButtonEnable = false
       this.intervalId = setInterval(() => {
         this.verifyCodeButtonText = `${this
         if(this.countDown<0){
           clearInterval(this.intervalId)
           this.countDown = 10
           this.intervalId = 0
           this.verifyCodeButtonText = '获取验证码'
           this.verifyCodeButtonEnable = true  // 倒计时结束之后就可用了
           return
         }
         this.countDown--
       },1000)
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
            .onChange(value => {
              this.phoneNumber = value
              })
          Button(this.verifyCodeButtonText)
            .enabled(this.verifyCodeButtonEnable)  // 当点击了,之后按钮不可用
            .onClick(async ()=> {
              this.waiting()
              try {
                 await cloud.auth().requestVerifyCode({
                   verifyCodeType: {
                     kind: 'phone',
                     phoneNumber: this.phoneNumber,
                     countryCode: '86'
                   },
                   action: VerifyCodeAction.REGISTER_LOGIN,
                   lang: 'zh_CN',
                   sendInterval: 10
                 })
                 hilog.info(0, 'VerifyCode', 'Success')
               } catch (e) {
                 // 弹窗提示错误 
                 AlertDialog.show({ title: '错误', message: '验证码发送失败' })

                 hilog.error(0, 'VerifyCode', JSON.stringify(e))
               }
            })
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')
         .onClick(async ()=> {
              try {
                  const result = await cloud.auth().signIn({
                  credentialInfo: {
                    kind: 'phone',
                    countryCode: '86',
                    phoneNumber: this.phoneNumber,
                    verifyCode: this.verifyCode
                  }
                })
                const user = result.getUser()
                hilog.info(0, 'Login', 'Success')
                router.replaceUrl({ url: 'pages/MyWelcome' })  // 跳转到登录页
               } catch (e) {
                 // 弹窗提示错误 
                 AlertDialog.show({ title: '错误', message: '验证码发送失败' })

                 hilog.error(0, 'VerifyCode', JSON.stringify(e))
               }
            })

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

自行实现登录登录细节调整

检查用户输入的是否是手机号


import cloud, { VerifyCodeAction } from '@hw-agconnect/cloud'
import hilog from '@ohos.hilog'
import router from '@ohos.router'
@Entry
@Component
struct MyLoginCustom {
  @State countDown: number = 10
  intervalId: number = 0
  @State verifyCodeButtonText: string = '获取验证码'
  @State verifyCodeButtonEnable: boolean = true
  @State phoneNumber: string = ''
  @State verifyCode: string = ''

  waiting() {
    this.verifyCodeButtonText = `${this.countDown} s`
       this.countDown--
       this.verifyCodeButtonEnable = false
       this.intervalId = setInterval(() => {
         this.verifyCodeButtonText = `${this
         if(this.countDown<0){
           clearInterval(this.intervalId)
           this.countDown = 10
           this.intervalId = 0
           this.verifyCodeButtonText = '获取验证码'
           this.verifyCodeButtonEnable = true  // 倒计时结束之后就可用了
           return
         }
         this.countDown--
       },1000)
  }

    async login() {
    try {
      const result = await cloud.auth().signIn({
        credentialInfo: {
          kind: 'phone',
          countryCode: '86',
          phoneNumber: this.phoneNumber,
          verifyCode: this.verifyCode
        }
      })
      const user = result.getUser()
      AppStorage.SetOrCreate('user', user) // 存
      hilog.info(0, 'Login', 'Success')
      router.replaceUrl({ url: this.mainPage })
    } catch (e) {
      hilog.error(0, 'Login', JSON.stringify(e))
      AlertDialog.show({ title: '错误', message: `登录失败 ${JSON.stringify(e)}` })
    }
  }

  async sending() {
    try {
      await cloud.auth().requestVerifyCode({
        verifyCodeType: {
          kind: 'phone',
          phoneNumber: this.phoneNumber,
          countryCode: '86'
        },
        action: VerifyCodeAction.REGISTER_LOGIN,
        lang: 'zh_CN',
        sendInterval: 10
      })
      hilog.info(0, 'VerifyCode', 'Success')
    } catch (e) {
      AlertDialog.show({ title: '错误', message: '验证码发送失败' })
      hilog.error(0, 'VerifyCode', JSON.stringify(e))
    }
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Text('登录')
          .fontSize(40)
          .fontWeight(FontWeight.Bold)
        Divider()
        TextInput({ placeholder: '请输入手机号' })
          .width('80%')
          .type(InputType.Number)

        Row({ space: 10 }) {
          TextInput({ placeholder: '请输入验证码' })
            .width('55%')
            .type(InputType.Number)
            .onChange(value => {
              this.phoneNumber = value
              })
          Button(this.verifyCodeButtonText)
            .enabled(this.verifyCodeButtonEnable)  // 当点击了,之后按钮不可用
            .onClick(async ()=> {
              this.waiting()
              this.sending()
              
            })
        }
        .width('80%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button('登录')
        // 判断是否是合法的手机号,验证码
        .enabled(this.phoneNumber.length === 11 && this.verifyCode.length === 6)
         .onClick(async ()=> {
              this.login()
            })

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

个人设置页-登出

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

个人设置页修改昵称和头像

不用户信息存起来
在这里插入图片描述
取出头像信息
在这里插入图片描述

云存储

上传头像

开通云存储服务
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

图片缓存问题

在这里插入图片描述

在这里插入图片描述

工程模式

进入工程模式
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

用户授权

想使用云存储需要下面三步
1、开通云存储服务
2、更新项目的一个AGC的配置文件
3、用户授权,允许读取媒体文件(前面为什么没有,授权呢?因为我们使用的是端云一体化模板,配置都写好了)

下面我们就看看,模板帮我们写了那些配置

在这里插入图片描述
在这里插入图片描述

上传进度

问题4:上传进度实现
1.上传状态变量
2…上传进度文字变量、对应的Text
3.编写进度回调
在这里插入图片描述

import cloud, { AuthUser } from '@hw-agconnect/cloud'
import router from '@ohos.router'
import hilog from '@ohos.hilog'
import picker from '@ohos.file.picker'

@Entry
@Component
struct MyIndex {
  @State photoUrl: string = ''
  @State displayName: string = ''
  @StorageLink('user') user: AuthUser = null
  @State uploading: boolean = false
  @State uploadingText: string = '0%'

  aboutToAppear() {
    // 1. cloud.auth().getCurrentUser()
    // 2. AppStorage
    this.displayName = this.user?.getDisplayName()
    this.photoUrl = this.user?.getPhotoUrl()
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Stack() { // 堆叠的效果
          Image(this.photoUrl ? this.photoUrl : $r('app.media.user_dark'))
            .width(70)
            .height(70)
            .borderRadius(70)
            .enabled(!this.uploading)
            .onComplete(()=>{
              this.uploading = false
            })
            .onClick(async () => {
              // this.photoUrl = 'https://img.zcool.cn/community/01a6095f110b9fa8012066219b67d4.png@1280w_1l_2o_100sh.png'
              try {
                // 1. 从相簿中选照片
                const options = new picker.PhotoSelectOptions()
                options.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE
                options.maxSelectNumber = 1
                const result = await new picker.PhotoViewPicker().select(options)
                hilog.info(0, 'Upload', `Picker Success ${result.photoUris[0]}`)
                this.uploading = true
                // 2. 调云存储 api 上传照片
                await cloud.storage().upload({
                  localPath: result.photoUris[0],
                  cloudPath: `test/${this.user.getUid()}.jpg`,
                  onUploadProgress: event => {
                    const percent = Math.floor(100 * event.loaded / event.total)
                    this.uploadingText = `${percent}%`
                  }
                })
                hilog.info(0, 'Upload', 'Upload Success')
                // 3. 获取上传照片的网络地址
                const url = await cloud.storage().getDownloadURL(`test/${this.user.getUid()}.jpg`)
                this.photoUrl = `${url}&ts=${new Date().getTime()}`
                // this.uploading = false
                hilog.info(0, 'Upload', `url: ${url}`)
              } catch (e) {
                hilog.error(0, 'Upload', JSON.stringify(e))
              }
            })
          if (this.uploading) {
            // 显示上传进度
            Text(this.uploadingText)
              .width(70)
              .height(70)
              .borderRadius(70)
              .fontColor('white')
              .backgroundColor('black')
              .opacity(0.6)
              .fontSize(24)
              .fontWeight(FontWeight.Bolder)
              .textAlign(TextAlign.Center)
          }
        }


        TextInput({ placeholder: '请设置昵称', text: this.displayName })
          .width('50%')
          .onChange(value => {
            this.displayName = value
          })
        Button(`保存`)
          .onClick(async () => {
            try {
              await this.user.updateProfile({
                displayName: this.displayName,
                photoUrl: this.photoUrl
              })
              hilog.info(0, 'updateProfile', 'Success')
            } catch (e) {
              hilog.error(0, 'updateProfile', JSON.stringify(e))
            }
          })
        Button(`登出`)
          .onClick(async () => {
            try {
              await cloud.auth().signOut()
              hilog.info(0, 'SignOut', 'Success')
              router.replaceUrl({ url: 'pages/MyLoginCustom' })
            } catch (e) {
              hilog.error(0, 'SignOut', JSON.stringify(e))
            }
          })

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

在进程中直接结束程序(没有点击退出),再次点击登录,会报错

在这里插入图片描述
原因,我们虽然结束了程序,由于我们没有正常退出程序,用户会话还在,再次登录的时候出现错误

在这里插入图片描述

云数据库

概念

存储区,对象类型,对象

在这里插入图片描述
在这里插入图片描述

数据类型
数据类型取值范围说明排序方式
String最大长度200如果字符串长度超过200,建议使用Text类型。采用 UTF-8 编码的字节顺序
Booleantrue/false-false < true
Byte ( − 2 7 ) ∼ ( 2 7 − 1 ) (-2^7)\sim(2^7-1) (27)(271)-数字顺序
Short ( − 2 15 ) ∼ ( 2 15 − 1 ) (-2^{15})\sim(2^{15}-1) (215)(2151)-
Integer ( − 2 31 ) ∼ ( 2 31 − 1 ) (-2^{31})\sim(2^{31}-1) (231)(2311)-
Long ( − 2 63 ) ∼ ( 2 63 − 1 ) (-2^{63})\sim(2^{63}-1) (263)(2631)由于JavaScript不支持数据类型“Long”,Web SDK通过引入第三方开源组件实现支持数据类型“Long”的能力。“Long”类型的使用方法请参考https://github.com/dcodeIO/long.js。
Float-3.40E+38 ~ +3.40E+38-
Double-1.79E+308 ~ +1.79E+308-
ByteArray-一般用于文件类型的数据存储,如图片、文档和视频等。在端侧时,使用Android开发应用时,以byte[]表示为字节数组。采用 UTF-8 编码的字节顺序
Text--
Date--时间顺序
IntAutoIncrement 1 ∼ ( 2 31 − 1 ) 1\sim(2^{31}-1) 1(2311)Android、HarmonyOS(Java)和iOS不支持此数据类型。数字顺序
LongAutoIncrement 1 ∼ ( 2 63 − 1 ) 1\sim(2^{63}-1) 1(2631)Android、HarmonyOS(Java)和iOS不支持此数据类型。数字顺序
  • 假设有自增类型 id,执行 upsert (insert+update)
    • id 值在数据库有,更新 {id:1, name:‘李四’ , age:20}
    • id 值在数据库无,按自增添加 {id:5, name:‘王五’ , age:16}
    • id 值不给,也按自增添加 {name:‘赵六’ , age:16}
student 表
id	 name   age
1    李四    20
2    王五    16
3	 赵六    16
权限管理

【角色】共有四种

  • World 所有人
  • Authenticated 已认证
  • Creator 创建者
  • Administrator 管理员

【权限】共有三种:Read 查询、Upsert 新增和修改、Delete 删除

例如

article 表
id	title  								
											用户 111
1   探索自我成长的道路:我的心路历程
2   如何在忙碌的生活中保持身心健康
3   分享我的旅行经历:探索世界的美
											用户 222
4	金融投资的新趋势:数字货币的崛起
5   未来科技发展展望:人工智能对社会的影响
  1. 用户111 未登录,查询所有文章 1 2 3 4 5
  2. 用户333 未登录,查询所有文章 1 2 3 4 5
  3. 用户111 未登录,添加新文章 失败
  4. 用户111 已登录,添加新文章 成功
  5. 用户111 已登录,删除文章1 成功
  6. 用户111 已登录,删除文章4 失败
  7. 用户111 已登录,修改文章4 成功

从上面我们可知,华为的云数据库,权限配置比较粗糙,还是需要配合业务代码,进行

它的权限设置如下:

"permissions": [
  {
    "rights": ["Read"],
    "role": "World"
  },
  {
    "rights": ["Read","Upsert"],
    "role": "Authenticated"
  },
  {
    "rights": ["Read","Upsert","Delete"],
    "role": "Creator"
  },
  {
    "rights": ["Read","Upsert","Delete"],
    "role": "Administrator"
  }
]

则有:

  • 未认证(未登录)的用户只能查询
    • 这时最好不要给 Upsert 权限,否则由未认证的用户新增的数据,除了管理员都不能删除
  • 已认证的用户可以查询、新增、修改
  • 已认证的用户自己新增的数据,自己可以删除,别人的数据不能删除(但可以修改)
  • 已认证用户查询时,还是能查到其它用户的数据的,因此需要自己维护 uid 来控制数据权限

准备云数据库

存储区

在这里插入图片描述

新建对象的类型,其实就是新建表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

另一种方法准备云数据库

使用云端一体化模板

新建一张表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

端侧调用云数据库

调用云数据库又分成了两种方式
一种叫端测调用:使用ArkTs它的API,然后直接去使用云数据库

另外一种叫云测调用: 云函数间接的使用云数据库
首先明确:云数据库需要对接华为帐号系统(与前面提到的权限相关联)

分成两种方式:

一. 直接在端侧调用数据库,优点是更直接面对数据库,而且根据文档具备缓存模式的好处

二. 通过云函数在云侧调用数据库,可能无法利用缓存模式的一些好处(未验证)。但又可以隐藏数据库细节,并具备云函数的优点

端侧调用

0)前提
  1. 添加依赖
  2. 下载 AGC 配置文件
  3. 初始化 AGC 连接器

这三步与调用云函数相同

在这里插入图片描述
long类型。js不支持,需要用到第三方库

每次我们做了新的配置,都要重新下载 AGC 配置文件

在这里插入图片描述
初始化 AGC 连接器
在这里插入图片描述

1)建立模型

在这里插入图片描述

在这里插入图片描述
js 代表端侧模型代码
serverSDK代表云侧模型代码

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

导出结果如下

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
 * Generated by the CloudDB ObjectType compiler. DO NOT EDIT!
 */

class t_user {
    constructor() {
        this.id = undefined;
        this.name = "";
        this.birthday = undefined;
    }

    setId(id) {
        this.id = id;
    }

    getId() {
        return this.id;
    }

    setName(name) {
        this.name = name;
    }

    getName() {
        return this.name;
    }

    setBirthday(birthday) {
        this.birthday = birthday;
    }

    getBirthday() {
        return this.birthday;
    }
}

t_user.className = 't_user';

export {t_user}
2)导出 schema


在这里插入图片描述

导出数据库 schema 文件,例如下面是一个文件名 com.example.myapplication_21_cn.json,格式为【应用名_版本_语言.json】

3)初始化 database

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
改进–分页查询
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

改进–搜索功能

在这里插入图片描述
改进下滑显示搜索框
只有下滑到顶部的时候,才显示搜索框

在这里插入图片描述
在这里插入图片描述

// @ts-ignore
import schema from './com.example.myapplication_21_cn.json'

@Entry
@Component
struct XXX {
  // ...  
  private database: Database

  async aboutToAppear() {
    try {
      // 根据 schema 和 zoneName 创建数据库对象
      this.database = cloud.database({ objectTypeInfo: schema, zoneName: "shopping" })
    } catch (e) {
      hilog.error(0, 'User Query', 'error:' + JSON.stringify(e))
    }
  }
4)查询
import cloud, { Database } from '@hw-agconnect/cloud'

// @ts-ignore
import schema from '../../resources/rawfile/schema.json'
import { t_student } from '../model/t_student'
import hilog from '@ohos.hilog'

@Entry
@Component
struct StudentPage {
  @State studentList: t_student[] = []
  private database: Database = null
  private dataOffset: number = 0
  @State searchAge: string = ''
  @State showSearch: boolean = false
  private startY: number = 0
  private startIndex: number = 0

  aboutToAppear() {
    this.database = cloud.database({
      zoneName: 'test',
      objectTypeInfo: schema
    })
    this.search('', '', '', this.dataOffset)
  }

  async search(id: string, name: string, age: string, offset: number, limit: number = 10) {
    try {
      const query = this.database.collection(t_student).query()
      if (id.length > 0) {
        // where id=?
        query.equalTo("id", Number(id))
      }
      if (name.length > 0) {
        // where name like ?
        query.contains("name", name)
      }
      if (age.length > 0) {
        // where age=?
        query.equalTo("age", Number(age))
      }
      query.limit(limit, offset)
      query.orderByAsc("id")
      const list: t_student[] = await query.get()
      this.studentList.push(...list)
      hilog.info(0, 'Query', `${list.map(s => s.id)}`)
    } catch (e) {
      hilog.error(0, 'Query', JSON.stringify(e))
    }
  }

  build() {
    Column({ space: 26 }) {
      Row() {
        Text(`编号`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('white')
          .textAlign(TextAlign.Center)
          .width('33%')
        Text(`姓名`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('white')
          .textAlign(TextAlign.Center)
          .width('34%')
        Text(`年龄`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('white')
          .textAlign(TextAlign.Center)
          .width('33%')
      }
      .width('100%')
      .height(36)
      .backgroundColor('black')

      if (this.showSearch) {
        Search()
          .searchButton('搜索')
          .onChange(value => {
            this.searchAge = value
          })
          .onSubmit(() => {
            this.studentList = []
            this.dataOffset = 0
            this.search('', '', this.searchAge, this.dataOffset)
            this.showSearch = false
          })
      }

      List({ space: 52 }) {
        ForEach(this.studentList, (stu: t_student) => {
          ListItem() {
            Row() {
              Text(`${stu.getId()}`)
                .fontSize(18)
                .textAlign(TextAlign.Center)
                .width('33%')
              Text(stu.getName())
                .fontSize(18)
                .textAlign(TextAlign.Center)
                .width('34%')
              Text(`${stu.getAge()}`)
                .fontSize(18)
                .textAlign(TextAlign.Center)
                .width('33%')
            }
            .width('100%')
          }
        })
      }
      .width('100%')
      .height('82%')
      .onReachEnd(() => {
        this.dataOffset += 10
        this.search('', '', '', this.dataOffset)
      })
      .onScrollIndex((start, end) => {
        hilog.info(0, 'Scroll', `start: ${start} end:${end}`)
        this.startIndex = start
      })
      .onTouch(event => {
        switch (event.type) {
          case TouchType.Down:
            this.startY = event.touches[0].y
            break;
          case TouchType.Move:
            const endY = event.touches[0].y
            if (endY - this.startY >= 35 && this.startIndex === 0) {
              this.showSearch = true
            } else if (this.startY - endY >= 25) {
              this.showSearch = false
            }
            break;
        }
      })

      Divider()
    }
    .width('100%')
    .justifyContent(FlexAlign.Start)
  }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实现左滑删除
在这里插入图片描述

有个难点bind

函数作为参数传递,this丢失问题
函数作为参数传递,剩余参数问问题

问题演示
在这里插入图片描述

在这里插入图片描述
问题出现
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5)新增、修改、删除
import cloud, { Database } from '@hw-agconnect/cloud'

// @ts-ignore
import schema from '../../resources/rawfile/schema.json'
import { t_student } from '../model/t_student'
import hilog from '@ohos.hilog'
import { StudentInsertDialog } from './StudentInsertDialog'
import router from '@ohos.router'
import { StudentUpdateDialog } from './StudentUpdateDialog'

@Entry
@Component
struct StudentPage {
  @State studentList: t_student[] = []
  private database: Database = null
  private dataOffset: number = 0
  @State searchAge: string = ''
  @State showSearch: boolean = false
  private startY: number = 0
  private startIndex: number = 0
  private studentInsertDialogController: CustomDialogController = new CustomDialogController({
    builder: StudentInsertDialog({
      confirm: async (name, age) => {
        try {
          const stu = new t_student()
          stu.setName(name)
          stu.setAge(age)
          await this.database.collection(t_student).upsert(stu)
          AlertDialog.show({ message: '添加学生成功' })
          hilog.info(0, 'Student Insert', 'Success')
          this.dataOffset = 0
          this.studentList = []
          this.search('', '', '', this.dataOffset)
        } catch (e) {
          hilog.error(0, 'Student Insert', JSON.stringify(e))
        }
      }
    })
  })
  private studentUpdateDialogController: CustomDialogController

  openStudentUpdateDialog(stu: t_student) {
    this.studentUpdateDialogController = new CustomDialogController({
      builder: StudentUpdateDialog({
        sid: stu.id, name: stu.name, age: stu.age,
        confirm: async (id, name, age) => {
          try {
            const stu = new t_student()
            stu.setId(id)
            stu.setName(name)
            stu.setAge(age)
            await this.database.collection(t_student).upsert(stu)
            AlertDialog.show({ message: '修改学生成功' })
            hilog.info(0, 'Student Update', 'Success')
            this.dataOffset = 0
            this.studentList = []
            this.search('', '', '', this.dataOffset)
          } catch (e) {
            hilog.error(0, 'Student Update', JSON.stringify(e))
          }
        }
      })
    })
    this.studentUpdateDialogController.open()
  }

  openStudentDeleteDialog(stu: t_student) {
    AlertDialog.show({
      title: '请确认',
      message: '真的要删除该用户吗?',
      confirm: {
        value: '确定',
        action: async () => {
          try {
            await this.database.collection(t_student).delete(stu)
            AlertDialog.show({ message: '删除学生成功' })
            hilog.info(0, 'Student Delete', 'Success')
            this.dataOffset = 0
            this.studentList = []
            this.search('', '', '', this.dataOffset)
          } catch (e) {
            hilog.error(0, 'Student Delete', JSON.stringify(e))
          }
        }
      }
    })
  }

  @Builder
  showOperation(stu: t_student) {
    Row({ space: 12 }) {
      Image($r("app.media.ic_public_edit_filled"))
        .width(20)
        .onClick(() => {
          this.openStudentUpdateDialog(stu)
        })
      Image($r("app.media.ic_public_delete_filled"))
        .width(21)
        .onClick(() => {
          this.openStudentDeleteDialog(stu)
        })
    }
    .width(80)
    .justifyContent(FlexAlign.Start)
  }

  aboutToAppear() {
    this.database = cloud.database({
      zoneName: 'test',
      objectTypeInfo: schema
    })
    this.search('', '', '', this.dataOffset)
  }

  async search(id: string, name: string, age: string, offset: number, limit: number = 10) {
    try {
      const query = this.database.collection(t_student).query()
      if (id.length > 0) {
        // where id=?
        query.equalTo("id", Number(id))
      }
      if (name.length > 0) {
        // where name like ?
        query.contains("name", name)
      }
      if (age.length > 0) {
        // where age=?
        query.equalTo("age", Number(age))
      }
      query.limit(limit, offset)
      query.orderByAsc("id")
      const list: t_student[] = await query.get()
      this.studentList.push(...list)
      hilog.info(0, 'Student Query', `${list.map(s => s.id)}`)
    } catch (e) {
      hilog.error(0, 'Student Query', JSON.stringify(e))
    }
  }

  build() {
    Column() {
      Row() {
        Text(`编号`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('white')
          .textAlign(TextAlign.Center)
          .width('33%')
        Text(`姓名`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('white')
          .textAlign(TextAlign.Center)
          .width('34%')
        Text(`年龄`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('white')
          .textAlign(TextAlign.Center)
          .width('33%')
      }
      .width('100%')
      .height(36)
      .backgroundColor('black')

      if (this.showSearch) {
        Search()
          .searchButton('搜索')
          .onChange(value => {
            this.searchAge = value
          })
          .onSubmit(() => {
            this.studentList = []
            this.dataOffset = 0
            this.search('', '', this.searchAge, this.dataOffset)
            this.showSearch = false
          })
      }

      List({ space: 48 }) {
        ForEach(this.studentList, (stu: t_student) => {
          ListItem() {
            Row() {
              Text(`${stu.getId()}`)
                .fontSize(18)
                .textAlign(TextAlign.Center)
                .width('33%')
              Text(stu.getName())
                .fontSize(18)
                .textAlign(TextAlign.Center)
                .width('34%')
              Text(`${stu.getAge()}`)
                .fontSize(18)
                .textAlign(TextAlign.Center)
                .width('33%')
            }
            .width('100%')
          }
          .swipeAction({ end: this.showOperation.bind(this, stu) })
        })
      }
      .width('100%')
      .height('78%')
      .margin({ top: 24, bottom: 24 })
      .onReachEnd(() => {
        this.dataOffset += 10
        this.search('', '', '', this.dataOffset)
      })
      .onScrollIndex((start, end) => {
        hilog.info(0, 'Scroll', `start: ${start} end:${end}`)
        this.startIndex = start
      })
      .onTouch(event => {
        switch (event.type) {
          case TouchType.Down:
            this.startY = event.touches[0].y
            break;
          case TouchType.Move:
            const endY = event.touches[0].y
            if (endY - this.startY >= 25 && this.startIndex === 0) {
              this.showSearch = true
            } else if (this.startY - endY >= 5 || this.startIndex === 0) {
              this.showSearch = false
            }
            break;
        }
      })

      Button('新增', { type: ButtonType.Normal })
        .width('100%')
        .margin({ bottom: 6 })
        .onClick(async () => {
          this.studentInsertDialogController.open()
        })

      Button('登出', { type: ButtonType.Normal })
        .width('100%')
        .backgroundColor(0xcccccc)
        .margin({ bottom: 6 })
        .onClick(async () => {
          try {
            await cloud.auth().signOut()
            hilog.info(0, 'SignOut', 'Success')
            router.replaceUrl({ url: 'pages/MyLoginCustom' })
          } catch (e) {
            hilog.error(0, 'SignOut', JSON.stringify(e))
          }
        })
      Divider()
    }
    .width('100%')
    .justifyContent(FlexAlign.Start)
  }
}
@CustomDialog
export struct StudentInsertDialog {
  private name: string = ''
  private age: number = 18
  controller: CustomDialogController
  confirm: (name: string, age: number) => void
  private static range = (start, stop, step = 1) => Array.from({
    length: (stop - start - 1) / step + 1
  }, (_, i) => start + (i * step))
  static AgeRange: string[] = StudentInsertDialog.range(16, 60).map(i => String(i))

  build() {
    Column({ space: 15 }) {
      Row() {
        Text('姓名')
          .width('20%')
        TextInput({ text: this.name })
          .width('80%')
          .onChange(value => {
            this.name = value
          })
      }
      .width('80%')
      .justifyContent(FlexAlign.SpaceAround)

      Row() {
        Text('年龄')
          .width('20%')
        TextPicker({ range: StudentInsertDialog.AgeRange, selected: 2 })
          .width('80%')
          .onChange(value => {
            this.age = Number(value)
          })
      }
      .width('80%')

      Row() {
        Button('取消')
          .onClick(() => {
            this.controller.close()
          })
          .backgroundColor(0xcccccc)
        Button('确定')
          .onClick(() => {
            this.confirm(this.name, this.age)
            this.controller.close()
          })
      }
      .width('80%')
      .justifyContent(FlexAlign.SpaceAround)
    }
    .justifyContent(FlexAlign.SpaceAround)
    .width('80%')
    .height('55%')
    .margin({ top: 15 })
  }
}
import { StudentInsertDialog } from './StudentInsertDialog'
@CustomDialog
export struct StudentUpdateDialog {
  private sid: number
  private name: string
  private age: number
  controller: CustomDialogController
  confirm: (id: number, name: string, age: number) => void

  build() {
    Column({ space: 15 }) {
      Text(`${this.sid}`)

      Row() {
        Text('姓名')
          .width('20%')
        TextInput({ text: this.name })
          .width('80%')
          .onChange(value => {
            this.name = value
          })
      }
      .width('80%')
      .justifyContent(FlexAlign.SpaceAround)

      Row() {
        Text('年龄')
          .width('20%')
        TextPicker({ range: StudentInsertDialog.AgeRange, selected: 2 })
          .width('80%')
          .onChange(value => {
            this.age = Number(value)
          })
      }
      .width('80%')

      Row() {
        Button('取消')
          .onClick(() => {
            this.controller.close()
          })
          .backgroundColor(0xcccccc)
        Button('确定')
          .onClick(() => {
            this.confirm(this.sid, this.name, this.age)
            this.controller.close()
          })
      }
      .width('80%')
      .justifyContent(FlexAlign.SpaceAround)
    }
    .justifyContent(FlexAlign.SpaceAround)
    .width('80%')
    .height('65%')
    .margin({ top: 15 })
  }
}

云侧调用

这部分代码可以自动生成,不作为重点讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值