钉钉开放平台-小程序开发实战(钉钉小程序客户端)

钉钉小程序客户端

关于钉钉开放平台

钉钉(Ding Talk)是阿里巴巴集团打造的企业级智能移动办公平台,是数字经济时代的企业组织协同办公和应用开发平台。

钉钉开放平台具有强大丰富的原生能力,目前已经开放超过2000个API接口,同时为开发者和服务商提供专业的技术培训体系,分享最新功能和开放能力,提供全面的服务支持。

一、小程序基础

1. 基本概念

参考官网: https://ding-doc.dingtalk.com/doc#/dev/framework-app

App代表顶层应用,管理所有页面和全局数据,以及提供生命周期方法。它也是一个构造方法,生成App实例。一个小程序就是一个App实例。

Page代表应用的一个页面,负责页面展示和交互。每个页面对应一个子目录,一般有多少个页面,就有多少个子目录。它也是一个构造函数,用来生成页面实例。

2. 小程序页面基础

【必看】官网: https://opendocs.alipay.com/mini/framework/page

页面运行机制

Page(object: Object)
在 /pages 目录的 .js 文件中,定义 Page(),用于注册一个小程序页面,接受一个 object 作为属性,用来指定页面的初始数据、生命周期回调、事件处理等。

页面生命周期

小程序主要靠视图线程(Webview)和应用服务线程(Worker)来控制管理。视图线程和应用服务线程同时运行。

  • 应用服务线程启动后运行 app.onLauch 和 app.onShow 以完成 App 创建,再运行 page.onLoad 和 page.onShow 以完成 Page 创建,此时等待视图线程初始化完成通知。

  • 视图线程初始化完成通知应用服务线程,应用服务线程将初始化数据发送给视图线程进行渲染,此时视图线程完成第一次数据渲染。

  • 第一次渲染完成后视图线程进入就绪状态并通知应用服务线程,应用服务线程调用 page.onReady 函数并进入活动状态。

  • 应用线程进入活动状态后每次数据修改将会通知视图线程进行渲染。当切换页面进入后台,应用线程调用page.onHide 函数后,进入存活状态;页面返回到前台将调用 page.onShow 函数,进入活动状态;当调用返回或重定向页面后将调用 page.onUnload 函数,进行页面销毁。

生命周期函数:

  • onShow()
    页面显示/切入前台时触发。

  • onReady()
    页面初次渲染完成时触发。 一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。 对界面的设置,如 my.setNavigationBar 请在 onReady 之后设置。

  • onHide()
    页面隐藏/切入后台时触发。 如 my.navigateTo 到其他页面或底部 tab 切换等。

  • onUnload()
    页面卸载时触发。 如 my.redirectTo 或 my.navigateBack 到其他页面等。

页面栈

参考URL: http://www.imooc.com/article/290548

getCurrentPage()函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。

框架以栈的形式维护当前的所有页面。路由切换与页面栈的关系如下:

在这里插入图片描述

页面跳转

<!-- sample.axml -->
<view class="btn-area">
  <navigator url="/page/navigate/navigate?title=navigate" hover-class="navigator-hover">跳转到新页面</navigator>
  <navigator url="../../redirect/redirect/redirect?title=redirect" open-type="redirect" hover-class="other-navigator-hover">在当前页打开</navigator>
  <navigator url="/page/index/index" open-type="switchTab" hover-class="other-navigator-hover">切换 Tab</navigator>
</view>

1、最常见的dd.navigateTo({})
保留当前页面,跳转到应用内的某个页面,使用 wx.navigateBack 可以返回;
2、dd.redirectTo()
关闭当前页面,跳转到非tabBar的某个页面
3. dd.switchTab ,跳转到tabBar的某个页面,
总结: 常用navigateTo方法。 保留当前页面,跳转到应用内的某个页面,使用 wx.navigateBack 可以返回;

怎么使用小程序的data-*属性?

怎么使用小程序的data-*属性?
参考URL: https://www.cnblogs.com/dawenyang/p/9039495.html

data-* 自定义属性。
当事件触发时,会将自定义属性传递给事件处理函数。

