mini小程序入坑说明

简介: mini小程序入坑说明

mini小程序入坑说明


开发者ID - 小程序 - 开发-开发管理 - 开发设置

代码上传 - 小程序 - 管理-版本管理-查看提交的信息

小程序介绍:

当微信中的 WebView 逐渐成为移动 Web 的一个重要入口时,微信就有相关的 JS API 【微信打开html网页】

微信小程序,2016年末推出,2017年初第一批小程序正式上线

SDK software-Development-Kit 软件开发工具包

小程序与普通网页开发的区别

小程序的主要开发语言是 JavaScript

网页开发渲染线程和脚本线程是互斥的, 而在小程序中,二者是分开的分别运行在不同的线程中。

网页开发可使用DOM API,小程序开发缺少相关的DOM API和BOM API

网页开发主要考虑浏览器兼容,小程序开发主要考虑的是安卓与iOS两大操作系统

第一个小程序

?问你开发工具 : 微信开发者工具

打开微信开发者工具 - 微信扫一下 - 注册的账号登录 - 项目名字 - 项目目录 - AppleID - 不使用云服务 - js | ts - 基础模板 -

下载微信开发者工具 并安装,使用微信扫码登录工具,创建小程序项目。

创建小程序应用时,选择填写自己的 AppID,先不使用云开发,选择最基础的模板即可。

可先认识一下开发者工具的界面结构

四类文件

每个文件夹下的四类文件名称需要一致

.json 后缀的 JSON 配置文件

.wxml 后缀的 WXML 模板文件

.wxss 后缀的 WXSS 样式文件

.js 后缀的 JS 脚本逻辑文件

JSON配置

程序中,后缀名为 *.json 的文件是静态配置文件

app.json

小程序的全局配置文件

pages字段:是小程序中所有页面路径(routes)配置,是数组结构。默认情况下,将数组的第一个元素表示的路径作为小程序应用的”首页“

默认将pages数组中第一项作为小程序首页,否则可设置 entryPagePath

window字段:小程序应用窗口的全局配置。 所有页面的顶部背景颜色,文字颜色定义等

enablePullDownRefresh:true, // 每个页面都允许下拉刷新功能

1

小程序最大为20M,每个分包2M

tabbar字段:| 自定义

2-5项tab,初始图标,选中激活图标,颜色

图表大小推荐 81*81

sitemap.json

配置是否允许微信对小程序页面进行索引

页面局部设置:navigationBarTitleText:‘页面标题’

WXML

充当的是 HTML 文件

WXML 中标签于 HTML 标签名字不一样,在标签中可使用类似 wx:if 的属性,{{ }} 插值的语法,

WXSS

充当的是css文件

书写同css规则,新增了 rpx 单位(响应式的单位),避免的换算困难(rem换算)。app.wxss文件中,提供了全局样式文件的处理

可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像

JS文件

主要负责交互逻辑

app.js

app.js 文件负责的是注册小程序应用

调用 App() 函数来注册小程序应用:

App({
  /**
   * 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
   */
  onLaunch: function () {
  },
  /**
   * 当小程序启动,或从后台进入前台显示,会触发 onShow
   */
  onShow: function (options) {
  },
  /**
   * 当小程序从前台进入后台,会触发 onHide
   */
  onHide: function () {
  },
  /**
   * 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
   */
  onError: function (msg) {
  }
})

onLaunch()、onShow()、onHide() 是小程序应用的生命周期钩子函数

页面 js

注册页面,调用 Page() 函数来注册页面:

Page({
  /**
   * 页面的初始数据,类似于 react class 组件中的 state。
   * 要修改 data 中的数据,调用 setData() 方法来修改
   */
  data: {
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
  }
})

taotao-原生小程序开发

小程序开发工具坑很多-慢慢尝试-重开

构建页面结构:首页/购物车/分类/登录…

app.json 配置 pages字段 、 window全局窗口配置、tabbat/list属性配置、

/images/tab-bar-icon/imgs.png存放图标渲染

navigationStyle:custom //自定义导航条样式
onReachBottomDistance:number //上拉处理距离底部高度
subpackage:object[] //分包
perimission:object //权限获取

布局-wxml文件

view标签 =》 div 视图容器

swiper滑块视图容器 swiper-item轮播每一项

scroll-view滚动视图

分类滚动视图

<scroll-view
scroll-x = "true" //字符串
scroll-x = "{{ true }}" //布尔值
/>
<!-- 列表渲染 item固定名称--> 
<text 
      wx:for= “{{ cat }}”
      wx:key= "id"
