手把手教你开发第一个HarmonyOS (鸿蒙)移动应用

打印 上一主题 下一主题

主题 995|帖子 995|积分 2985

⼀、移动应⽤开发的介绍

移动应⽤开发:


  • Android
  • IOS
  • HarmonyOS (鸿蒙)

⼆、HarmonyOS介绍

文档概览-HarmonyOS应用开发官网
2.1 系统的界说

2.1.1 系统的定位
HarmonyOS有三⼤特征:


  • 搭载该操作系统的设备在系统层⾯融为⼀体、形成超级终端,让设备的硬件能⼒可以弹性 扩展,实现设备之间 硬件互助,资源共享。 对消费者⽽⾔,HarmonyOS可以或许将⽣活场景中的各类终端进⾏能⼒整合,实现差别终端 设备之间的快速连接、能⼒互助、资源共享,匹配符合的设备、提供流通的全场景体验。
  • ⾯向开发者,实现⼀次开发,多端部署。 对应⽤开发者⽽⾔,HarmonyOS采⽤了多种分布式技术,使应⽤开发与差别终端设备的 形态差异⽆关,从⽽让开发者可以或许聚焦上层业务逻辑,更加便捷、⾼效地开发应⽤。
  • ⼀套操作系统可以满⾜差别能⼒的设备需求,实现统⼀OS,弹性部署。 对设备开发者⽽⾔,HarmonyOS采⽤了组件化的计划⽅案,可根据设备的资源能⼒和业 务特征机动裁剪,满⾜差别形态终端设备对操作系统的要求。
2.1.2 系统架构



  • 内核层:提供⼿机操作系统的基础能⼒。HarmonyOS采⽤多内核的系统计划,基于 Linux内核、LiteOS,使⽤了Linux的微内核(使⽤了Linux的最简功 能)

    • Linux内核: ⼿机操作系统的内核
    • LiteOS内核:智能硬件的内核

  • 系统服务层:HarmonyOS的核⼼能⼒集合,这些能⼒是有系统自己决定的,为我们应⽤ 开发提供了服务调⽤功能。系统服务层提供的能⼒是可以被我们开发的应⽤进⾏调⽤的。
  • 框架层:为HarmonyOS的应⽤开发提供了差别语⾔步伐调⽤的接⼝
2.2 鸿蒙发展史

2.2.1 “鸿蒙”
盘古开天辟地——⼀⽚混沌(鸿蒙时代) ⽴志要在⼿机系统的国产化道路上开天辟地
2.2.2 发展史


  • 2012年,华为开始规划智能操作系统“鸿蒙”
  • 2019年5⽉,华为申请“鸿蒙”商标
  • 2019年5⽉17⽇,发布鸿蒙系统
  • 2019年8⽉,鸿蒙正式版发布,实⾏开源
  • 2020年9⽉,鸿蒙2.0(beta)
  • 2021年6⽉2⽇晚上,华为线上发布HarmonyOS 2.0---⼿机
2.3 鸿蒙与安卓的区别

2.3.1 内核


  • 安卓:基于Linux的内核计划,对Linux的依靠很⼤(也就是说Android操作系统⼤多数功能都 是依靠Linux)
  • 鸿蒙:采⽤了多内核计划,Linux内核+LiteOS内核,操作系统最⼩限度的依靠Linux内核
2.3.2 运⾏服从


  • 安卓:应⽤的运⾏是基于虚拟机的 (Java---JDK编译器---字节码---虚拟机---操作系统)
  • 鸿蒙:⽅⾈编译器 (Java----⽅⾈编译器---机器码---操作系统)
据说, 鸿蒙系统运⾏服从相较于安卓提拔了50%+
2.4 技术特性 与 系统安全

2.4.1 技术特性


  • 硬件互助,资源共享
  • ⼀次开发,多端部署
  • 统⼀OS,弹性部署
2.4.2 系统安全


  • 正确的⼈:⾼效安全的⽤户身份辨认
  • 正确的设备:设备的辨认
  • 正确的使⽤数据:数据的安全
三、第⼀个鸿蒙应⽤

3.1 开发准备

3.1.1 开发情况搭建(Java)


  • 安装JDK、设置情况变量
  • 下载安装DevEco Studio集成开发情况(基于Idea开发的专⻔⽤于鸿蒙应⽤开发的IDE)