在组件中可以定义数据, 书写方式: 以data-开头,多个单词由连字符-链接,不能有大写(大写会自动转成小写)如data-element-type,最终在 event.currentTarget.dataset 中会将连字符转成驼峰elementType。

<view data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>
Page({
  bindViewTap:function(event){
    event.currentTarget.dataset.alphaBeta === 1 // - 会转为驼峰写法
    event.currentTarget.dataset.alphabeta === 2 // 大写会转为小写
  }
})

注意: 组件触发事件对象event,有两个target、crrentTarget,其中currentTarget代表当前组件。通过 event.currentTarget.dataset 下获取我们 组件data-* 绑定的数据。

3. 小程序常用布局

【推荐-作者写的全面完整】小程序开发之页面布局
参考URL: https://www.cnblogs.com/flywong/p/9503069.html

**Flex布局又称弹性布局,在小程序开发中比较适用。**因此将Flex布局相关属性整理如下,搞清楚了这个布局,小程序开发的页面布局就不在话下了。

布局时处处用到的flex属性 display: flex; 这个基本是小程序中最常见的一行布局代码了。

1. Flex布局基础

Flex布局,是W3c在2009年提出的一种新的方案,可以简便,完整,响应式的实现各种页面布局。

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

在这里插入图片描述如上图所示:
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。

项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

总结:main轴就是水平轴,cross轴就是垂直轴。从左到右就是main start 到main end,从上到下就是cross start到cross end。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

2. Flex布局测试

1) 水平部署
display:block 指定为块内容器模式,总是使用新行开始显示,小程序的视图容器(view,scroll-view和swiper)默认都是dispaly:block。

<view class="flex-row" style="display: block;">
  <view class="flex-view-item">1</view>
  <view class="flex-view-item">2</view>
  <view class="flex-view-item">3</view>
</view>

结果如下:block改换成display:flex的显示效果:
在这里插入图片描述
可以从效果图看到block和flex的区别,子元素view是在换行显示(block)还是行内显示(flex)。

2) 让水平部署的元素,平分

  <view class="btn-area">
    <button formType="submit">Submit</button>
    <button formType="reset">Reset</button>
  </view>

设置样式如下:

.btn-area {
    display:flex;
}

显示结果
在这里插入图片描述

justify-content容器属性
justify-content属性定义了项目在主轴上的对齐方式。

.container {
  justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
}

二、小程序组件

参考官网:https://opendocs.alipay.com/mini/component/overview

小程序框架为开发者提供了一系列基础组件,开发者可以通过组合这些基础组件进行业务开发。

所有的组件都包含以下属性:
在这里插入图片描述组件触发事件对象event,有两个target、crrentTarget,其中currentTarget代表当前组件。
在这里插入图片描述

1. 常用原生组件

底部导航栏 tabBar

参考官网 :https://ding-doc.dingtalk.com/doc#/dev/framework-app
思路:底部导航栏切换多个页面。所以第一步我们需要定义多个Page。

  1. 使用官方IDE,如下图在Pages目录上右键新建Page。
    在这里插入图片描述2. app.json 配置你新添加页面
    app.json用于全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
    如果你使用IDEA 添加的页面,它会如下自动在app.json添加Page,如下 添加了config

    {
      "pages": [
        "pages/index/index",
        "pages/config/config"
      ],@[TOC](这里写自定义目录标题)
      "window": {
        "defaultTitle": "个人E应用DEMO"@[TOC](这里写自定义目录标题)
      }
    }
    
  2. 添加底部tab
    app.json配置项如下。
    在这里插入图片描述

  • pages属性
    pages属性是一个数组,每一项都是字符串,用来指定小程序的页面。每一项代表对应页面的路径信息,数组的第一项代表小程序的首页。小程序中新增/减少页面,都需要对 pages数组进行修改。

    页面路径不需要写 js 后缀,框架会自动去加载同名的.json、.js、.axml、.acss文件。

  • window属性
    window属性用于设置通用的的状态栏、导航条、标题、窗口背景色。

  • tabBar
    如果你的小程序是一个多 tab 应用(客户端窗口的底部栏可以切换页面),那么可以通过tabBar配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

    注意,通过页面跳转(dd.navigateTo)或者页面重定向(dd.redirectTo)所到达的页面,即使它是定义在 tabBar 配置中的页面,也不会显示底部的 tab 栏。另外,tabBar 的第一个页面必须是首页。