>
    <!--item代表名称 index代表索引-->
    {{item}} 分类名称 {{index}}
</text>

列表渲染:绑定 key 提高性能

换行解决 white-space

发网络请求

index.js

data:{
    //初始数据
    cat:[
        {}
    ]
}

index.wxss

-写样式代码

网络请求

API: wx.request( object )

wx.request({
    url:'',
    success:res=>{
        //数据处理-状态为200,处理返回数据
        if(res.statusCode===200){
            const { code,data } = res.data
            if(code ===200 ){
                //调用setData修改数据
                this.setData(
                  cat:data.list
                )
            }
        }
    },
    fail:err=>{},
})

小程序跨域-详情-本地设置-不校验本地合法域名勾选

项目上线后,只能发https请求

多次请求繁琐,实现 axios 封装

wx.request 不支持promise写法

utils/request.js 二次封装

commonJS的规范 – module.exports导出 require()

/**
 * wx.request() 方法的二次封装处理
 */
const baseURL = 'http://quanzhan.site:3000'
const request = ({url, data, header = {}, method = 'GET'} = {}) => {
  // 新 new Promise() 创建的 Promise 对象的状态是 pending 待定状态
  return new Promise((resolve, reject) => {
    wx.request({
      url: baseURL + url, // 在访问接口前拼接统一的 baseURL
      data, // 请求发送给后端的数据
      header: { // 请求头
        ...header,
        'X-Token': 'token string.............', // 携带 token 认证字段
      },
      timeout: 5000, // 请求超时时间
      method, // 请求方法,默认为 GET
      success: res => { // 请求成功的回调函数
        if (res.statusCode === 200) { // HTTP状态码为 200 表示 OK
          // res.data 中保存的是后端向前端返回的数据
          // 通常后端与前端交互时有统一的规范,比如返回一个对象,有 code 与 data、message 属性。
          // 当前接口规范中,code 为 200 表示后端向前端返回成功的数据。
          // 返回的数据在 data 属性中。
          const {code, data} = res.data
          if (code === 200) {
            resolve(data) // 将 Promise 的状态修改为 fulfilled 成功状态,并携带成功的数据
            return
          }
        }
        // 如果 HTTP 状态码或后端返回的 code 表示有误时,将
        // Promise 状态修改为 rejected 失败状态,携带失败原因。
        const error = new Error('接口访问有误')
        reject(error)
      },
      fail: err => { // 请求失败
        // 将 Promise 状态标记为失败状态
        reject(err)
      },
    })
  })
}
// 利用 CommonJS 规范中的 module.exports 导出当前文件中定义的 request 方法
module.exports = request

轮播图swiper

<swiper
  indicator-dots //显示面板指示点
  autoplay //自动播放
  duration //500m内完成滑动
  ...
>  
------------------------------------------------
  <!-- 轮播图 -->
    <swiper
      indicator-dots
      autoplay
    >
      <block wx:for="{{ banners }}" wx:key="id">
      <swiper-item>
        <image src="{{item.image}}" style="width:100%;height:100%"></image>
      </swiper-item>
      </block>
    </swiper>

block标签类似<></>