下载地址
华为操作系统DevEco Studio和SDK下载与升级 | HarmonyOS开发者
安装


  • 运行安装包
  • 点击Next
  • 选择安装目录
  • 创建桌面快捷方式
  • 开始安装
运行DevEco Studio
  1. 第一次启动开发环境会下载鸿蒙开发所需要的SDK
复制代码


  • 双击桌⾯快捷⽅式,打开DevEco Studio
  • 开始使⽤
  • 选择SDK⽬录
  • 点击Next进⼊下⼀步
  • 选择Accept,点击Next开始下载SDK
  • 点击Finish完成下载,开始使⽤
3.1.2 注册华为帐号
注册_华为帐号 (huawei.com)
3.2 创建鸿蒙应⽤




3.3 鸿蒙应⽤⽬录结构


3.4 运⾏项⽬

3.4.1 启动鸿蒙模拟器


  • Tools—Device Manager
  • 在弹出的窗⼝装点击 Login 按钮,登录华为帐号
  • 启动⼀个模拟器

3.5 鸿蒙应⽤的启动流程

3.5.1 config.json
   config.json 是鸿蒙应⽤的主设置⽂件
  

  • app 设置 :界说当前应⽤的唯⼀标识

    • bundleName应用的唯一标识(一般包名用公司名+应用名称)
    • version 应⽤的版本

  • module 设置: ⽤于声明当前应⽤的信息(包、主类、主界⾯、功能模块声明、适配设 备类型等等)

    • mainAbility声明当前应用启动时默认加载的Ability
    • deviceType声明当前应用适配的设备类型
    • abilities 声明当前应⽤中每个ability的设置信息

      • 创建一个ability那么在config.json中会自动生成对应的ability的设置信息。




3.5.2 应⽤启动流程


  • 启动鸿蒙应⽤加载config.json⽂件,根据 mainAbility 设置加载启动应⽤的主界⾯
    1. "mainAbility": "com.example.myapplication.demo01.MainAbility"
    复制代码
  • 执⾏MainAbility的 onStart ⽅法
    MainAbility是⼀个显示界⾯的容器,在onStart⽅法中通过调⽤ setMainRoute ⽅法来指 定当前界⾯容器中显示的视图界⾯
    1. // ability相当于一个容器(或者说是浏览器窗口),
    2. // 在容器里面通过填充一个视图slice(html中的body)来展示。
    3. // MainAbilitySlice就是⼀个界⾯
    4. super.setMainRoute(MainAbilitySlice.class.getName());
    复制代码
  • 执⾏MainAbilitySlice中的 onStart 完成界⾯的渲染
    slice是⼀个界⾯,界⾯中显示什么视图,就是通过onstart⽅法来加载渲染的


3.5.3 你好,天下哪里来的

$string : 表示引用的一sing,表示要去string文件中探求键值对,如图探求key为:mainability_HelloWorld 的值。
四、Ability框架

4.1 Ability介绍

Ability是应⽤所具备能⼒的抽象,也是应⽤步伐的重要构成部分。⼀个应⽤可以具备多种能⼒
(即可以包含多个Ability),HarmonyOS⽀持应⽤以Ability为单位进⾏部署。
Ability可以分为 FA(Feature Ability) 和PA (Particle Ability) 两种类型,每种类型为
开发者提供了差别的模板,以便实现差别的业务功能。


  • FA⽀持Page Ability:
    Page模板是FA唯⼀⽀持的模板,⽤于提供与⽤户交互的能⼒。⼀个Page实例可以包含⼀
    组相关⻚⾯,每个⻚⾯⽤⼀个AbilitySlice实例表示。
    ​ 

  • PA⽀持Service Ability和Data Ability:

    • Service模板:⽤于提供后台运⾏使命的能⼒。
    • Data模板:⽤于对外部提供统⼀的数据访问抽象。


4.2 PageAbility