在这里插入图片描述在这里插入图片描述icon 推荐大小为 60*60px 大小,系统会对任意传入的图片非等比拉伸/缩放。

  • 首页显示页面
    <view>
        <view class="row">开盘价:{
         
         {open}}</view>
        <view class="row">收盘价:{
         
         {close}}</view>
        <view class="row">最高价:{
         
         {high}}</view>
        <view class="row">最低价:{
         
         {low}}</view>
    </view>
    

http请求

参考钉钉官网 https://ding-doc.dingtalk.com/doc#/dev/httprequest

```
 dd.httpRequest({
        headers: {
            "Content-Type": "application/json"
        },
        url: huobi_url + "?period=1day&size=1&symbol=btcusdt",
        method: 'GET',
        dataType: 'json',
        success: function(res) {

            debugger
            let tmpData = res.data.data[0];
            _this.setData({
                open:tmpData.open,
                close:tmpData.close,
                high:tmpData.high,
                low:tmpData.low

            })

            dd.alert({content: 'success'});
        },
        fail: function(res) {
            dd.alert({content: 'fail'});
        },
        complete: function(res) {
            //dd.alert({content: 'complete'});
        }
        });
```

循环block标签

微信小程序之循环
参考URL: https://www.cnblogs.com/jianxian/p/11106990.html

1)<block></block>标签

block常用于结合循环
```
<block wx:for="{
   
   {array}}" wx:key="{
   
   {index}}">
      <view class="aaa">{
   
   {item}}</view>
</block>

array: [1, 2, 3, 4, 5]
```

循环结果:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200326112759971.png)

(2)view标签
接下来用view标签进行循环
<view wx:for="{ {array}}" wx:key="{ {index}}"> <view class="aaa">{ {item}}</view> </view>
循环渲染结果:
在这里插入图片描述
总结: block结合循环时,block不会被解析编译到代码中。而其他标签元素则会编译

钉钉dingui-mini   https://www.npmjs.com/package/dingui-mini 官方建议: a:if ,a:for等最好在block组件中使用。

小程序之动态获取元素宽高

微信小程序之动态获取元素宽高
参考URL: https://www.cnblogs.com/zjjDaily/p/9566234.html

var obj=wx.createSelectorQuery();
obj.selectAll('.npl-intro').boundingClientRect();
obj.exec(function (rect) {
    console.log(rect[0].height)
    console.log(rect[0].width)
}) ;

这方法可以写在onLoad、onReady、onShow等这些生命周期的方法,也可以写在自定义的方法里。什么时候需要,什么时候就调用。

注意:如果要获取通过wx:if 和**setData来实现显示与隐藏的元素,调用的这个方法的时候可能出现获取到的内容为null。**我的解决办法是加个定时器:因为这个获取元素的方法是异步的,所以只有拖延点时间再去获取,不然有可能元素还未加载出来,就调用了这个方法,当然返回的结果就是null了。

//动态设置高度
setTimeout(function () {
var query = wx.createSelectorQuery();
query.select(‘.nd-btnBox’).boundingClientRect();
query.exec(function (rect) {
if (rect[0] === null) return;
that.setData({
marginBM: rect[0].height + 10
})
});
}, 500)

2. 常用表单组件

配置 告警信息 规则。

switch 开关选择器

小程序:开关选择器(switch)
参考URL: https://www.jianshu.com/p/58d32d7ac291

属性说明
在这里插入图片描述

  <view class="section section_gap">
    <view class="section__title">告警开关</view>
    <switch type="switch" color="green" checked  name="isNotice"/>
  </view>

在这里插入图片描述

滑动选中器 slider

小程序:滑动选中器 slider
参考URL: https://www.jianshu.com/p/23e20d22b0e4

在这里插入图片描述其中min最小值、max最大值、 value 可以配置默认值。