block` 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性

image标签默认宽高 320*240

if(this.data.isEnd){
      console.log('到底了');
      return 
    }
    console.log('...');
    //触底发送网络请求
    let data = await request({
      url:'/api/tab/1/feeds?start=' + this.data.nextIndex
    })
    console.log('触底:',data);
    this.setData({
      //!this.date.list
      productList:this.data.productList.concat(data.list),
      isEnd:data.isEnd,
      nextIndex:data.nextIndex,
    })

if vs hidden

wx:if vs hidden

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。

同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好

<view wx:if="{{ !isEnd }}">加载中...</view>
<view wx:else="{{ isEnd }}">我是有底线的!!</view>

点击商品详情跳转-

navigator组件
<navigator to="/....">
// 跳转到其他小程序 - 需要app-id配置
// 跳转到当前小程序其他页面
//图像绑定点击事件 300ms之内为短按 tap 长按longpress
bindtap = "handleBing"
data-id = "{{ item.id }}" !!
js文件中:添加属性 js逻辑使用 ---
handleBing:(event)=>{
    //event.target.dataset => data-自定义的数据获取
    //编程式导航跳转 -API路由
    //=》什么是tabbar页面 - 不能跳到tabbar页面 - tabbar绑定的页面
    wx.navigatorTo({
        url:'/...?id=' + event.target.dataset.id
    })
}

拿参=》页面跳转传参加载时,在新页面的onLoad 函数中拿到 query 参数

传参=》?查询字符串,navigation的 url属性 ? 拼接传参

data- 自定义的属性前缀 通过event.target.dataset

传参:

 <navigator url="/pages/details/index?id={{item.id}}">
 <text bindtap="handleDetail" data-id="{{item.id}}">{{item.title}}</text>
   // 点击跳转函数
  handleDetail:(event)=>{
    console.log('.....',event.target);
    wx.navigateTo({
      url:'/pages/details/index?id=' + event.target.dataset.id 
    })
  }

获取参数:新页面的onLoad参数:

 /**
  * 生命周期函数--监听页面加载
  */
  onLoad: function (query) {
    console.log('details:id',query);
  },

基础组件

编写 wxml 结构时,使用到的基本标签

view

scroll-view

swiper 、 swiper-item

image

text

navigator 类似 a

block 分组

API

wx.request()

wx.navigatorTo() 跳转非 tabbar 页面 【编程式 ?传参】

wxml

和 vue 模板语法类似

绑定数据 {{ expression }}

绑定事件

bindtap 【冒泡】catchtap 【阻止当前事件冒泡】、

bindtap

capture-bind 【传播】capture-catch 【阻止传播】

跳转到tabbar页面

 <!-- 点击跳转到分类页面0tabbar页面 -->
      <text
        bindtap="handleCategory"
        wx:for="{{ categories }}"
        wx:key= "id"
      >{{item.name}}</text>

导航跳转到分类页面 - 即tabbar跳转 API switchTab 【不能带参数】

组件属性跳转 :

wx.switchTab({
  url:'/pages/details/index'
})
-------
<navigator open-type = 'switchTab'> <navigator>
存storage:
  // 导航跳转tabbar页面
  handleCategory:(event)=>{
    // 点击哪个分类传递对应分类id,查询数据
    wx.setStorageSync('cid', event.target.dataset.id)
    wx.switchTab({
      url: '/pages/category/index',
    })
  }
取storage
  /**
   * 生命周期函数--监听页面显示onShow
   */
  onShow: function () {
    let data = wx.getStorageSync('cid')
    console.log('cid',data);
  },

跳到分类页面的时候传 当前分类id 查询子分类

1-- 本地存储 实现传参 wx.setStorageSync-同步 Async-异步

同步结果直接返回 - 存储数据类型不限 【js中local存string】

2-- 全局globalData传递数据

组件内部共享数据

app.js

//自定义全局共享数据
globalData:{
    cid:0,
}

页面存取

//得到整个app对象
const app = getApp()
//存取
app.globalData.cid = event.target.dataset.id
//读取
const cid = app.globalData.cid

wx.reLaunch 重新启动

wx.redirectTo() 重定向

小程序登录

》wx.login获得临时登录凭证 - wx.request()发送code - 后端发给微信接口服务认证 - 返回信 息 - 自定义登录存入storage - request() 发送携带登录状态 -响应数据 getUserInfo()

》配合后端一起 -

<button bindtap="handleLogin">点击</button>
----------------
//登录
handleLogin(){
    wx.getUserProfile({
      desc: '描述用户信息',
      success(data){
        console.log("success",data);
      },
      fail(erro){
        console.log(erro);
      }
    })
  }
---------------------
wx.getUserProfile({ //弹出授权框框
    desc:'描述使用用户信息'
    success:res=>{}, //res.userInfo 拿到头像和昵称
    fail:err=>{}
})

picker选择器

camera相机 - 拍照功能 API - wx.createCamerCOntext

 <!-- 相机 -->
    <view>
      <button bindtap="takephoto">拍照</button>
      <button bindtap="changePosition">切换摄像头</button>
      <camera 
        style="height: 200px;width: 100%;"
        mode="normal"
        flash="on"
        device-position="{{ position }}"  //改变摄像头朝向
      ></camera>
      <text>拍照的图片为:</text>
      <image src="{{ tempImagePath }}"></image>
    </view>
//拍照
  takephoto(){
    //创建相机上下文对象
    const cameraContext = wx.createCameraContext()
    //调用拍照方法
    cameraContext.takePhoto({
        success:(data)=>{
          console.log('拍照成功',data);
          this.setData({
            //保存拍照图片地址
            tempImagePath:data.tempImagePath,
          })
        },
        fail:(error)=>{
          console.log('拍照失败',error);
        }
    })
//扫码
扫码成功触发handleScancode - 打印event对象 event.detail....
<!-- 扫码 -->
      <camera 
        style="height: 200px;width: 100%;"
        mode="scanCode" 
        device-position="back"
        bindscancode="handleScancode" 
      ></camera>
        //扫码成功后的回调
  handleScancode(event){
    console.log('扫码成功:',event.detail);
  },

-调用API 媒体 调出手机自身的相机

wx.chooseMedia() //可以录视频
wx.chooseImage()
wx.chooseMedia()
wx.scanCode({
    ...
})
---------------
handleCamera(){
    let _this = this
    //API实现上传视频
    wx.chooseMedia({
      success(data){
        console.log('上传视频:',data);
        _this.setData({
          tempImagePath:data.tempFiles[0].tempFilePath
        })
      }
    })
    //API实现上传图片
    wx.chooseImage({
      count:2,
      success(data){
        console.log('成功回调:',data);
        _this.setData({
          tempImagePath:data.tempFilePaths
        })
      },
      fail(data){
        console.log('失败回调',data);
      }
    })
  },

map组件

<map
// 地图中心是哪里 - 经纬度
     langitude="经",
     latitude="纬"
     markers="标记点"
     show-location = "带有方向的定位点" //手机测试准确
     enable-traffic = '{{true}}' // 实时路况
     ></map>
---------------------
 详情页-详情页-地图
  <map
    longitude="104.066646"
    latitude="30.671305"
    show-location="{{true}}"
    enable-traffic="{{true}}"
  ></map>
page,view{
    w100%,
    h100%,
}

更新客户端微信版本

wx.updateWeChatApp({
    //监视客户版本过低,小程序不支持,会自动跳转到更新微信页面
}) 

showNavgationBarLoading()

showActionSheet()

wx.seetNavigationBarTitle() // 点击商品动态设置导航栏的标题

wx.uploadFile() downloadFile()

wx.stopwifi

wx.addPhoneContact()

wx.getNetworkType()

wx.makePhoneCall() //打电话

wx.uploadFile() wx.downloadFile()

组件自定义 【指南】

建立components目录/cart组件/js | wxml | wxss | json四类文件

json文件自定义组件声明

{ //json文件是个对象
    "component":true,// 自定义组件声明
}

自定义组件结构 | 样式 书写

js 逻辑书写

//组件函数 =》自定义组件中的js是个组件函数
component({
    properties:{
      //只读 - 类型校验
        item:{type:Object}
    },
    data:{
      //内部数据
    },
    methods:{
    },
})

使用组件

//哪个页面使用,就在它的json文件标注:
  "usingComponents": {
    "xiaolan-upup":"/components/backTop/index"
  }
----------
{
    "usingComponent":{
        "自定义组件名字":"/文件路径"
    }
}

全局定义组件-

App.json文件中做 useingComponent: 注册,可以所有页面都使用

通信:===Vue通信

骨架屏

页面的空白版本,在页面完全渲染前,通过灰色模块来勾勒模块,等数据加载完毕后,在替换成真实的内容。

工具自带生成骨架屏 右下角点点点 - wx-if === wx:if

小程序进入的首页多了index.skeleton.wxml wxss两个文件

使用的时候在index.wxml wxss 引入代码

如果loading为true,显示骨架屏;//需要自己添加loading数据

数据读取完毕且更改之后之后,将loading改为false

loading:true, //骨架屏显示与否
-------------------------------------
setTimeout( async()=>{
    try {
      let [cateData,homeData] =await Promise.allSettled([
        request({ url:'/api/tabs'}),
        request({ url:'/api/tab/1'})
      ])
      // console.log('data:all',homeData,cateData);
      this.setData({
        categories:cateData.value.list,
        banners:homeData.value.banners,
        productList:homeData.value.items.list,
        loading:false, //加载完数据后将骨架屏撤掉
      })
    } catch (error) {
      console.error(error)
    }
   },5000)

小程序也可以使用组件库—=》

Vant Weapp 小程序

步骤一:通过 npm 安装

》需要先 读npm 支持

文件资源管理器 : npm init -y =>创建package.json文件

根文件创建 miniprogram

npm install

npm i @vant/weapp -S --production

步骤二:按照流程修改配置

app.json 删除 style:v2

project.config.json添加配置

构建 npm 包

引入组件 app.json文件 :注意路径!!!

"van-button": "/miniprogram/miniprogram_npm/@vant/weapp/button/index"

页面通信

通过 url ?

本地存储

页面app全局定义数据 globalData

遇到的奇葩问题:

wxml代码避免换行,会识别源码格式

wx:for=" {{ categories }}" 引号多打了空格,显示失败

小程序框架

》多个平台都能运行小程序,除了写多套小程序外,也可以使用框架

mpvue 美团 vue2.x - 跨平台 18年没有维护

wepy

uni-app 基于mpvue…扩展 vuejs

taro 京东 react重度使用者 支持react-vue

uniApp

》快速上手 Vue语法写法 + 小程序标签

工具HbuilderX

建立项目-uni-app

模板 hello-uni-app 演示组件使用的 Vue版本选择

运行时开发环境的任务 npm start

发行 npm run build -原生App-(独立生成安装包…)构建完成后当前目录文件下生成unpackage文件

HBuilder:运行到小程序模拟器-需要各自的开发者工具安装-运行设置-小程序运行配置-开发工具路径需要配置一下–

微信开发者工具:设置-安全设置-安全-服务端口 开启命令行和HTTP才可以调用该工具

uniapp小程序案例- Dcloud插件市场

模板下载参照 - 小程序一个月完成

简历不要直接写 电商管理系统

写 == 江苏无锡旅游

uniapp小程序axios封装

ES6 Moudles 导入导出

做原生开发还是uniapp开发 套路 页面的配置 全局配置 那个文件中要清楚

uni-grid 网格

uni-app不能直接使用vue-router

插件市场有vueRrouter-ui等封装

配置文件是 pages.json 如何封装的axios?

登录

uni-forms uni-forms-item uni-esatinput

VUEx VUErOUTER

!条件编译

编译器 - 条件编译

平台差异-编写ifelse性能低-条件编译

特殊的注释

<!-- #ifdef APP-PLUS --> 
// #ifdef H5
------------在注释中写这些!!---------
原生APP  平台代码
#endif
#ifdef H5 
浏览器中才有
#endif
#ifdef MP-WEIXIN
微信小程序中才有
#endif

微信的 wx.login方法获取用户信息,uni-app通过表单注册登录,如何根据不同平台进行差异编译

taro react语法+小程序标签

uniapp vue语法+小程序标签

相关文章
|
3月前
|
人工智能 小程序 编译器
Ant Design Mini 问题之Antd Mini 使用小程序函数式组件(functional-mini)来确保组件逻辑适配到双端,如何实现
Ant Design Mini 问题之Antd Mini 使用小程序函数式组件(functional-mini)来确保组件逻辑适配到双端,如何实现
|
3月前
|
小程序 前端开发 API
Ant Design Mini 问题之在微信小程序中,由于不支持slot特性,Ant Design Mini的什么组件功能受到了限制,如何解决
Ant Design Mini 问题之在微信小程序中,由于不支持slot特性,Ant Design Mini的什么组件功能受到了限制,如何解决
106 1
|
5月前
|
人工智能 小程序 前端开发
Ant Design Mini 支持微信小程序啦!
Ant Design Mini 支持微信小程序啦!
483 0
Ant Design Mini 支持微信小程序啦!
|
1月前
|
移动开发 小程序 数据可视化
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
240 3
|
1月前
|
小程序 API
微信小程序更新提醒uniapp
在小程序开发中,版本更新至关重要。本方案利用 `uni-app` 的 `uni.getUpdateManager()` API 在启动时检测版本更新,提示用户并提供立即更新选项,自动下载更新内容,并在更新完成后重启小程序以应用新版本。适用于微信小程序,确保用户始终使用最新版本。以下是实现步骤: ### 实现步骤 1. **创建更新方法**:在 `App.vue` 中创建 `updateApp` 方法用于检查小程序是否有新版本。 2. **测试**:添加编译模式并选择成功状态进行模拟测试。
50 0
微信小程序更新提醒uniapp
|
3月前
|
小程序 前端开发 Java
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
JavaDog Chat v1.0.0 是一款基于 SpringBoot、MybatisPlus 和 uniapp 的简易聊天软件,兼容 H5、小程序和 APP,提供丰富的注释和简洁代码,适合初学者。主要功能包括登录注册、消息发送、好友管理及群组交流。
108 0
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
|
3月前
|
小程序 前端开发 JavaScript
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
【避坑宝】是一款企业黑红名单吐槽小程序,旨在帮助打工人群体辨别企业优劣。该平台采用SpringBoot+MybatisPlus+uniapp+uview2等技术栈构建,具备丰富的注释与简洁的代码结构,非常适合实战练习与学习。通过小程序搜索“避坑宝”即可体验。
104 0
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
|
3月前
|
存储 小程序 JavaScript
|
4月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的汉服交易小程序的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的汉服交易小程序的详细设计和实现(源码+lw+部署文档+讲解等)
63 7
|
4月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的宠物医院微信小程序的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的宠物医院微信小程序的详细设计和实现(源码+lw+部署文档+讲解等)
84 7