详解微信小程序中自定义的 modal 弹窗组件

why 1033 2024-08-09

小程序不知从何时火起来的,很多人都入坑了吧,对于搞开发的小伙伴来说,不管是android,ios,还是很流行的微信小程序

都会发现官方提供的原生控件已经不能完全满足我们的开发需求,所以本文介绍的就是一个自定义的微信小程序组件(modal弹窗组件),

先来一张图。

20180731165929235.gif

 看到这里了,说明效果图还是对你有点吸引的么,哈哈,废话不多说了,开始上代码。。。

一共是四个文件js、json、xml,wxss,如果这个都还不清楚的童鞋请出门左拐,面壁思过5分钟。

先上布局dialog.xml文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<!--mask dialog-->

<view class="drawer_screen" bindtap="hideDialog" wx:if="{{isShow}}" catchtouchmove="myCatchTouch"></view>

<!--content-->

<!--使用animation属性指定需要执行的动画-->

<view animation="{{animationData}}" class="drawer_box" wx:if="{{isShow}}">

 

  <!--drawer content-->

  <view class=&#39;row&#39;>

    <view class="drawer_title" style=&#39;width:100%;padding-left:60rpx&#39;>{{title}}</view>

    <icon type="clear" style=&#39;margin-top:40rpx;margin-right:20rpx;&#39; bindtap="hideDialog"></icon>

  </view>

  <form bindsubmit="_formSubmit">

    <scroll-view scroll-y>

      <view class="drawer_content">

        <view wx:for="{{dataObject}}" wx:key="{{id}}">

          <view class="top grid">

            <label class="title col-0" style="color:red" wx:if="{{item.must}}">*</label>

            <label class="title col-0" wx:else> </label>

            <input class="input_base input_h30 col-1" placeholder=&#39;{{item.placeholder}}&#39; wx:if="{{item.type === type_input}}" name="{{item.id}}" value="{{bean[item.id]}}"></input>

            <view class="input_base input_h30 col-1" wx:elif="{{item.id === id_sex}}" hover-class=&#39;btn_ok_hover&#39; bindtap=&#39;{{item.event}}&#39;>{{sexDefault}}</view>

            <view class="input_base input_h30 col-1" wx:elif="{{item.id === id_group}}" hover-class=&#39;btn_ok_hover&#39; bindtap=&#39;{{item.event}}&#39;>{{groupDefault}}</view>

          </view>

        </view>

      </view>

    </scroll-view>

    <button class="btn_ok" hover-class=&#39;btn_ok_hover&#39; formType="submit">确定</button>

  </form>

</view>

然后是dialog.wxss文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

/*mask dialog start*/

.drawer_screen {

  width: 100%;

  height: 100%;

  position: fixed;

  top: 0;

  left: 0;

  right:0;

  bottom:0;

  z-index: 1000;

  background: #000;

  opacity: 0.5;

  overflow: hidden;

}

 

/*content*/

 

.drawer_box {

  width: 650rpx;

  overflow: hidden;

  position: fixed;

  top: 50%;

  left: 0;

  z-index: 1001;

  background: #fafafa;

  margin: -480rpx 50rpx 0 50rpx;

  border-radius: 6px;

}

 

.drawer_title {

  padding: 15px;

  font: 20px "microsoft yahei";

  text-align: center;

}

 

.drawer_content {

  height: 720rpx;

  /*overflow-y: scroll; 超出父盒子高度可滚动*/

}

 

.btn_ok {

  padding: 10px;

  font: 20px "microsoft yahei";

  text-align: center;

  border-top: 1rpx solid #e8e8ea;

  color: #3cc51f;

}

 

.btn_ok_hover {

  color: #aaa;

  background: #d9d9d9;

}

 

- {

  padding-top: 8px;

}

 

.input_base {

  border: 2rpx solid #ccc;

  border-radius: 20rpx;

  padding-left: 20rpx;

  margin-right: 20rpx;

}

 

.input_h30 {

  height: 30px;

  line-height: 30px;

}

 

.title {

  height: 30px;

  line-height: 30px;

  width: 40rpx;

  text-align: center;

  display: inline-block;

  font: 300 28rpx/30px "microsoft yahei";

}

 

.grid {

  display: -webkit-box;

  display: box;

}

 

.col-0 {

  -webkit-box-flex: 0;

  box-flex: 0;

}

 

.col-1 {

  -webkit-box-flex: 1;

  box-flex: 1;

}

 