4.2.1PageAbility简介
   ⼀个PageAbility相当于⼀个⻚⾯的容器(欣赏器窗⼝),⼀个AbilitySlice相当于显示在容器
  中的⼀个⻚⾯(HTML)
  Page模板(以下简称“Page”)是FA唯⼀⽀持的模板,⽤于提供与⽤户交互的能⼒。⼀个Page可以由⼀个或多个AbilitySlice构成,AbilitySlice是指应⽤的单个⻚⾯及其控制逻辑的总和(相当于⼀个HTML⽂件)。在⼀个Abiliy种可以包含多个Slice
  1. 商品管理:         ProductAbility(PageAbility):
  2. goods-list.html        ProductListSlice
  3. goods-detail.html         ProductDetailSlice
复制代码
4.2.2 创建AbilitySlice


  • AbilitySlice创建slice包中
  • 创建步骤:

    • 创建⼀个类继承 ohos.aafwk.ability.AbilitySlice 类
      1. public class MainAbilitySlice2 extends AbilitySlice {
      2. }
      复制代码
    • 在 resources/base/layout ⽬录下创建布局⽂件
      1. <?xml version="1.0" encoding="utf-8"?>
      2. <DirectionalLayout
      3. xmlns:ohos="http://schemas.huawei.com/res/ohos"
      4. ohos:height="match_parent"
      5. ohos:width="match_parent"
      6. ohos:orientation="vertical">
      7. <Text
      8. ohos:height="match_content"
      9. ohos:width="match_content"
      10. ohos:text="这是测试⽂本"
      11. ohos:text_color="#ff0000"
      12. ohos:text_size="40vp"></Text>
      13. </DirectionalLayout>
      复制代码
    • 在创建的 AbilitySlice 类中重写 onStart ⽅法,调⽤setUIContent⽅法加载布局⽂件
      通过 ResourceTable 加载resouces⽬录下的资源
      1. public class MainAbilitySlice2 extends AbilitySlice {
      2.     @Override
      3.     protected void onStart(Intent intent) {
      4.         super.onStart(intent);
      5.                 //定义slice视图组件(Java,XML)
      6.                 //setUIContent(int); 加载应⽤的 布局⽂件(xml) 作为当前slice的视图
      7.                 setUIContent(ResourceTable.Layout_ability_main_slice2);
      8.         }
      9. }
      复制代码

4.2.3 PageAbility⽣命周期
   ⼀个Page中可以包含多个Slice,但是只能同时显示⼀个slice,怎样设置PageAbility默认
  显示的slice?
  在⼀个Page Ability中提供了多个声明周期⽅法,这些⽅法在当前PageAbility加载的差别
  阶段会⾃定调⽤
  
   说明
  INACTIVE状态是一种短暂存在的状态,可明白为“激活中”。
  1. public class MainAbility extends Ability {
  2.      /**
  3.       * 当系统⾸次创建当前PageAbility实例时,⾃动调⽤onstart⽅法。也就是说对于⼀个Page⽽⾔,onStart⽅法只会执⾏⼀次
  4.       */
  5.      public void onStart(Intent intent) {
  6.              super.onStart(intent);
  7.              super.setMainRoute(MainAbilitySlice2.class.getName());
  8.              System.out.println("--------------onStart");
  9.      }
  10.    
  11.      /**
  12.       * 当前Page进⼊active状态进⼊到⼿机前台获取焦点时,会触发onActive⽅法的执⾏
  13.       */
  14.      protected void onActive() {
  15.              super.onActive();
  16.              System.out.println("--------------onActive");
  17.         }
  18.    
  19.      /**
  20.       * 当前PageAbility失去焦点(⻚⾯切⼊到后台、切换到其他Page),触发onInactive⽅法的执⾏
  21.       */
  22.      protected void onInactive() {
  23.              super.onInactive();
  24.              System.out.println("--------------onInactive");
  25.      }
  26.    
  27.      /**
  28.       * 当前PageAbility切换到后台,不可⻅时,触发onBackground执⾏
  29.       */
  30.      protected void onBackground() {
  31.              super.onBackground();
  32.              System.out.println("--------------onBackground");
  33.      }
  34.      /**
  35.       * 当PageAbility从后台不可⻅状态(⻚⾯在后台、但是没销毁)切换到前台可⻅状态时触发 onForeground执⾏
  36.       */
  37.      protected void onForeground(Intent intent) {
  38.              super.onForeground(intent);
  39.              System.out.println("--------------onForeground");
  40.      }
  41.      /**
  42.       * 当前Page销毁时,触发onStop的执⾏
  43.       */
  44.      protected void onStop() {
  45.              super.onStop();
  46.              System.out.println("--------------onStop");
  47.      }
  48. }