radio-group

radio-group 为单项选择器,内部由多个组成。

radio-group 默认选中某一项

    <radio-group name="rise">
      <label><radio value="1"/>涨</label>
      <label><radio value="0" checked="true"/>跌</label>
    </radio-group>

在这里插入图片描述

checkbox-group

同radio-group,要设置默认值,加checked="true"即可。如下:

    <checkbox-group name="msgType">
      <label><checkbox value="sms"/>短信</label>
      <label><checkbox checked="true" value="notice"/>钉钉工作通知</label>
    </checkbox-group>

在这里插入图片描述

input 输入框

WeChat小程序表单篇~input
参考URL: https://www.jianshu.com/p/ae1a207a63d6

在这里插入图片描述如上参数描述,如果我们需要设置默认初始值,则设置
value属性即可

  <view class="section">
    <view class="section__title">交易对</view>
    <input name="symbol" value="btcusdt" placeholder="请输入交易对,如 btcusdt" />
  </view>

在这里插入图片描述

选择器

参考官网: https://ding-doc.dingtalk.com/doc#/dev/picker-component

picker 从底部弹起的滚动选择器。

3. 身份验证

官网参考:https://ding-doc.dingtalk.com/doc#/serverapi2/vt6khw

身份验证“免登”是指用户进入应用后,无需输入钉钉用户名和密码,应用程序可自动获取当前用户身份,进而登录系统的流程。

  • 企业应用免登

    当您是为自己的企业/代表一个企业开发全新的“企业应用”,或者把您的企业内部的遗留系统连接到钉钉后形成“企业微应用”后,企业员工在钉钉内使用该企业微应用时,只要直接点击应用,便可免输入账户密码实现自动登录您所开发的系统。

  • 应用管理后台免登

    当您开发完企业或者ISV微应用后,需要企业管理员在oa.dingtalk.com对微应用进行一些设置和管理功能时,您需要开发一套应用的后台管理系统,管理员在oa.dingtalk.com中只要直接点微应用管理后台,便可免输入账户密码实现自动登录您的应用管理后台系统。

  • 扫码登录第三方网站

    当您开发了一个独立的网站,但是希望用户以钉钉的账号登录您的网站时,可以通过钉钉扫码方式实现免密登录此网站。注意此网站并不是钉钉客户端内使用的企业/ISV应用。

  • 钉钉内免登第三方网站

    当您开发的H5网站在钉钉客户端内打开,只需要用户直接点击H5链接,便可以免输入钉钉账户密码实现自动登录的流程。注意此网站并不是钉钉客户端内使用的企业应用/第三方企业应用。

  • 密码登录独立的第三方网站

    当您开发了一个独立的网站,希望以钉钉的账号登录您的网站时,可以通过展现钉钉提供的登录URL的页面,用户输入钉钉账户密码后实现登录您的网站的流程。注意此网站并不是钉钉客户端内使用的企业应用/第三方应用。

总结: 这里我们该demo适应于第一种,企业应用免登。

企业应用免登

其登录流程如下图所示分成4步:
在这里插入图片描述1. 钉钉小程序客户端js 获取授权码
企业应用和个人应用的免登授权码均可通过该JSAPI获取。

dd.getAuthCode({
    success:function(res){
        /*{
            authCode: 'hYLK98jkf0m' //string authCode
        }*/
    },
    fail:function(err){
    }
});

授权码,有效期5分钟,且只能使用一次。
2. 服务器端获取access_token
3. 钉钉客户端发送第一步获取的授权码到业务服务器端。
4. 业务服务器端 通过免登授权码和access_token获取用户的userid。

4. 钉钉小程序拓展UI组件库

dingui-mini 组件:https://www.npmjs.com/package/dingui-mini
mini-ddui(Deprecated 已废弃)
参考URL: http://npm.taobao.org/package/mini-ddui-comps

小程序扩展组件库是 基础组件库 的重要补充,是基于 小程序自定义组件规范 开发的一套开源UI组件库,供小程序开发者快速复用。

mini-ddui readme描述:
Deprecated,请使用 dingui-mini 组件库代替 https://www.npmjs.com/package/dingui-mini

