【HarmonyOS NEXT星河版开发学习】综合测试案例-各平台评论部分

news2024/11/16 0:02:14

 

目录

 前言

功能展示

整体页面布局

最新和最热 

写评论 

点赞功能 

界面构建

 初始数据的准备

 列表项部分的渲染

底部区域 

index部分 

 知识点概述

List组件

List组件简介

ListItem组件详解

ListItemGroup组件介绍

ForEach循环渲染

列表分割线设置

列表排列方向设置

索引值计算规则

@Prop装饰器 

@Prop注解的基本作用

@Prop注解的限制条件

@Prop注解的使用规则

@Prop注解的具体使用场景

全套代码

BottomCom部分

InfoCom部分

InfoItem部分

CommentDate部分

Index部分


个人主页→VON

收录专栏→鸿蒙开发小型案例总结​​​​​

基础语法部分会发布于github 和 gitee上面(暂未发布)

由于一些个人的特殊原因,基础部分的代码迟迟未进行上传,我会尽快进行整理发布后会第一时间进行通知,希望大家多多谅解

 前言

鸿蒙基础部分到这里也要和大家说再见了,此案例所用到的知识点众多构建过程较为复杂。希望读者们能够认真观看。我也会尽力帮助大家梳理代码以及各各部分的逻辑及其思路。

此案例可以应用于多个地方,比如博客,短视频app等都可以进行应用

功能展示

整体页面布局

整体分为三大部分,分别为头部、中间、底部。认真观察不难发现头部区域采用了Row,中间区域采用了List,底部区域采用了两个Row。整体布局为层叠布局。 

最新和最热 

 

最新和最热点击时显示的效果不同,并且列表所展示的也不同。点击最新时评论的内容会根据时间进行排序,最热时会根据点赞数进行排序。

写评论 

 自己可以在写评论处发布评论,列表会根据评论进行渲染。

点赞功能 

 

界面构建

由于代码的长度限制,过于基础的部分不在进行逐一讲解,希望大家谅解

 最新和最热是两个按钮组件,Extend装饰器进行修改,当未被选中时是一种状态,被选中时是另外一种状态。这两种状态的样式我是根据最左侧的颜色来进行适当调整的,大家也可以效仿。

 初始数据的准备

 初始数据是自己进行构建的一些数据,这些数据被打包在CommentDate中,其中点赞数和等级是随机的,其他部分都是自定义的。因为这是一个离线的静态页面,并没有实现交互功能所以目前只能这样来代替。

 列表项部分的渲染

渲染部分用到了CommentDate中的数据来进行逐一渲染的,因为实现交互的是子组件,但是要修改的部分是父组件的一些内容,所以对于可变的数据用到了Prop装饰器进行的装饰。 

底部区域 

底部区域实现了双向绑定,当输入评论并且单击回车键后数据会进行增加。并且会排在首位,也就是下标为0的位置增添了一个全新的数据。 

index部分 

 

一些函数的定义及其功能的实现全都在index页面来进行实现的,所以这一界面要尽量保持简介,每一部分都可以进行抽取单独进行ui界面的构建,通过import来进行导入即可。 

 知识点概述

List组件

鸿蒙开发中的List组件是一个功能强大且常用的UI组件,用于呈现连续的数据列表。

List组件简介

  • 基础定义:List组件是一种容器组件,用于展示一系列相同宽度的列表项,适合连续、多行呈现同类数据,例如图片和文本。
  • 参数配置:List组件接受三个主要参数:space(子组件主轴方向的间隔)、initialIndex(初次加载时视口起始位置显示的item索引)、scroller(可滚动组件的控制器)。

ListItem组件详解

  • 基本概念:ListItem是具体的列表项组件,必须配合List使用,用于展示每个具体的数据项。
  • 参数设置:主要参数包括selectable(是否可被鼠标框选)和swipeAction(设置划出组件的属性)。

ListItemGroup组件介绍

  • 功能描述:用于展示列表项分组,宽度默认充满List组件,也必须配合List使用。
  • 主要参数:header(头部组件)、footer(尾部组件)和space(列表项间距)。