复制代码
4.2.4 Slice的两种渲染方式
   Slice相当于一个网页,Slice中显示的视图是通过组件来声明的,Slice中的组件加载支持两种方式:
  

  • Java代码
  • xml布局文件
  onStart方法:在Slice实例创建时执行,用于载入当前Slice的视图组件,在onStart方法中通过调用setUIContext来加载视图组件
   setUIContext方法提供了2个重载:
  setUIContext(int):通过布局⽂件的ID,加载resources/base/layout⽬录下的布局⽂件
  完成⻚⾯的渲染
  setUIContext(ComponentContainer) :通过加载⼀个使⽤Java代码创建的组件完成⻚⾯
  的渲染
  XML****⽅式渲染


  • 创建布局⽂件:ability_main_slice2.xml
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <DirectionalLayout
    3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
    4.     ohos:height="match_parent"
    5.     ohos:width="match_parent"
    6.     ohos:orientation="vertical">
    7.     <Text
    8.         ohos:height="match_content"
    9.         ohos:width="match_content"
    10.         ohos:text="这是测试文本"
    11.         ohos:text_color="#ff0000"
    12.         ohos:text_size="40vp"></Text>
    13. </DirectionalLayout>
    复制代码
  • 在Slice的onStart⽅法中加载布局⽂件:
    每创建一个布局文件(大概说每个资源都会创建一个ID)都会生成一个ID,所以布局文件调用的是setUIContext(int)方法

    1. @Override
    2. protected void onStart(Intent intent) {
    3.     super.onStart(intent);
    4.     // 定义slice视图组件(java,xml)
    5.     // setUIContent(int); 加载应用的 布局文件(xml)作为当前slice的试图
    6.     super.setUIContent(ResourceTable.Layout_ability_main_slice2);
    7. }
    复制代码
Java⽅式渲染


  • 使⽤Java代码创建组件,渲染到slice中
    1. public class MainAbilitySlice2 extends AbilitySlice {
    2.     @Override
    3.     protected void onStart(Intent intent) {
    4.         super.onStart(intent);
    5.         // 定义slice视图组件(java,xml)
    6.         // setUIContent(int); 加载应用的 布局文件(xml)作为当前slice的试图
    7. //        super.setUIContent(ResourceTable.Layout_ability_main_slice2);
    8.         // ComponentContainer(组件容器)
    9.         DirectionalLayout directionalLayout = new DirectionalLayout(this);
    10.         directionalLayout.setOrientation(Component.DRAG_VERTICAL);
    11.         // Component(组件)
    12.         Text text = new Text(this);
    13.         text.setText("Hello Boys");
    14.         text.setHeight(40);
    15.         text.setTextSize(40);
    16.         // 将组件放到组件容器中
    17.         directionalLayout.addComponent(text);
    18.         // 将组件容器渲染到slice中
    19.         setUIContent(directionalLayout);
    20.     }
    21. }
    复制代码