总结:根据官方描述mini-ddui已废弃,现在使用 dingui-mini 。

最后贴出官网体验demo。
在这里插入图片描述

dingui-mini 组件包

1) 安装dingui-mini npm包
钉钉IDE输入 dingui-mini 进行安装。
在这里插入图片描述测试发现IDE直接安装dingui-mini,好像不行,一直报,找不到模块。最后发现问题出在路径上面(躺过一个大坑)。

注意你的node_modules目录在项目根目录下,和pages同级目录。
在这里插入图片描述使用IDE怎么创建这个node_modules呢?如下图
在这里插入图片描述
Yarn是Facebook最近发布的一款依赖包安装工具。Yarn是一个新的快速安全可信赖的可以替代NPM的依赖管理工具。

Yarn

npm 安装yarn
参考URL: https://www.jianshu.com/p/ca79e7ca38a4

Yarn是Facebook发布的一款依赖包安装工具。Yarn是一个新的快速安全可信赖的可以替代NPM的依赖管理工具。

在官方介绍里有这么一句话:

Yarn is a package manager for your code. It allows you to use and share code with other developers from around the world. Yarn does this quickly, securely, and reliably so you don’t ever have to worry.

关键意思就是,快速,安全,可靠。你下载的包将不再重新下载。而且确保在不同系统中可以正常工作。

npm 安装yarn

npm install -g yarn --registry=https://registry.npm.taobao.org
# 再配置下源
yarn config set registry https://registry.npm.taobao.org -g
yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g

总结:未测试,该节可忽略,该项目还是使用npm管理安装包。

2) dingui-mini组件 使用
安装dingui-mini组件库后,在我们将要使用组件的页面json文件中引入dingui-mini组件,例如:

页面 page.json中写入代码(为了区别于原生组件,强烈建议使用dingui-开头 以引入):

注意:是在page.json中,写引入组件!// page.json 注意,不是在app.json里配置。

注意: 网上找 dingui-mini 的doc很难找到,建议参考支付小程序doc,两者基本一样。https://opendocs.alipay.com/mini/component-ext/list

mini-ali-ui 组件包

官方doc https://opendocs.alipay.com/mini/component-ext/ui-overview

小程序扩展组件库 mini-ali-ui, 由原 mini-antui 组件库升级而来,是 基础组件库 的重要补充。mini-ali-ui 是基于 小程序自定义组件规范 开发的一套开源 UI 组件库,供小程序开发者快速复用。mini-antui 目前已停止维护,如需使用原 mini-antui 组件库,请参见 GitHub、获取文档,建议使用 mini-ali-ui。

特性

  • 基于 Alipay Design 设计规范。
  • 支持多端小程序(支付宝,淘宝,钉钉等)。
  • 支持主题配置切换。
  • 支持 px 与 rpx。

注意:官网描述,mini-ali-ui支持钉钉。因此,我们也可以引入 mini-ali-ui。

1)安装dingui-mini npm包

直接通过 IDE 安装使用扩展组件。

或在小程序项目的根目录(根目录是指 app.json 所在的目录)中,通过 npm 进行安装。

$ npm install mini-ali-ui --save

2)dingui-mini 使用

在页面json中文件中进行注册,如card组件的注册如下所示:

“usingComponents”: {
“card”: “mini-antui/es/card/index”,
}

总结:强烈建议参考官方文档,doc 中不关有demo,怎么引入依赖都可以复制粘贴,非常详细。
官方文档:https://opendocs.alipay.com/mini/component-ext/list

3)list 和list-item使用 示例

参考官网: https://opendocs.alipay.com/mini/00c1g1

list-item 共有6个插槽,位置和名称如图表所示:
在这里插入图片描述##### 4)list 和list-item使用 示例
小程序如何设置满屏?
参考URL: https://www.jianshu.com/p/ac9f1cdae193

小程序默认了 page 属性,所以只需设置

 page{
    background: red;
  }

第二种方式
整个屏幕默认满屏为100vh

  .container{
      width: 100%;
      height: 100vh;
  }

6. 过程问题整理或坑