ForEach循环渲染

  • 应用场景:当列表由多个相似或重复的列表项组成时,为减少代码冗余,可以使用ForEach进行循环渲染。
  • 工作原理:ForEach接口基于数组类型数据进行循环渲染,需与容器组件配合使用,如List。

列表分割线设置

  • 功能解释:通过divider属性设置列表项之间的分割线样式,提升视觉上的区分度和美观性。
  • 参数说明:主要参数包括strokeWidth(分割线的线宽)、color(分割线的颜色)、startMargin和endMargin(分割线距离列表侧边起始端和结束端的距离)。

列表排列方向设置

  • 垂直排列:List默认采用垂直排列方式,即列表项按垂直方向线性排列。
  • 水平排列:通过设置listDirection属性为Axis.Horizontal,可以实现列表项的水平排列。

索引值计算规则

  • 规则概述:索引值用来确定列表项在列表中的具体位置,初次加载时默认从0开始。
  • 细节掌握:初始索引可以通过initialIndex参数手动设置,但需确保索引值不超过列表项总数。

@Prop装饰器 

@Prop是一个方便的注解,用于在鸿蒙应用开发中实现组件之间的数据传递

在现代软件开发中,组件化和数据传递是提高开发效率和代码可维护性的关键环节。特别是在鸿蒙这类分布式操作系统中,对组件间数据传递机制的优化尤为重要。下面将详细探讨@Prop注解的作用、限制条件、使用规则以及具体的使用场景。

@Prop注解的基本作用

  • 单向数据同步:@Prop注解主要用于实现组件间的单向数据同步。这意味着,当父组件的状态发生改变时,这些改变会通过@Prop注解传递给子组件,但子组件对这些属性的修改不会影响到父组件。
  • 支持的数据类型:@Prop能够处理各种基本数据类型,包括字符串、数字、布尔值和枚举类型。这保证了其在不同场景下的灵活性和适用性。

@Prop注解的限制条件

  • 复杂类型的深拷贝:当@Prop涉及到复杂数据类型时(如对象或数组),会进行深拷贝操作。这一过程中,除了基本类型(如字符串、数字)、Map、Set、Date和Array外,其他类型可能会丢失。
  • 使用场景限制:@Prop不能在@Entry装饰的自定义组件中使用。这限定了@Prop的使用范围,通常仅适用于页面级组件。

@Prop注解的使用规则

  • 参数配置:@Prop不需要特定参数,其同步类型为单向同步。
  • 类型要求严格:使用@Prop时必须明确指定被装饰变量的具体类型,不允许使用any类型,也不允许使用undefined和null作为默认值。

@Prop注解的具体使用场景

  • 简单数据类型同步:例如,父组件中的@State状态可以通过@Prop注解传递给子组件。如果父组件的状态发生更新,子组件的@Prop也会相应更新。但如果子组件尝试修改这些属性,更改不会反映到父组件中。
  • 数组项同步:当父组件中的@State数组项更新时,子组件中对应的@Prop也会同步更新。例如,父组件可以包含一个数字数组,每个元素用来初始化子组件中的一个@Prop。同样,子组件对@Prop的修改不会反映到父组件中。
  • 类对象属性同步:父组件中的@State类对象可以用来初始化子组件的@Prop。任何父组件中对象属性的更新都会同步到子组件,但子组件对@Prop的修改依旧不会反向同步到父组件。

全套代码

BottomCom部分