4.2.5 AbilitySlice间导航
   ⼀个PageAbility可以包含多个Slice,同⼀时刻只能显示⼀个Slice,但是可以在差别的
  Slice之间进⾏跳转——AbilitySlice间的导航
  



  • 在MainAbilitySlice中添加按钮,并监听按钮的点击事件(略 参考4.3)
  • 创建SecondAbilitySlice(略)
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <DirectionalLayout
    3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
    4.     ohos:height="match_parent"
    5.     ohos:width="match_parent"
    6.     ohos:orientation="vertical">
    7.     <!-- 将图片文件存到resource/base/media目录 PS:文件的名字不要用数字开头,因为名字会作为变量名 -->
    8.     <Image
    9.         ohos:height="match_content"
    10.         ohos:width="match_parent"
    11.         ohos:image_src="$media:cat">
    12.     </Image>
    13. </DirectionalLayout>
    复制代码
    1. public class SecondAbilitySlice extends AbilitySlice {
    2.     @Override
    3.     protected void onStart(Intent intent) {
    4.         super.onStart(intent);
    5.         this.setUIContent(ResourceTable.Layout_ability_second);
    6.     }
    7. }
    复制代码
  • 在MainAbilitySlice中监听按钮的点击事件,导航到SecondAbilitySlice
    1. public class MainAbilitySlice extends AbilitySlice {
    2.     @Override
    3.     public void onStart(Intent intent) {
    4.         super.onStart(intent);
    5.         // 在当前slice中渲染试图组件有2中方式:
    6.         // 1. 基于Java代码的渲染
    7.         // 2. 基于xml标签渲染,例如: super.setUIContent(ResourceTable.Layout_ability_main);
    8.         super.setUIContent(ResourceTable.Layout_ability_main);
    9.         // 1. 获取id=btn1的按钮组件
    10.         Button btn1 = (Button) this.findComponentById(ResourceTable.Id_btn1);
    11.         // 2.设置按钮事件监听
    12.         MainAbilitySlice _this = this;
    13.         // a.创建事件监听器
    14.         Component.ClickedListener clickedListener = new Component.ClickedListener() {
    15.             @Override
    16.             public void onClick(Component component) {
    17.                 //挑转到SecondAbilitySlice
    18.                 // 从this指代的当前slice跳转到new的slice中
    19.                 _this.present(new SecondAbilitySlice(), new Intent());
    20.             }
    21.         };
    22.         // b.设置组件的事件监听
    23.         btn1.setClickedListener(clickedListener);
    24.     }
    25. }
    复制代码
    简化版本
    1. public class MainAbilitySlice extends AbilitySlice {
    2.     @Override
    3.     public void onStart(Intent intent) {
    4.         super.onStart(intent);
    5.         // 在当前slice中渲染试图组件有2中方式:
    6.         // 1. 基于Java代码的渲染
    7.         // 2. 基于xml标签渲染,例如: super.setUIContent(ResourceTable.Layout_ability_main);
    8.         super.setUIContent(ResourceTable.Layout_ability_main);
    9.         // 1. 获取id=btn1的按钮组件
    10.         Button btn1 = (Button) this.findComponentById(ResourceTable.Id_btn1);
    11.         
    12. //        //b.设置组件的事件监听 PS:简化成lambda表达示
    13.         // PS:简化成lambda表达式 因为listener要实现onclick方法
    14. //        btn1.setClickedListener(clickedListener->{
    15. //            present(new SecondAbilitySlice(), new Intent() );
    16. //        });
    17.         
    18.         // b.设置组件的事件监听  因为只有一个方法,可以省略大括号
    19.         btn1.setClickedListener(clickedListener -> present(new SecondAbilitySlice(), new Intent()));
    20.     }
    21. }
    复制代码
4.2.6 Slice之间的传值问题
   使⽤Intent对象实现slice间的传值
  



  • MainAbilitySlice
    1. public class MainAbilitySlice extends AbilitySlice {
    2.     @Override
    3.     public void onStart(Intent intent) {
    4.         super.onStart(intent);
    5.         super.setUIContent(ResourceTable.Layout_ability_main);
    6.         // 1. 获取id=btn1的按钮组件
    7.         Button btn1 = (Button) this.findComponentById(ResourceTable.Id_btn1);
    8.         // b.设置组件的事件监听
    9.         btn1.setClickedListener(clickedListener -> {
    10.             // 在跳转之前的slice将需要传递的数据设置到Intent对象中
    11.             Intent intent1 = new Intent();
    12.             intent1.setParam("productId", "101");
    13.             present(new SecondAbilitySlice(), intent1);
    14.         });
    15.     }
    16. }
    复制代码
  • SecondAbilitySlice
    1. public class SecondAbilitySlice extends AbilitySlice {
    2.     @Override
    3.     protected void onStart(Intent intent) {
    4.         super.onStart(intent);
    5.         this.setUIContent(ResourceTable.Layout_ability_second);
    6.         // 在跳转后的slice的onStart方法中,从intent对象中获取数据
    7.         if (intent != null) {
    8.             // 因为intent.getParam()返回的是IntentParams, 所以先获取IntentParams然后再获取值
    9.             IntentParams params = intent.getParams();
    10.             String productId = (String) params.getParam("productId");
    11.             // 获取到id=text1的文本组件
    12.             Text text = (Text) findComponentById(ResourceTable.Id_text1);
    13.             // 将获取到的商品ID设置到text文本组件
    14.             text.setText(productId);
    15.         }
    16.     }
    17. }
    复制代码