小程序如何获取当前页面的page对象

app的实例是getApp(),那么Page对象呢?

在页面对象中定义的函数里直接用this获取;

var pages = getCurrentPages()    //获取加载的页面
var currentPage = pages[pages.length-1]    //获取当前页面的对象

可以用下面代码测试:

Page({
    
    
    onLoad:function(){
    
    
          //获取加载的页面
        var currentPage = pages[pages.length - 1]    //获取当前页面的对象
        console.log("page:")
        console.log(currentPage===this)    //true
    }
})

微信小程序 this.data与this.setData

微信小程序 this.data与this.setData
参考URL: https://blog.csdn.net/ahilll/article/details/82732741

小程序中我们会经常使用到this.data与this.setData。其中this.data是用来获取页面data对象的,而this.setData是用来更新界面的。

this.data与this.setData的关系就是this.setData里面存储的是this.data的副本,而界面是从this.setData里面托管的this.data的副本取数据的。所以我们更改this.data并不会直接更新界面,因为这个时候的this.setData里面的副本还是没有更新前的。

this.setData({})用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)。用this.data而不用this.setData({})会造成页面内容不更新的问题。

form标签hidden元素不起作用

form标签hidden元素不起作用,改用view 如下,测试通过。

<view hidden="{
    
    {showRuleFormView}}">

this的值,使用console.log打印正常,但debugger时却是undefined

参考URL: https://stackoverflow.com/questions/54712662/react-this-is-undefined-in-chrome-developer-tools
https://segmentfault.com/q/1010000020303049

问题分析:大概就是浏览器自身的问题,大概和chrome使用babel这种第三方的解析工具来解析成ES5的语法有关,
Due to how lexical this is treated by Babel in arrow functions, it cannot be this inside an arrow. Temporary variables like _this, _this2, etc. are used to imitate lexical this in ES5.

解决方法:如下,把this赋值到 _this 变量,此时你debugger出来了!

        dd.httpRequest({
    
    
          url: url,
          method: 'POST',
          data: {
    
    
            authCode: res.authCode
          },
          dataType: 'json',
          success: (res) => {
    
    
            // dd.alert({content: "step2"});
            console.log('success----', res)
            let userId = res.data.result.userId;
            let userName = res.data.result.userName;
            debugger
            console.log(this);
            let _this = this;
            debugger

dd.getAuthCode 的http请求是异步的

经过测试, dd.getAuthCode 的http请求是异步的。

它应该是为了效率,估计它所有的 http请求( dd.httpRequest)默认都是异步的。

解决思路:
回调函数执行,后一个方法写到前一个的回调函数中从而实现顺序执行,缺点是嵌套太多,代码混乱。

三、钉钉/支付宝小程序和微信小程序的区别

官网描述: https://opendocs.alipay.com/mini/framework
钉钉/支付宝小程序和微信小程序的区别及转换方案
参考URL: https://www.cnblogs.com/dora-zc/p/10963644.html

钉钉小程序和支付宝小程序几乎没有差别,最主要的是支付宝小程序全局变量 my, 钉钉小程序对应为 dd。
钉钉小程序的文档不如支付宝小程序详细,所以看文档直接看支付宝小程序的就可以了。

  1. 文件后缀区别
    微信是:axml、wxss
    钉钉是:wxml、acss
  2. import路径
    钉钉可以引用绝对路径,但是微信在wxml和js中只能引用相对路径
  3. 模板语法
    钉钉小程序 a
    微信小程序 wx

…等,具体参考官网: https://opendocs.alipay.com/mini/framework

四、上传应用(客户端版本上线)

登录钉钉开放平台,看到如下显示,提醒我们应用使用IDE上传,
在这里插入图片描述打开IDE,如下点击上传
在这里插入图片描述上传完成,再次打开钉钉开放平台,可以看到如下图,显示已上传。
在这里插入图片描述
5. 提交审核
如下图,可以选择提交审核。点击更多

在这里插入图片描述
注意:企业内建应用好像没有这个审核,直接点击发布。

猜你喜欢

转载自blog.csdn.net/inthat/article/details/125456338