/*mask dialog end*/

 

.row {

  display: flex;

  flex-direction: row;

}

再然后就是dialog.js文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

// components/Dialog/dialog.js

Component({

  options: {

    multipleSlots: true // 在组件定义时的选项中启用多slot支持

  },

  /**

   * 组件的属性列表

   */

  properties: {

    title: { // 属性名

      type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)

      value: &#39;标题&#39; // 属性初始值(可选),如果未指定则会根据类型选择一个

    }

 

  },

 

  /**

   * 组件的初始数据

   */

  data: {

    // 弹窗显示控制

    isShow: false,

    type_input: "input",

    type_btn: "button",

    id_sex: "sex",

    id_group: "group",

    dataObject: [],

    sexDefault: "男",

    groupDefault: "组织",

    sexArray: [&#39;男&#39;, &#39;女&#39;],

    groupArray: [&#39;组织&#39;, &#39;群众&#39;],

    bean: {},

  },

 

  /**

   * 组件的方法列表

   */

  methods: {

    /*

     * 公有方法

     */

    setDataObj(dataObj,beanObj) {

      this.setData({

        dataObject: dataObj,

        bean: beanObj

      })

      if (beanObj.hasOwnProperty("sex") && beanObj.sex != ""){

        this.setData({

          sexDefault: beanObj.sex

        })

      }

      if (beanObj.hasOwnProperty("group") && beanObj.group != "") {

        this.setData({

          groupDefault: beanObj.group

        })

      }

    },

    //隐藏弹框

    hideDialog() {

      this._showOrCloseDialog("close")

    },

    //展示弹框

    showDialog() {

      this._showOrCloseDialog("open")

    },

    /*

     * 内部私有方法建议以下划线开头

     * triggerEvent 用于触发事件

     */

 

    _formSubmit(e) {

      if ("" === e.detail.value.name) {

        wx.showToast({

          title: &#39;请填写姓名&#39;,

          icon: &#39;none&#39;

        })

        return

      }

      if ("" === e.detail.value.phone) {

        wx.showToast({

          title: &#39;请填写电话&#39;,

          icon: &#39;none&#39;

        })

        return

      }

      this._showOrCloseDialog("close")

      //触发成功回调

      this.triggerEvent("confirmEvent", {

        e: e

      });

    },

 

    sexButton: function() {

      var that = this;

      wx.showActionSheet({

        itemList: this.data.sexArray,

        success: function(res) {

          console.log(res.tapIndex)

          that.setData({

            sexDefault: that.data.sexArray[res.tapIndex]

          })

        },

        fail: function(res) {

          console.log(res.errMsg)

        }

      })

    },

    groupButton: function() {

      var that = this;

      wx.showActionSheet({

        itemList: this.data.groupArray,

        success: function(res) {

          console.log(res.tapIndex)

          that.setData({

            groupDefault: that.data.groupArray[res.tapIndex]

          })

        },

        fail: function(res) {

          console.log(res.errMsg)

        }

      })

    },

    _showOrCloseDialog: function(currentStatu) {

      var that = this;

      /* 动画部分 */

      // 第1步:创建动画实例

      var animation = wx.createAnimation({

        duration: 200, //动画时长

        timingFunction: "linear", //线性

        delay: 0 //0则不延迟

      });

 

      // 第2步:这个动画实例赋给当前的动画实例

      this.animation = animation;

 

      // 第3步:执行第一组动画

      animation.opacity(0).rotateX(-100).step();

 

      // 第4步:导出动画对象赋给数据对象储存

      that.setData({

        animationData: animation.export()

      })

 

      // 第5步:设置定时器到指定时候后,执行第二组动画

      setTimeout(function() {

        // 执行第二组动画

        animation.opacity(1).rotateX(0).step();

        // 给数据对象储存的第一组动画,更替为执行完第二组动画的动画对象

        that.setData({

          animationData: animation

        })

 

        //关闭

        if (currentStatu == "close") {

          that.setData({

            isShow: false

          });

        }

      }.bind(this), 200)

 

      // 显示

      if (currentStatu == "open") {

        that.setData({

          isShow: true

        });

      }

    }

  },

  //解决滚动穿透问题

  myCatchTouch: function () {

    return

  }

})

看到这里可能有些小伙伴就问了,你个水货,怎么js文件结构怎么跟我的不一样,是不是来忽悠我们的?客官别急么,请听我娓娓道来:其实这里为了方便调用,我把这个dialog封装成了一个组件了。你问我怎么封装组件?请移步官方教程:

https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/

封装完成之后项目结构如下:

image.png

这里误会就解释清楚了,麻烦给个面子继续往下看。。

已经展示了三大金刚了,还有一个dialog.json文件未展示,这个文件很简单

1

2

3

4

{

  "component": true,//作为组件

  "usingComponents": {}//引用别的组件

}

这个文件也和一般json文件有差异,主要是配置了作为组件让别人引用的,所以到这个,这个组件已经封装完成了

可能这个时候很多童鞋迫不及待的复制粘贴然后把代码放入项目中想一睹真容,发现运行结果跟效果图不一样,然后有一部分人可能又要说:娘希匹,果然是个水货,骗人的!!!到这里,我只能说:各位乡亲们,蛋定、蛋定。。。,因为到js中的dataObject和bean没有么,所以效果图跟我在文章开始展示的不一样。so,下面就告诉大家,怎么调用,并且实现和我一样的效果。

在引用的xml中添加如下代码

1

2

3

<image src="../../img/add.png" class="buttom" bindtap="bindAdd"></image>

 

<dialog id=&#39;dialog&#39; title=&#39;新增&#39; bind:confirmEvent="_confirmEvent"></dialog>

buttom这个是个悬浮按钮,wxss也给你

1

2

3

4

5

6

7

8

9

.buttom{

  width: 100rpx;

  height: 100rpx;

  display: flex;

  flex-direction: row;

  position: fixed;

  bottom:60rpx;

  right: 60rpx;

}

然后引用页面的js文件中

1

2

3

4

5

6

7

8

9

10

11

12

onReady: function() {

    //获得dialog组件

    this.dialog = this.selectComponent("#dialog");

}

 

 

 

//响应button弹框

bindAdd: function(e) {

    this.dialog.setDataObj(addObject, {})

    this.dialog.showDialog();

}

到这里基本上点击悬浮按钮就可以实现弹框了。有的大胸dei又要说了:哎哎哎,等等,addObject,和那个add.png还没有给了。好吧,给给给,都给你们

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

const addObject = [{

  id: "name",

  must: true,

  placeholder: "姓名",

  type: "input",

  event: "nameInput"

},

{

  id: "sex",

  must: true,

  placeholder: "男",

  type: "button",

  event: "sexButton"

},

{

  id: "group",

  must: true,

  placeholder: "组织",

  type: "button",

  event: "groupButton"

},

{

  id: "phone",

  must: true,

  placeholder: "电话号码",

  type: "input",

  event: "phoneInput"

},

{

  id: "shortNum",

  must: false,

  placeholder: "集团短号",

  type: "input",

  event: "shortNumInput"

},

{

  id: "mail",

  must: false,

  placeholder: "电子邮箱",

  type: "input",

  event: "mailInput"

},

{

  id: "unit",

  must: false,

  placeholder: "单位名称",

  type: "input",

  event: "unitInput"

},

{

  id: "department",

  must: false,

  placeholder: "部门名称",

  type: "input",

  event: "departmentInput"

},

{

  id: "job",

  must: false,

  placeholder: "职务",

  type: "input",

  event: "jobInput"

},

{

  id: "function",

  must: false,

  placeholder: "涉及工作内容",

  type: "input",

  event: "functionInput"

},

{

  id: "comPhone",

  must: false,

  placeholder: "办公电话",

  type: "input",

  event: "comPhoneInput"

},

{

  id: "fax",

  must: false,

  placeholder: "传真",

  type: "input",

  event: "faxInput"

},

{

  id: "homePhone",

  must: false,

  placeholder: "家庭电话",

  type: "input",

  event: "homePhoneInput"

},

{

  id: "showOrder",

  must: false,

  placeholder: "显示顺序",

  type: "input",

  event: "showOrderInput"

},

{

  id: "departOrder",

  must: false,

  placeholder: "部门顺序",

  type: "input",

  event: "departOrderInput"

},

{

  id: "remark",

  must: false,

  placeholder: "备注",

  type: "input",

  event: "remarkInput"

}

]

图片

image.png

 到这里应该可实现跟我一样的效果了吧,尽情去折腾吧。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微信小程序开发聊天会话组件用于在线客服聊天对话的实现
下一篇:提高效率的管理照片插件:让您的照片井井有条
相关文章

 发表评论

暂时没有评论,来抢沙发吧~