@Component
struct BottomCom {
  @State txt:string=''
  onSubmitComment=(content:string)=>{}
  build() {
    Row(){
      Row(){
        Image($r('app.media.edit'))
          .width(20)
          .margin({left:10})
        TextInput({
          placeholder:'写评论...',
          // 双向绑定
          text:$$this.txt
        })
          .backgroundColor(Color.Transparent)
          .fontSize(18)
          // 回车
          .onSubmit(()=>{
            this.onSubmitComment(this.txt)
          })
      }
      .height(40)
      .backgroundColor('#f5f6f5')
      .borderRadius(20)
      .margin({left:15,right:20,top:10,bottom:10})
      .layoutWeight(1)

      Image($r('app.media.love_stare'))
        .width(25)
      Image($r("app.media.like_stare"))
        .width(25)
        .margin({left:15,right:10})

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

export default BottomCom

InfoCom部分

import BottomCom from './BottomCom'
@Extend(Button)
function ButStyle(click:boolean){
  .fontSize(12)
  .border({width:1,color:click?'#fff':'#ffbeb7b7'})
  .width(46)
  .height(32)
  .padding({left:5,right:5})
  .fontColor(click ? '#80555858' :'#ff1f1e1e')
  .backgroundColor(click ? '#fff' : '#1ae0e0e0')
}
@Component
struct InfoCom {
  @State click:boolean=true
  onSort=(type:number)=>{

  }
  build() {
    Row(){
      Text('全部评论')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      Row(){
        Button('最新')
          .ButStyle(!this.click)
          .onClick(()=>{
            this.click=true
            this.onSort(0)
          })
        Button('最热')
          .ButStyle(this.click)
          .onClick(()=>{
            this.click=false
            this.onSort(1)
          })
      }
    }
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({left:20,right:20})
    .width('100%')
    .height(60)
  }
}
export default InfoCom

InfoItem部分

import { CommentDate } from '../model/CommentDate'

@Component
struct InfoItem {
  @Prop itemObj:CommentDate
  @Prop index:number
  onLikeClick=(index:number)=>{

  }
  build() {
    // 列表项组件
    Column(){
      Row(){
        // 头像
        Image(this.itemObj.avatar)
          .width(30)
          .borderRadius(15)
          .margin({top:10,right:5})
        // 昵称
        Text(this.itemObj.name)
          .fontColor('#808d8585')
          .fontSize(14)
          .margin({top:10,left:5})
        // 等级
        Image(this.itemObj.levelIcon)
          .width(20)
          .margin({left:10,top:10})
      }
      // 评论内容
      Text(this.itemObj.commentext)
        .fontSize(13)
        .fontWeight(700)
        .margin({top:10,left:40})
      Row(){
        // 时间
        Text(this.itemObj.timeString)
          .fontSize(10)
          .fontColor(Color.Gray)
        // 点赞
        Row(){
          Image(this.itemObj.islike ? $r('app.media.like_end') : $r('app.media.like_stare'))
            .width(12)
          Text(this.itemObj.likenum.toString())
            .fontSize(10)
            .fontColor(this.itemObj.islike ? Color.Red : Color.Gray)
        }
        .onClick(()=>{
          this.onLikeClick(this.index)
        })
      }
      .width('100%')
      .padding({left:40,right:20,top:15})
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .alignItems(HorizontalAlign.Start)
    .padding({left:15,top:10})
  }
}

export default InfoItem

CommentDate部分

// 准备评论数据类
export class CommentDate{
  avatar:ResourceStr;// 头像
  name:string;// 昵称
  level:number;// 用户等级
  likenum:number;// 点赞数量
  commentext:string;// 评论内容
  islike:boolean;// 是否喜欢
  levelIcon:Resource;// level等级
  timeString:string;// 发布时间
  time:number // 时间戳

  constructor(avatar: ResourceStr, name: string, level: number, likenum: number, commentext: string,
              islike: boolean,time:number) {
    this.avatar=avatar
    this.name=name
    this.level=level
    this.likenum=likenum
    this.commentext=commentext
    this.islike=islike
    this.time=time
    this.levelIcon=this.convertLevel(this.level)
    this.timeString=this.convertTime(time)
  }

  //时间转换函数
  convertTime(time:number){
    const currentTimestamp = new Date().getTime();
    // 转换为秒
    const timeDifference = (currentTimestamp-time)/1000;
    console.log(timeDifference.toString())
    if(timeDifference<0 || timeDifference==0){
      return '刚刚';
    }else if(timeDifference<60){
      return `${Math.floor(timeDifference)}秒前`;
    }else if(timeDifference<3600){
      return `${Math.floor(timeDifference/60)}分钟前`;
    }else if(timeDifference<86400){
      return `${Math.floor(timeDifference/3600)}小时前`;
    }else if(timeDifference<604800){
      return `${Math.floor(timeDifference/86400)}天前`;
    }else if(timeDifference<2592000){
      return `${Math.floor(timeDifference/604800)}周前`;
    }else if(timeDifference<31536000){
      return `${Math.floor(timeDifference/2592000)}个月前`;
    }else{
      return `${Math.floor(timeDifference/31536000)}年前`;
    }
  }

  // 等级图片获取
  convertLevel(Level:number){
    const iconList=[
      $r('app.media.lv1'),
      $r('app.media.lv2'),
      $r('app.media.lv3'),
      $r('app.media.lv4'),
      $r('app.media.lv5'),
      $r('app.media.lv6')
    ]
    return iconList[Level]
  }
}

// 封装一个方法,创建假数据
export const createListRange=():CommentDate[]=>{
  let result:CommentDate[]=new Array()
  result=[
    new CommentDate($r('app.media.tx_01'),'JohnYan',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'要是那天,我抓住你就好了',false,1705850201128),
    new CommentDate($r('app.media.tx_03'),'cv工程师',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'故事不长,也不难讲,相识一场,爱而不得',false,1643800201128),
    new CommentDate($r('app.media.tx_04'),'风行水上',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'后来啊,书没有读好,喜欢的人也没有在一起',false,1715850201128),
    new CommentDate($r('app.media.tx_05'),'枫以',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'你根本忘不了一个认认真真爱过的人,你以为错过的是一个人,其实你错过的是一整个人生',false,1680850201128),
    new CommentDate($r('app.media.tx_06'),'幼稚园里的幼稚鬼',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'有些伤痛即使已经痊愈,也会留下难以愈合的伤疤',false,1705850201128),
    new CommentDate($r('app.media.tx_07'),'浮临子',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'不是所有的梦想都会成真,不是所有的伤痛都能愈合,但我们要有勇气继续前行',false,1625050201128),
    new CommentDate($r('app.media.tx_08'),'╭⌒浅浅笑',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'孤独并不可怕,可怕的是渴望有人陪伴而得不到',false,1720850201128),
    new CommentDate($r('app.media.tx_09'),'枕头说它不想醒',Math.floor(Math.random()*6),Math.floor(Math.random()*100),'可惜爱不是写诗 我只能欲言又止',false,1745050201128)
  ]
  return result
}

Index部分

import InfoCom from '../components/InfoCom'
import BottomCom from '../components/BottomCom'
import InfoItem from '../components/InfoItem'
import {CommentDate,createListRange} from '../model/CommentDate'

@Entry
@Component
struct Index {
  // 处理点赞时的方法
  handlelike(index:number){
    // 要有唯一标识
    // AlertDialog.show({
    //   message:index.toString()
    // })
    // 父组件的方法,如果抽取出来,如果直接传递给子组件会有this指向问题,this通常直接指向调用者
    // 需要用箭头函数包一层,保证this还是指向父组件

    // 根据index进行判断
    let itemData=this.commentList[index]
    if(itemData.islike){
      itemData.likenum-=1
    }else{
      itemData.likenum+=1
    }
    itemData.islike= !itemData.islike

    // 对于复杂类型:状态对象,状态数组,只会对第一层数据进行监视变化
    this.commentList.splice(index,1,itemData)
  }
  // 处理提交
  handleSubmit(content:string){
    // 将数据添加到数组最前面
    const newItem:CommentDate=new CommentDate(
      $r('app.media.tx_01'),'我',2,0,content,false,new Date().getTime()
    )
    this.commentList=[newItem,...this.commentList]
  }
  // 处理排序
  handleSort(type:number){
    if(type==0){
      this.commentList.sort((a,b)=>{
        return b.time-a.time
      })
    }else if(type==1){
      this.commentList.sort((a,b)=>{
        return b.likenum-a.likenum
      })
    }

  }
  // 初始化数据
  @State commentList:CommentDate[]=createListRange()
  // 生命周期函数,会自动执行
  aboutToAppear(): void {
    this.handleSort(0)
  }
  build() {
    Column(){
      //头部
      InfoCom({
        onSort:(type:number)=>{
          this.handleSort(type)
        }
      })
      //中间
      List(){
        ForEach(this.commentList,(item:CommentDate,index:number)=>{
          ListItem(){
            // 列表项组件
            InfoItem({
              index:index,
              itemObj:item,
              onLikeClick:(index:number)=>{
                // 此处的this就是父组件
                this.handlelike(index)
              }
            })
          }
        })

      }
      .width('100%')
      .layoutWeight(1)
      //底部
      BottomCom({
        onSubmitComment:(content:string)=>{
          this.handleSubmit(content)
        }
      })
    }
    .width('100%')
    .height('100%')
  }
}

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

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

相关文章

高德地图SDK Android版开发 6 显示覆盖物

高德地图SDK Android版开发 6 显示覆盖物 前言地图类中覆盖物的接口覆盖物类Marker示例Polyline示例Polygon示例Arc示例Circle示例移除示例效果图 Marker的更多属性常用属性交互动画其它属性 折线的更多属性常用属性其它属性 多边形的更多属性常用属性其它属性 Arc的更多属性Ci…

5.2、配置stp

一、常用命令罗列 1. 查看STP状态 show spanning-tree功能&#xff1a;显示交换机上生成树协议的当前状态&#xff0c;包括各个端口的状态、根桥信息、VLAN相关信息等。 2. 启用STP spanning-tree vlan [VLAN_ID]功能&#xff1a;在指定的VLAN上启用生成树协议&#xff08;…

背包问题的模板及各个等价变形

目录 0-1背包 —— 二维二重循环 01背包 —— 一维二重循环 完全背包 —— 二维三重循环 完全背包 —— 二维二重循环 完全背包 —— 一维二重循环 0-1背包 —— 二维二重循环 #include <bits/stdc.h> using namespace std; const int N 1010; int dp[N][N]; int v…

Kali Linux 命令大全

一、引言 Kali Linux 作为一款专为渗透测试和安全研究设计的操作系统&#xff0c;拥有丰富的命令行工具&#xff0c;熟练掌握这些命令对于高效地进行安全测试和分析至关重要。本文将为您详细介绍 Kali Linux 中常用的命令&#xff0c;涵盖系统信息获取、文件操作、网络分析、用…

期权懂技巧分享:小细节决定成功的一切!

今天带你了解期权懂技巧分享&#xff1a;小细节决定成功的一切&#xff01;在期权交易市场中&#xff0c;有不少玩家胜率都只在50%左右&#xff0c;长期下来基本都是不赚不亏&#xff0c;甚至是有小额的亏损。 期权是一种金融衍生工具&#xff0c;它赋予持有人在未来某个时间以…

UWB定位系统常用的定位方法

UWB定位系统是一种无线定位技术&#xff0c;它使用超宽带无线电信号来确定移动设备的位置。与其他定位技术相比&#xff0c;UWB定位系统具有多种优势&#xff0c;例如高精度、低功耗、非视距定位能力和抗干扰能力强等。 UWB定位系统的工作原理是向移动设备发送一系列高频无线电…

如何在桌面同时展示多个窗口

一、实现2分屏显示 win箭头 二、实现3分屏显示 1. 在实现2分屏显示的基础上&#xff0c;再次点击箭头图标&#xff0c;这次选择屏幕的上方或下方。 2. 点击后&#xff0c;第三个窗口将会出现在你选择的区域。现在&#xff0c;你可以在三个窗口之间自由切换&#xff0c;提高工…

品牌渠道管控中的网络维权

在当今竞争激烈的市场环境中&#xff0c;品牌的渠道管控至关重要。其中&#xff0c;对渠道价格的有效管理是关键一环。然而&#xff0c;并非所有低价链接都能通过简单的沟通解决&#xff0c;这使得知识产权维权在渠道管控中成为不可或缺的手段。 那么&#xff0c;究竟什么是品牌…

细说防静电活动地板的分类

防静电地板是一种专门设计用于消除静电荷积累的地板。防静电地板的主要功能是通过将电荷导入大地&#xff0c;使得机房的防静电地板表面不再带有静电离子&#xff0c;从而防止静电对电子设备、人员等的干扰和潜在损害。防静电地板分为直铺防静电地板和架空防静电地板&#xff0…

【方正飞翔-注册/登录安全分析报告-验证结果保存到前端变量导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

网站建站有哪些方法

1. 传统网站建设 特点&#xff1a; 通过手动编写HTML、CSS和JavaScript等前端代码&#xff0c;结合后端开发语言&#xff08;如PHP、Java、.NET等&#xff09;和数据库技术&#xff08;如MySQL、Oracle等&#xff09;来构建网站。 灵活性和可定制性高&#xff0c;可以根据需…

资产负债率、净资产收益率怎么分析?教你弄懂财务报表的关键

财务报表中包含大量的信息&#xff0c;如果我们在解读财务报表时没有思路&#xff0c;不分重点&#xff0c;就很容易被繁杂的数据弄得头晕眼花。本文就财务报表中的关键指标、资产负债率解读、净资产收益率分析、计算销售复合增长率等几个方面进行介绍&#xff0c;大家可以根据…

防止网络环路,98%的网工都是这么操作的

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 我的网工朋友&#xff0c;大家好。 你肯定知道这个经典网络组网模型&#xff1a;接入-汇聚-核心-出口。 接入多数是二层交换机为主&#xff0c;…

Vue3 +Elementplus的学习 (用vite构建项目)

一、创建vite3项目: 1、在某个盘下面新建项目的文件夹,然后右键,在终端打开: 2、输入命令: npm init vite 3、输入项目名称: 4、选择框架vue: 5、选择JavaScript: 6、进入到项目文件夹:

瑞幸因《黑神话:悟空》被骂惨;雷军小米汽车正研究进入欧洲;Meta Quest 3 头显透视摄像头被「破解」| 网易数智日报

今日&#xff0c;网易数智日报来啦~~ 1.刚刚&#xff0c;瑞幸因《黑神话&#xff1a;悟空》被骂惨&#xff0c;A股却“牛”了&#xff01; “官方昨天在直播间明确表示门店一开门就能核销套餐&#xff0c;今天又改口9点之后才能买。”“明明吧台上一堆周边&#xff0c;但是买不…

高维数据检索:局部敏感哈希算法

文章目录 LSH 算法的原理工作原理LSH 模拟实现应用示例 LSH 的优缺点优点缺点实际案例 优化策略存在的挑战与局限性优化方向多哈希策略其他优化点 LSH 在大数据环境中的扩展性与适应性大数据环境下的挑战结合分布式系统的解决方案使用 Apache Spark 进行 LSH 结语 局部敏感哈希…

day24 Java面向对象——什么是面向对象

day24 Java面向对象——什么是面向对象 文章目录 day24 Java面向对象——什么是面向对象1. 什么是面向对象面向对象的核心概念面向对象的优点示例 1. 什么是面向对象 在Java中&#xff0c;面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;是一…

ardupilot开发 --- 故障保护 篇

不患贫而患不均&#xff0c;不患寡而患不安 RC 故障触发因素参数保护动作如何设置关于 SmartRTL、RTL 的返航高度 RC 故障 触发因素 遥控器关机 丢失连接、超出连接RC范围 遥控器油门值低于 FS_THR_VALUE. 遥控接收机(天空端)失去电源 遥控接收机与飞控的连接出现故障 等等…

跨云容灾原来还能这么便捷!

背景 大量组织正在将业务系统迁移到云&#xff0c;以利用其可扩展性。而云上的业务连续性保护依然重要&#xff0c;灾难&#xff0c;病毒&#xff0c;人为错误等仍然在威胁企业数据安全。 云平台被广泛采用作为各种规模企业的首选容灾平台&#xff0c;是 DRaaS 的重要市场驱动…

基于云快充协议1.5-1.6版本的充电桩系统软件-充电桩系统 -新能源车充电平台源码

介绍 SpringBoot 框架&#xff0c;充电桩平台充电桩系统充电平台充电桩互联互通协议云快充协议1.5-1.6协议新能源汽车二轮车公交车二轮车充电-四轮车充电充电源代码充电平台源码Java源码 充电桩平台充电桩系统充电桩小程序充电桩管理系统充电桩项目充电桩协议充电桩微信小程序S…