4.3 组件的事件监听

4.3.1 在MainAbilitySlice布局文件添加按钮
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
  4.     ohos:height="match_parent"
  5.     ohos:width="match_parent"
  6.     ohos:alignment="center"
  7.     ohos:orientation="vertical">
  8.     <Image
  9.         ohos:height="match_content"
  10.         ohos:width="match_content"
  11.         ohos:image_src="$media:icon">
  12.     </Image>
  13.     <Text
  14.         ohos:id="$+id:text_helloworld"
  15.         ohos:height="match_content"
  16.         ohos:width="match_content"
  17.         ohos:background_element="$graphic:background_ability_main"
  18.         ohos:layout_alignment="horizontal_center"
  19.         ohos:text="$string:mainability_HelloWorld"
  20.         ohos:text_size="40vp"/>
  21.     <Button
  22.         ohos:id="$+id:btn1"
  23.         ohos:height="match_content"
  24.         ohos:width="match_parent"
  25.         ohos:padding="5vp"
  26.         ohos:text="点我试试!"
  27.         ohos:text_size="40vp"
  28.         ohos:background_element="#a0b0c0"></Button>
  29. </DirectionalLayout>
复制代码
当我们在Button利用$+id:btn1时,这个组件就会在ResourceTable中产生一个唯一标识

4.3.2 监听按钮的点击事件


  • 在加载布局文件的Slice类中,获取按钮组件,设置点击事件的监听器
    1. package com.example.myapplicationdemo01.slice;
    2. import com.example.myapplicationdemo01.ResourceTable;
    3. import ohos.aafwk.ability.AbilitySlice;
    4. import ohos.aafwk.content.Intent;
    5. import ohos.agp.components.Button;
    6. import ohos.agp.components.Component;
    7. public class MainAbilitySlice extends AbilitySlice {
    8.     @Override
    9.     public void onStart(Intent intent) {
    10.         super.onStart(intent);
    11.         // 在当前slice中渲染试图组件有2中方式:
    12.         // 1. 基于Java代码的渲染
    13.         // 2. 基于xml标签渲染,例如: super.setUIContent(ResourceTable.Layout_ability_main);
    14.         super.setUIContent(ResourceTable.Layout_ability_main);
    15.         // 1. 获取id=btn1的按钮组件
    16.         Button btn = (Button) this.findComponentById(ResourceTable.Id_btn1);
    17.         // 2.设置按钮事件监听
    18.         // a.创建事件监听器
    19.         Component.ClickedListener clickedListener = new Component.ClickedListener(){
    20.             @Override
    21.             public void onClick(Component component) {
    22.                 System.out.println("--------触发了点击事件");
    23.             }
    24.         };
    25.         // b.设置组件的事件监听
    26.         btn.setClickedListener(clickedListener);
    27.     }
    28. }
    复制代码
  • 可以利用同一个监听器监听多个组件的事件,如果点击差别的组件执行的业务差别,则可以通过组件判断来执行差别的业务
    1. // 1. 获取id=btn1的按钮组件
    2. Button btn1 = (Button) this.findComponentById(ResourceTable.Id_btn1);
    3. Button btn2 = (Button) this.findComponentById(ResourceTable.Id_btn2);
    4. // 2.设置按钮事件监听
    5. // a.创建事件监听器
    6. Component.ClickedListener clickedListener = new Component.ClickedListener(){
    7.     @Override
    8.     public void onClick(Component component) {
    9.         // Component参数 表示监听的组件
    10.         if (component ==  btn1) {
    11.             System.out.println("--------aaa");
    12.         } else if (component == btn2) {
    13.             System.out.println("~~~~~~~~bbb");
    14.         }
    15.     }
    16. };
    17. // b.设置组件的事件监听
    18. btn1.setClickedListener(clickedListener);// 输出aaa
    19. btn2.setClickedListener(clickedListener);// 输出bbb
    复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

不到断气不罢休

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表