Parcourir la source

网页端:选择人员去掉退休、离职的

移动端:新增我的流程页面
余思翰 il y a 4 semaines
Parent
révision
f26c135cac
79 fichiers modifiés avec 9023 ajouts et 47 suppressions
  1. 1
    1
      oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcBorrowMapper.xml
  2. 35
    0
      oa-ui-app/pages/components/auditor.vue
  3. 15
    2
      oa-ui-app/pages/components/fileUpload.vue
  4. 140
    0
      oa-ui-app/pages/components/flowable/FlowRecord.vue
  5. 63
    23
      oa-ui-app/pages/form/borrow/borrow.vue
  6. 6
    4
      oa-ui-app/pages/form/borrow/borrowDetail.vue
  7. 131
    8
      oa-ui-app/pages/message/index.vue
  8. 315
    4
      oa-ui-app/pages/message/myProcess/index.vue
  9. BIN
      oa-ui-app/static/font/auditor.ttf
  10. 10
    0
      oa-ui-app/static/scss/global.scss
  11. 25
    0
      oa-ui-app/uni_modules/mescroll/App.vue
  12. 157
    0
      oa-ui-app/uni_modules/mescroll/api/goods-edit.js
  13. 169
    0
      oa-ui-app/uni_modules/mescroll/api/goods.js
  14. 169
    0
      oa-ui-app/uni_modules/mescroll/api/mock.js
  15. 67
    0
      oa-ui-app/uni_modules/mescroll/components/good-list/good-list.vue
  16. 186
    0
      oa-ui-app/uni_modules/mescroll/components/me-tabs/me-tabs.vue
  17. 179
    0
      oa-ui-app/uni_modules/mescroll/components/me-video/me-video.vue
  18. 47
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/components/mescroll-down.css
  19. 39
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/components/mescroll-down.vue
  20. 330
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/mescroll-body.vue
  21. 29
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/mescroll-uni-option.js
  22. 406
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/mescroll-uni.vue
  23. 44
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-down.css
  24. 53
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-down.vue
  25. 32
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-up.css
  26. 40
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-up.vue
  27. 350
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/mescroll-body.vue
  28. 36
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/mescroll-uni-option.js
  29. 430
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/mescroll-uni.vue
  30. 55
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-down.css
  31. 47
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-down.vue
  32. 90
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-empty.vue
  33. 83
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-top.vue
  34. 47
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-up.css
  35. 39
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-up.vue
  36. 19
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-body.css
  37. 348
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-body.vue
  38. 65
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js
  39. 36
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni-option.js
  40. 36
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni.css
  41. 799
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni.js
  42. 424
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni.vue
  43. 48
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mixins/mescroll-comp.js
  44. 59
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mixins/mescroll-more-item.js
  45. 74
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mixins/mescroll-more.js
  46. 109
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/wxs/mixins.js
  47. 92
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/wxs/renderjs.js
  48. 268
    0
      oa-ui-app/uni_modules/mescroll/components/mescroll-uni/wxs/wxs.wxs
  49. 17
    0
      oa-ui-app/uni_modules/mescroll/main.js
  50. 68
    0
      oa-ui-app/uni_modules/mescroll/manifest.json
  51. 26
    0
      oa-ui-app/uni_modules/mescroll/package.json
  52. 161
    0
      oa-ui-app/uni_modules/mescroll/pages.json
  53. 125
    0
      oa-ui-app/uni_modules/mescroll/pages/base/list-msg.vue
  54. 89
    0
      oa-ui-app/uni_modules/mescroll/pages/base/list-news.vue
  55. 78
    0
      oa-ui-app/uni_modules/mescroll/pages/base/list-products.vue
  56. 106
    0
      oa-ui-app/uni_modules/mescroll/pages/base/list-search.vue
  57. 119
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-body-part.vue
  58. 37
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-comp-item.vue
  59. 57
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-comp.vue
  60. 80
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-more-item.vue
  61. 75
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-more.vue
  62. 87
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-native.vue
  63. 89
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-one.vue
  64. 191
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-options.vue
  65. 134
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-uni-part.vue
  66. 113
    0
      oa-ui-app/uni_modules/mescroll/pages/base/mescroll-uni.vue
  67. 141
    0
      oa-ui-app/uni_modules/mescroll/pages/base/sticky-data.vue
  68. 177
    0
      oa-ui-app/uni_modules/mescroll/pages/base/sticky-scroll-data.vue
  69. 142
    0
      oa-ui-app/uni_modules/mescroll/pages/base/sticky-scroll.vue
  70. 93
    0
      oa-ui-app/uni_modules/mescroll/pages/base/sticky-uni.vue
  71. 107
    0
      oa-ui-app/uni_modules/mescroll/pages/base/sticky.vue
  72. 132
    0
      oa-ui-app/uni_modules/mescroll/pages/index/index.vue
  73. 65
    0
      oa-ui-app/uni_modules/mescroll/pages/intermediate/beibei.vue
  74. 86
    0
      oa-ui-app/uni_modules/mescroll/pages/intermediate/mescroll-swiper-item.vue
  75. 58
    0
      oa-ui-app/uni_modules/mescroll/pages/intermediate/mescroll-swiper.vue
  76. 124
    0
      oa-ui-app/uni_modules/mescroll/pages/intermediate/xinlang.vue
  77. BIN
      oa-ui-app/uni_modules/mescroll/static/img/mescroll-empty.png
  78. BIN
      oa-ui-app/uni_modules/mescroll/static/img/mescroll-totop.png
  79. 4
    5
      oa-ui/src/views/flowable/form/budget/components/choosePeople.vue

+ 1
- 1
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcBorrowMapper.xml Voir le fichier

@@ -242,7 +242,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
242 242
             <if test="dszUserId != null">dsz_user_id = #{dszUserId},</if>
243 243
             <if test="dszComment != null">dsz_comment = #{dszComment},</if>
244 244
             <if test="unionUserId != null">union_user_id = #{unionUserId},</if>
245
-            <if test="unionComment != null">zjl_comment = #{unionComment},</if>
245
+            <if test="unionComment != null">union_comment = #{unionComment},</if>
246 246
             <if test="cwUserId != null">cw_user_id = #{cwUserId},</if>
247 247
             <if test="cwComment != null">cw_comment = #{cwComment},</if>
248 248
             <if test="deptTime != null">dept_time = #{deptTime},</if>

+ 35
- 0
oa-ui-app/pages/components/auditor.vue Voir le fichier

@@ -0,0 +1,35 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-19 10:28:16
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-19 14:09:29
6
+-->
7
+<template>
8
+  <view class="box" v-if="name">
9
+    <view class="title">签名:</view>
10
+    <view class="auditor">{{ name }}</view>
11
+    <view class="title">审核时间:</view>
12
+    <view class="time">{{ time }}</view>
13
+  </view>
14
+</template>
15
+
16
+<script>
17
+export default {
18
+  props: {
19
+    name: String,
20
+    time: String
21
+  }
22
+}
23
+</script>
24
+
25
+<style lang="scss" scoped>
26
+.box {
27
+  display: flex;
28
+  align-items: center;
29
+  justify-content: right;
30
+  padding-top:5px;
31
+  .auditor{
32
+    padding-right: 15px;
33
+  }
34
+}
35
+</style>

+ 15
- 2
oa-ui-app/pages/components/fileUpload.vue Voir le fichier

@@ -1,6 +1,14 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-18 11:14:52
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-19 14:05:47
6
+-->
1 7
 <template>
2 8
   <view>
3
-    <uv-button @click="chooseFile" text="+ 选择文件" type="primary" v-if="showBtn"></uv-button>
9
+    <view class="btn">
10
+      <uv-button @click="chooseFile" text="+ 选择文件" type="primary" :disabled="!showBtn"></uv-button>
11
+    </view>
4 12
     <view v-if="fileName" class="file-box">
5 13
       <view class="link">
6 14
         <uv-link :href="`${fileUrl + fileName}`" :text="docName" color="#19be6b" line-color="#19be6b"></uv-link>
@@ -91,10 +99,15 @@ export default {
91 99
   align-items: center;
92 100
   border: 1px solid #ececec;
93 101
   padding: 10px;
94
-
95 102
   .link {
96 103
     flex: 1;
97 104
 
98 105
   }
99 106
 }
107
+
108
+.btn {
109
+  width: 100px;
110
+  height: 40px;
111
+}
112
+
100 113
 </style>

+ 140
- 0
oa-ui-app/pages/components/flowable/FlowRecord.vue Voir le fichier

@@ -0,0 +1,140 @@
1
+<template>
2
+  <view class="box">
3
+    <!-- <uni-steps :options="flowRecordList" active-color="#007AFF" :active="active" direction="column">
4
+      <uni-card>
5
+        <text>这是一个基础卡片示例,内容较少,此示例展示了一个没有任何属性不带阴影的卡片。</text>
6
+      </uni-card>
7
+    </uni-steps> -->
8
+    <scroll-view scroll-y="true" class="scroll-box">
9
+      <uv-steps :current="active" direction="column">
10
+        <uv-steps-item v-for="item, index in flowRecordList" :key="'f' + index" :title="item.taskName">
11
+          <!-- <template v-slot:icon>
12
+            <uv-icon name="checkmark-circle-fill" color="#53c21d"></uv-icon>
13
+          </template> -->
14
+          <template v-slot:desc>
15
+            <view class="desc">
16
+              <view class="desc-item">
17
+                <view class="item-label">办理人:</view>
18
+                <view class="item-value">{{ item.assigneeName }}</view>
19
+              </view>
20
+              <view class="desc-item" v-if="item.candidate">
21
+                <view class="item-label">候选办理:</view>
22
+                <view class="item-value">{{ item.candidate }}</view>
23
+              </view>
24
+              <view class="desc-item">
25
+                <view class="item-label">接收时间:</view>
26
+                <view class="item-value">{{ item.createTime }}</view>
27
+              </view>
28
+              <view class="desc-item">
29
+                <view class="item-label">处理时间:</view>
30
+                <view class="item-value">{{ item.finishTime }}</view>
31
+              </view>
32
+              <view class="desc-item">
33
+                <view class="item-label">耗时:</view>
34
+                <view class="item-value">{{ item.duration }}</view>
35
+              </view>
36
+            </view>
37
+          </template>
38
+        </uv-steps-item>
39
+      </uv-steps>
40
+    </scroll-view>
41
+
42
+  </view>
43
+</template>
44
+
45
+<script>
46
+import { flowRecord } from "@/api/flowable/finished";
47
+export default {
48
+  props: {
49
+    rows: Object
50
+  },
51
+  watch: {
52
+    rows(newval) {
53
+      console.log(newval);
54
+      this.getFlowRecordList();
55
+    }
56
+  },
57
+  created() {
58
+    this.getFlowRecordList();
59
+  },
60
+  data() {
61
+    return {
62
+      flowRecordList: [], // 流程流转数据
63
+      flowList: [],
64
+      active: 0,
65
+    }
66
+  },
67
+  methods: {
68
+    setColor(val) {
69
+      if (val) {
70
+        return "#2bc418";
71
+      } else {
72
+        return "#b3bdbb";
73
+      }
74
+    },
75
+    setIcon(val) {
76
+      if (val) {
77
+        return "el-icon-check";
78
+      } else {
79
+        return "el-icon-time";
80
+      }
81
+    },
82
+    /** 获取流程变量内容 */
83
+    processVariables() {
84
+      if (this.rows.taskId) {
85
+      }
86
+    },
87
+    /** 流程流转记录 */
88
+    getFlowRecordList() {
89
+      const params = { procInsId: this.rows.procInsId, deployId: this.rows.deployId }
90
+      flowRecord(params).then(res => {
91
+        this.flowRecordList = res.data.flowList.reverse();
92
+        console.log(this.flowRecordList);
93
+        this.active = this.flowRecordList[i].length - 1
94
+        for (let i = 0; i < this.flowRecordList.length; i++) {
95
+          if (!this.flowRecordList[i].finishTime) {
96
+            this.active = i
97
+          }
98
+        }
99
+      }).catch(res => {
100
+
101
+      })
102
+    },
103
+  }
104
+}
105
+</script>
106
+
107
+<style lang="scss" scoped>
108
+.box {
109
+  padding: 20px;
110
+  height: 100%;
111
+}
112
+
113
+.scroll-box {
114
+  height: 100%;
115
+}
116
+
117
+.desc {
118
+  padding: 8px;
119
+  background: #fff;
120
+  border-radius: 8px;
121
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
122
+  width: 100%;
123
+
124
+  .desc-item {
125
+    display: flex;
126
+    border-bottom: 1px solid #eee;
127
+    height: 30px;
128
+    align-items: center;
129
+
130
+    .item-label {
131
+      width: 80px;
132
+      text-align: right;
133
+    }
134
+
135
+    .item-value {
136
+      // flex: 1;
137
+    }
138
+  }
139
+}
140
+</style>

+ 63
- 23
oa-ui-app/pages/form/borrow/borrow.vue Voir le fichier

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-20 10:20:22
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-18 17:13:59
5
+ * @LastEditTime: 2025-03-19 14:19:17
6 6
 -->
7 7
 <template>
8 8
   <view class="form-container">
@@ -31,13 +31,13 @@
31 31
 
32 32
       <!-- 借款类型 -->
33 33
       <uni-forms-item label="借款类型" required class="form-item" name="borrowUsage">
34
-        <uni-data-checkbox v-model="form.borrowUsage" :localdata="borrowUsageOptions"
35
-          :disabled="taskName != '借款申请'"></uni-data-checkbox>
34
+        <uni-data-checkbox v-model="form.borrowUsage" :localdata="borrowUsageOptions" :disabled="taskName != '借款申请'"
35
+          @change="hanldeChangeType"></uni-data-checkbox>
36 36
       </uni-forms-item>
37 37
 
38 38
       <!-- 借款事由 -->
39 39
       <uni-forms-item label="借款事由" required class="form-item" name="applyReason" v-if="form.borrowUsage != 0">
40
-        <uv-textarea v-model="form.applyReason" placeholder="请输入借款事由"></uv-textarea>
40
+        <uv-textarea v-model="form.applyReason" placeholder="请输入借款事由" :disabled="taskName != '借款申请'"></uv-textarea>
41 41
       </uni-forms-item>
42 42
 
43 43
       <!-- 选择项目 -->
@@ -69,7 +69,8 @@
69 69
           超过预算金额:¥ {{ getMoreAmount('0') }}
70 70
         </view>
71 71
       </uni-forms-item>
72
-      <uni-forms-item label="校准金额" required class="form-item" v-if="taskName != '借款申请' && taskName != '部门审核'">
72
+      <uni-forms-item label="校准金额" required class="form-item"
73
+        v-if="taskName != '借款申请' && taskName != '部门审核' && taskName != '党工团审核'">
73 74
         <view class="amount-text">¥ {{ (this.form.managerAmount ? this.form.managerAmount : 0).toFixed(2) }}</view>
74 75
         <view class="warning-text" v-if="exceed && form.borrowUsage == '0'">
75 76
           超过预算金额:¥ {{ getMoreAmount('1') }}
@@ -87,24 +88,43 @@
87 88
           @deleteFile="form.borrowDocument = ''" @success="uploadSuccess"></file-upload>
88 89
       </uni-forms-item>
89 90
 
90
-      <!-- 部门审核 -->
91
-      <uni-forms-item label="部门负责人意见" required class="form-item" v-if="taskName != '借款申请'" name="deptComment">
92
-        <uv-textarea v-model="form.deptComment" placeholder="请输入部门负责人意见" :disabled="taskName != '部门审核'"></uv-textarea>
93
-      </uni-forms-item>
91
+      <view v-if="form.borrowUsage == '0' || form.borrowUsage == '1'">
92
+        <!-- 部门审核 -->
93
+        <uni-forms-item label="部门负责人意见" required class="form-item" v-if="taskName != '借款申请'" name="deptComment">
94
+          <uv-textarea v-model="form.deptComment" placeholder="请输入部门负责人意见" :disabled="taskName != '部门审核'"></uv-textarea>
95
+          <auditor :name="form.deptUser ? form.deptUser.nickName : ''" :time="form.deptTime"></auditor>
96
+        </uni-forms-item>
94 97
 
95
-      <!-- 分管审核 -->
96
-      <uni-forms-item label="分管领导审核意见" required class="form-item" v-if="taskName != '借款申请'" name="managerComment">
97
-        <uv-textarea v-model="form.managerComment" placeholder="请输入分管领导审核意见"
98
-          :disabled="taskName != '分管审核'"></uv-textarea>
99
-      </uni-forms-item>
98
+        <!-- 分管审核 -->
99
+        <uni-forms-item label="分管领导审核意见" required class="form-item" v-if="taskName != '借款申请'" name="managerComment">
100
+          <uv-textarea v-model="form.managerComment" placeholder="请输入分管领导审核意见"
101
+            :disabled="taskName != '分管审核'"></uv-textarea>
102
+          <auditor :name="form.managerUser ? form.managerUser.nickName : ''" :time="form.managerTime"></auditor>
103
+        </uni-forms-item>
100 104
 
101
-      <!-- 总经理审核 -->
102
-      <uni-forms-item label="总经理审核意见" required class="form-item" v-if="taskName != '借款申请'" name="zjlComment">
103
-        <uv-textarea v-model="form.zjlComment" placeholder="请输入总经理审核意见" :disabled="taskName != '总经理审核'"></uv-textarea>
104
-      </uni-forms-item>
105
+        <!-- 总经理审核 -->
106
+        <uni-forms-item label="总经理审核意见" required class="form-item" v-if="taskName != '借款申请'" name="zjlComment">
107
+          <uv-textarea v-model="form.zjlComment" placeholder="请输入总经理审核意见" :disabled="taskName != '总经理审核'"></uv-textarea>
108
+          <auditor :name="form.zjlUser ? form.zjlUser.nickName : ''" :time="form.zjlTime"></auditor>
109
+        </uni-forms-item>
110
+      </view>
105 111
 
106
-      <!-- 非党工团审核 -->
112
+      <!-- 党工团审核 -->
113
+      <view v-if="form.borrowUsage != '0' && form.borrowUsage != '1'">
114
+        <uni-forms-item :label="dgtLabel" required class="form-item" v-if="taskName != '借款申请'" name="unionComment">
115
+          <uv-textarea v-model="form.unionComment" placeholder="请输入审核意见" :disabled="taskName != '党工团审核'"></uv-textarea>
116
+          <auditor :name="form.unionUser ? form.unionUser.nickName : ''" :time="form.unionTime"></auditor>
117
+        </uni-forms-item>
118
+      </view>
107 119
 
120
+      <!-- 财务处理 -->
121
+      <uni-forms-item label="财务部支付备注" class="form-item" v-if="taskName != '借款申请'" name="cwComment">
122
+        <uv-textarea v-model="form.cwComment" placeholder="请输入财务部支付备注" :disabled="taskName != '财务处理'"></uv-textarea>
123
+      </uni-forms-item>
124
+      <uni-forms-item label="支付时间" required class="form-item" v-if="taskName != '借款申请'" name="lendTime">
125
+        <uni-datetime-picker type="date" :clear-icon="false" v-model="form.lendTime" :disabled="taskName != '财务处理'" />
126
+        <auditor :name="form.cwUser ? form.cwUser.nickName : ''" :time="form.lendTime"></auditor>
127
+      </uni-forms-item>
108 128
 
109 129
       <!-- 操作栏 -->
110 130
       <view class="form-button" v-if="taskName">
@@ -137,6 +157,7 @@ import BorrowDetail from './borrowDetail.vue';
137 157
 import FlowNote from '@/pages/components/flowNote.vue';
138 158
 import ReturnPopup from '@/pages/components/returnPopup.vue';
139 159
 import FileUpload from '@/pages/components/fileUpload.vue';
160
+import Auditor from "@/pages/components/auditor.vue";
140 161
 export default {
141 162
   components: {
142 163
     ProjectPicker,
@@ -145,6 +166,7 @@ export default {
145 166
     FlowNote,
146 167
     ReturnPopup,
147 168
     FileUpload,
169
+    Auditor
148 170
   },
149 171
   props: {
150 172
     taskForm: Object,
@@ -210,6 +232,7 @@ export default {
210 232
       hasBorrow: 0, //已申请借款
211 233
       deptId: undefined,
212 234
       returnBack: false,
235
+      dgtLabel: '工会审核意见',
213 236
     }
214 237
   },
215 238
   methods: {
@@ -263,6 +286,12 @@ export default {
263 286
             required: this.taskName === '总经理审核',
264 287
             errorMessage: '请输入总经理意见'
265 288
           }]
289
+        },
290
+        lendTime: {
291
+          rules: [{
292
+            required: this.taskName === '财务处理',
293
+            errorMessage: '请选择支付时间'
294
+          }]
266 295
         }
267 296
       }
268 297
     },
@@ -271,6 +300,7 @@ export default {
271 300
         if (res.data) {
272 301
           this.formTotal = 1;
273 302
           this.form = res.data;
303
+          this.hanldeChangeType();
274 304
           if (this.form.projectId) {
275 305
             getProject(this.form.projectId).then(res => {
276 306
               if (res.data) {
@@ -318,6 +348,15 @@ export default {
318 348
       })
319 349
 
320 350
     },
351
+    hanldeChangeType() {
352
+      if (this.form.borrowUsage == '2') {
353
+        this.dgtLabel = '工会审核意见'
354
+      } else if (this.form.borrowUsage == '3') {
355
+        this.dgtLabel = '党委审核意见'
356
+      } else {
357
+        this.dgtLabel = '团委审核意见'
358
+      }
359
+    },
321 360
     // 计算超过预算的金额
322 361
     getMoreAmount(type) {
323 362
       let result;
@@ -406,7 +445,6 @@ export default {
406 445
             this.form[currentConfig.userIdField] = currentUserId
407 446
             this.form[currentConfig.timeField] = currentTime
408 447
           }
409
-
410 448
           // 更新借款申请
411 449
           await updateBorrow(this.form);
412 450
           const params = { taskId: this.taskForm.taskId };
@@ -471,8 +509,9 @@ export default {
471 509
     // 借款申请提交方法
472 510
     async borrowApprovalFun() {
473 511
       let userId;
474
-      console.log(this.deptId);
475
-
512
+      if (this.form.borrowUsage == '2' || this.form.borrowUsage == '3' || this.form.borrowUsage == '4') {
513
+        this.deptId = 0;
514
+      }
476 515
       // 如果是党工团申请 deptId == 0 为党工团申请
477 516
       if (this.deptId === 0) {
478 517
         // 2为工会、3为党委
@@ -486,7 +525,8 @@ export default {
486 525
         this.taskForm.variables.dept = this.deptId;
487 526
         this.taskForm.variables.approval = userId;
488 527
         this.handleComplete(this.taskForm);
489
-      } else if (this.deptId == 102) { //如果是经营管理层申请,走总经理审批
528
+      }
529
+      else if (this.deptId == 102) { //如果是经营管理层申请,走总经理审批
490 530
         this.form.managerAmount = this.form.applyAmount;
491 531
         await updateBorrow(this.form);
492 532
         const res = await getUserByPost({ postName: '总经理' });

+ 6
- 4
oa-ui-app/pages/form/borrow/borrowDetail.vue Voir le fichier

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-27 10:43:04
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-18 16:36:32
5
+ * @LastEditTime: 2025-03-19 10:34:18
6 6
 -->
7 7
 <template>
8 8
   <view class="container">
@@ -28,8 +28,8 @@
28 28
           <text class="total-amount">¥{{ item.applyAmount }}</text>
29 29
         </view>
30 30
       </view>
31
-      <uv-divider :dashed="true" v-if="taskName != '借款申请' && taskName != '部门审核'"></uv-divider>
32
-      <view class="list-item" v-if="taskName != '借款申请' && taskName != '部门审核'">
31
+      <uv-divider :dashed="true" v-if="taskName != '借款申请' && taskName != '部门审核' && taskName != '党工团审核'"></uv-divider>
32
+      <view class="list-item" v-if="taskName != '借款申请' && taskName != '部门审核' && taskName != '党工团审核'">
33 33
         <view class="item-left">
34 34
           <text style="color:#398ade;padding-left: 10rpx;">分管审核金额</text>
35 35
         </view>
@@ -297,12 +297,14 @@ export default {
297 297
   font-weight: bold;
298 298
   padding-right: 30rpx;
299 299
 }
300
-.mg-amount{
300
+
301
+.mg-amount {
301 302
   font-size: 30rpx;
302 303
   color: #398ade;
303 304
   font-weight: bold;
304 305
   padding-right: 30rpx;
305 306
 }
307
+
306 308
 .delete {
307 309
   font-size: 30rpx;
308 310
   color: #ff6a6c;

+ 131
- 8
oa-ui-app/pages/message/index.vue Voir le fichier

@@ -2,10 +2,14 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-01-21 10:01:39
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-17 16:01:12
5
+ * @LastEditTime: 2025-03-19 16:21:31
6 6
 -->
7 7
 <template>
8
-	<view class="u-page">
8
+	<view class="u-page container">
9
+		<view class="send" @click="openSendFlow">
10
+			<!-- <uv-icon name="bell-fill" color="#2979ff" size="50"></uv-icon> -->
11
+			<uni-icons type="paperplane-filled" size="50" color="#2979ff"></uni-icons>
12
+		</view>
9 13
 		<scroll-view class="scroll-list" scroll-y="true">
10 14
 			<view class="uni-list">
11 15
 				<view class="uni-list-cell">
@@ -14,10 +18,10 @@
14 18
 							<u-image :fade="false" src="@/static/images/message/apply.png" width="40px" height="40px"></u-image>
15 19
 						</view>
16 20
 						<view class="uni-media-list-body">
17
-							<view class="uni-media-list-text-top"><span>审批</span>
21
+							<view class="uni-media-list-text-top"><span>待办任务</span>
18 22
 							</view>
19 23
 							<view class="uni-media-list-text-bottom">
20
-								<uni-text><span>您有新流程待审批</span></uni-text>
24
+								<uni-text><span>您有新流程待</span></uni-text>
21 25
 							</view>
22 26
 						</view>
23 27
 						<view class="info">
@@ -56,23 +60,47 @@
56 60
 				</view>
57 61
 			</view>
58 62
 		</scroll-view>
63
+
64
+		<uv-popup ref="popup" mode="bottom">
65
+			<view class="bottom-popup">
66
+				<view v-for="item, index in sendFlowList" :key="index">
67
+					<u-button @click="sendFlow(item)">{{ item.name }}</u-button>
68
+				</view>
69
+			</view>
70
+		</uv-popup>
59 71
 	</view>
60 72
 </template>
61 73
 
62 74
 <script>
63
-import {
64
-	todoList
65
-} from "@/api/flowable/todo";
75
+import { Snowflake } from '@/utils/snowFlake.js';
76
+import { getNextFlowNodeByStart, todoList } from "@/api/flowable/todo";
77
+import { listDefinition, definitionStart, flowXmlAndNode } from "@/api/flowable/definition";
66 78
 export default {
67 79
 	data() {
68 80
 		return {
69 81
 			todoList: [],
70 82
 			detail: 0,
71
-			acceptTime: ''
83
+			acceptTime: '',
84
+			flowList: ['借款审批', '用车审批', '设备审批', '工作填报'],
85
+			sendFlowList: [],
86
+			queryProcessParams: {
87
+				pageNum: 1,
88
+				pageSize: 9999,
89
+				name: null,
90
+				category: null,
91
+				key: null,
92
+				tenantId: null,
93
+				deployTime: null,
94
+				derivedFrom: null,
95
+				derivedFromRoot: null,
96
+				parentDeploymentId: null,
97
+				engineVersion: null
98
+			},
72 99
 		};
73 100
 	},
74 101
 	created() {
75 102
 		this.getTodoList();
103
+		this.getDefinitionList();
76 104
 	},
77 105
 	onLoad: function (options) {
78 106
 		uni.startPullDownRefresh();
@@ -94,6 +122,78 @@ export default {
94 122
 				}
95 123
 				uni.stopPullDownRefresh();
96 124
 			});
125
+		},
126
+		getDefinitionList() {
127
+			listDefinition(this.queryProcessParams).then(response => {
128
+				this.definitionList = response.data.records;
129
+				this.processTotal = response.data.total;
130
+				this.processLoading = false;
131
+				let list = []
132
+				for (let i of this.definitionList) {
133
+					if (this.flowList.includes(i.name)) {
134
+						list.push(i)
135
+					}
136
+				}
137
+				this.sendFlowList = list;
138
+			});
139
+		},
140
+		openSendFlow() {
141
+			this.$refs.popup.open();
142
+		},
143
+		sendFlow(row) {
144
+			uni.showModal({
145
+				title: '提示',
146
+				content: '是否发起《'+row.name+'》流程?',
147
+				success: (res) => {
148
+					if (!res.confirm) {
149
+						return;
150
+					}
151
+					// 继续执行后续代码
152
+					this.sendFlowFun(row);
153
+				}
154
+			});
155
+		},
156
+		sendFlowFun(row) {
157
+			let formId = new Snowflake(1n, 1n, 0n).nextId().toString();
158
+			getNextFlowNodeByStart({ deploymentId: row.deploymentId, variables: { formId: formId } }).then(res => {
159
+				let data = res.data;
160
+				const variables = {};
161
+				const formData = {};
162
+				formData.disabled = true;
163
+				formData.formBtns = false;
164
+				formData.formId = formId
165
+				if (row.id) {
166
+					variables.variables = formData;
167
+					definitionStart(row.id, JSON.stringify(variables)).then(res => {
168
+						this.$modal.msgSuccess(res.msg);
169
+						let procInstanceId = res.data;
170
+						todoList({
171
+							pageNum: 1,
172
+							pageSize: 99999999, processInsId: procInstanceId
173
+						}).then(toDoRes => {
174
+							let records = toDoRes.data.records;
175
+							if (records.length == 1) {
176
+								records = records[0]
177
+							}
178
+							const query = {
179
+								procInsId: records.procInsId,
180
+								executionId: records.executionId,
181
+								deployId: records.deployId,
182
+								taskId: records.taskId,
183
+								taskName: records.taskName,
184
+								startUserName: records.startUserName + '-' + records.startDeptName,
185
+								formId: formData.formId,
186
+								procDefName: records.procDefName
187
+							}
188
+							const encodedParams = encodeURIComponent(JSON.stringify(query));
189
+							uni.navigateTo({
190
+								url: `/pages/message/apply/detail?params=${encodedParams}`
191
+							})
192
+							this.$refs.popup.close();
193
+						})
194
+					})
195
+				}
196
+			})
97 197
 		}
98 198
 	},
99 199
 };
@@ -110,6 +210,29 @@ export default {
110 210
 		top: 45rpx;
111 211
 	}
112 212
 }
213
+
214
+.container {
215
+	// position: relative;
216
+}
217
+
218
+.send {
219
+	position: absolute;
220
+	right: 0px;
221
+	bottom: 10px;
222
+	width: 50px;
223
+	height: 50px;
224
+	background-color: #fff;
225
+	border-radius: 4px;
226
+}
227
+
228
+.bottom-popup {
229
+	max-height: 50vh;
230
+	margin-bottom: 65px;
231
+	padding: 10px;
232
+	text-align: center;
233
+
234
+	.popup-item {}
235
+}
113 236
 </style>
114 237
 <style>
115 238
 .icon {

+ 315
- 4
oa-ui-app/pages/message/myProcess/index.vue Voir le fichier

@@ -1,15 +1,326 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-02-19 15:36:34
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-20 15:27:00
6
+-->
1 7
 <template>
2
-  <view>
8
+  <view class="page-container">
9
+    <!-- 搜索框 -->
10
+    <view class="search-box">
11
+      <uni-search-bar radius="34" placeholder="请输入流程名称" @confirm="search" @input="inputChange"
12
+        v-model="queryParams.procDefName" />
13
+    </view>
3 14
 
15
+    <!-- 流程列表 -->
16
+    <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :fixed="false" :top="10">
17
+      <uni-list>
18
+        <uni-list-item v-for="(item, index) in list" :key="index" clickable>
19
+          <template v-slot:body>
20
+            <view class="list-item">
21
+              <!-- 状态与标题行 -->
22
+              <view class="header-line">
23
+                <uni-tag :text="item.finishTime ? '已完成' : '进行中'" :type="item.finishTime ? 'success' : 'warning'"
24
+                  size="small" class="status-tag" />
25
+                <text class="proc-name">{{ item.procDefName }}</text>
26
+              </view>
27
+
28
+              <!-- 主要内容 -->
29
+              <view class="content-box">
30
+                <!-- 标题 -->
31
+                <view class="title-box">
32
+                  <uni-icons type="info" size="16" color="#666" class="title-icon"></uni-icons>
33
+                  <text class="title-text">{{ item.title }}</text>
34
+                </view>
35
+
36
+                <!-- 信息分割线 -->
37
+                <uv-divider :dashed="true" margin="10rpx 0"></uv-divider>
38
+
39
+                <!-- 详细信息 -->
40
+                <view class="detail-grid">
41
+                  <view class="detail-item">
42
+                    <uni-icons type="calendar" size="14" color="#999"></uni-icons>
43
+                    <text class="detail-text">{{ item.createTime }}</text>
44
+                  </view>
45
+
46
+                  <view class="detail-item">
47
+                    <uni-icons type="clock" size="14" color="#999"></uni-icons>
48
+                    <text class="detail-text">{{ item.duration }}</text>
49
+                  </view>
50
+
51
+                  <view class="detail-item">
52
+                    <uni-icons type="location" size="14" color="#999"></uni-icons>
53
+                    <text class="detail-text">{{ item.taskName }}</text>
54
+                  </view>
55
+
56
+                  <view class="detail-item">
57
+                    <uni-icons type="person" size="14" color="#999"></uni-icons>
58
+                    <text class="detail-text">{{ item.assigneeName || '暂无待办人' }}</text>
59
+                  </view>
60
+                </view>
61
+              </view>
62
+
63
+              <!-- 操作按钮 -->
64
+              <view class="action-box">
65
+                <text class="view-detail">查看详情</text>
66
+                <uni-icons type="more-filled" size="20" color="#666" @click="showAction(item)"
67
+                  class="action-icon"></uni-icons>
68
+              </view>
69
+            </view>
70
+          </template>
71
+        </uni-list-item>
72
+      </uni-list>
73
+    </mescroll-uni>
74
+
75
+    <!-- 操作菜单 -->
76
+    <uni-popup ref="actionPopup" type="bottom">
77
+      <view class="action-menu">
78
+        <view class="menu-item" @click="handleFlowRecord(selectedItem)">
79
+          <text>办理进度</text>
80
+        </view>
81
+        <view class="menu-item" @click="handleFlowNote(selectedItem)">
82
+          <text>表单信息</text>
83
+        </view>
84
+        <view class="menu-item danger" @click="handleDelete(selectedItem)"
85
+          :class="{ disabled: !beDeleted(selectedItem) }">
86
+          <text>取消流程</text>
87
+        </view>
88
+      </view>
89
+    </uni-popup>
90
+
91
+    <!-- 菜单抽屉 -->
92
+    <uni-drawer ref="drawer" mode="right" :width="300">
93
+      <flow-record :rows="selectedItem" v-if="active == 'record'"></flow-record>
94
+    </uni-drawer>
4 95
   </view>
5 96
 </template>
6 97
 
7 98
 <script>
8
-  export default {
9
-    
10
-  }
99
+import { debounce } from 'lodash-es';
100
+import MescrollMixin from '@/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js'
101
+import { myProcessList, stopProcess } from "@/api/flowable/process";
102
+import FlowRecord from '../../components/flowable/FlowRecord.vue';
103
+export default {
104
+  components: { FlowRecord },
105
+  mixins: [MescrollMixin],
106
+  data() {
107
+    return {
108
+      // 我发起的流程列表数据
109
+      myProcessList: [],
110
+      list: [], // 列表数据
111
+      queryParams: {
112
+        keyword: '',
113
+        pageNum: 1,
114
+        pageSize: 10
115
+      },
116
+      selectedItem: null, // 当前选中项
117
+      debounceSearch: null, // 初始化防抖方法
118
+      clickRow: {},
119
+      active: 'record'
120
+    }
121
+  },
122
+  created() {
123
+    // 在生命周期中创建防抖方法
124
+    this.debounceSearch = debounce(() => {
125
+      this.search()
126
+    }, 500)
127
+  },
128
+  methods: {
129
+    // mescroll下拉刷新回调
130
+    async downCallback() {
131
+      this.queryParams.pageNum = 1
132
+      const res = await this.loadData()
133
+      this.list = res.data.list;
134
+      this.mescroll.endSuccess(res.data.length)
135
+      this.mescroll.endErr()
136
+    },
137
+    // mescroll上拉加载回调
138
+    async upCallback(page) {
139
+      this.queryParams.pageNum = page.num
140
+      const res = await this.loadData()
141
+      this.list = this.list.concat(res.data.list)
142
+      this.mescroll.endSuccess(res.data.list.length, res.data.hasNextPage)
143
+    },
144
+    // 加载数据
145
+    async loadData() {
146
+      try {
147
+        const res = await myProcessList(this.queryParams)
148
+        return {
149
+          data: {
150
+            list: res.data.records,
151
+            hasNextPage: res.hasNextPage
152
+          }
153
+        }
154
+      } catch (e) {
155
+        this.mescroll.endErr()
156
+        return { data: { list: [], hasNextPage: false } }
157
+      }
158
+    },
159
+    // 显示操作菜单
160
+    showAction(item) {
161
+      this.selectedItem = item
162
+      this.$refs.actionPopup.open()
163
+    },
164
+    // 输入变化处理
165
+    inputChange() {
166
+      this.debounceSearch()
167
+    },
168
+
169
+    // 执行搜索
170
+    search() {
171
+      this.loadData()
172
+      this.mescroll.resetUpScroll()
173
+    },
174
+
175
+    handleFlowRecord(row) {
176
+      console.log(row);
177
+      this.active = 'record';
178
+      this.$refs.drawer.open();
179
+    },
180
+    handleFlowNote(row) { /* ... */ },
181
+    handleDelete(row) { /* ... */ },
182
+    beDeleted(row) { /* ... */ },
183
+  },
184
+}
11 185
 </script>
12 186
 
13 187
 <style lang="scss" scoped>
188
+.list-item {
189
+  padding: 24rpx;
190
+  background: #fff;
191
+  border-radius: 16rpx;
192
+  margin: 16rpx 24rpx;
193
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
194
+  width: 100%;
195
+}
196
+
197
+/* 头部状态行 */
198
+.header-line {
199
+  display: flex;
200
+  align-items: center;
201
+  margin-bottom: 20rpx;
202
+}
203
+
204
+.proc-name {
205
+  font-size: 30rpx;
206
+  color: #333;
207
+  font-weight: 500;
208
+  margin-left: 16rpx;
209
+}
210
+
211
+.status-tag {
212
+  transform: translateY(-2rpx);
213
+}
214
+
215
+/* 内容区域 */
216
+.content-box {
217
+  padding: 12rpx 0;
218
+}
219
+
220
+.title-box {
221
+  display: flex;
222
+  align-items: center;
223
+  margin-bottom: 16rpx;
224
+}
225
+
226
+.title-icon {
227
+  margin-right: 8rpx;
228
+}
229
+
230
+.title-text {
231
+  font-size: 28rpx;
232
+  color: #333;
233
+  font-weight: 600;
234
+}
235
+
236
+/* 详细信息网格 */
237
+.detail-grid {
238
+  display: grid;
239
+  grid-template-columns: repeat(2, 1fr);
240
+  gap: 16rpx 24rpx;
241
+  margin-top: 20rpx;
242
+}
243
+
244
+.detail-item {
245
+  display: flex;
246
+  align-items: center;
247
+}
248
+
249
+.detail-text {
250
+  font-size: 24rpx;
251
+  color: #666;
252
+  margin-left: 8rpx;
253
+  overflow: hidden;
254
+  text-overflow: ellipsis;
255
+}
256
+
257
+/* 操作区域 */
258
+.action-box {
259
+  display: flex;
260
+  justify-content: space-between;
261
+  align-items: center;
262
+  margin-top: 24rpx;
263
+  padding-top: 16rpx;
264
+  border-top: 1rpx solid #eee;
265
+}
266
+
267
+.view-detail {
268
+  color: #2979FF;
269
+  font-size: 26rpx;
270
+  padding: 8rpx 16rpx;
271
+  border-radius: 8rpx;
272
+  background: #f5f7fa;
273
+}
274
+
275
+.action-icon {
276
+  padding: 8rpx;
277
+}
278
+
279
+/* 添加点击态效果 */
280
+.action-box:active {
281
+  background: #f8f8f8;
282
+  transform: scale(0.98);
283
+  transition: all 0.2s;
284
+}
285
+
286
+/* 状态标签增强 */
287
+.status-tag {
288
+  padding: 4rpx 12rpx !important;
289
+  border-radius: 8rpx !important;
290
+}
291
+
292
+/* 颜色增强 */
293
+.proc-name {
294
+  color: #1a1a1a;
295
+}
296
+
297
+.title-text {
298
+  color: #2d2d2d;
299
+}
300
+
301
+/* 图标颜色统一 */
302
+.detail-item uni-icons {
303
+  color: #909399 !important;
304
+}
305
+
306
+
307
+.action-menu {
308
+  background: #fff;
309
+  border-radius: 16rpx 16rpx 0 0;
310
+  padding: 30rpx;
311
+}
312
+
313
+.menu-item {
314
+  padding: 30rpx;
315
+  border-bottom: 1rpx solid #eee;
316
+}
317
+
318
+.menu-item.danger {
319
+  color: #f56c6c;
320
+}
14 321
 
322
+.menu-item.disabled {
323
+  color: #c0c4cc;
324
+  pointer-events: none;
325
+}
15 326
 </style>

BIN
oa-ui-app/static/font/auditor.ttf Voir le fichier


+ 10
- 0
oa-ui-app/static/scss/global.scss Voir le fichier

@@ -155,6 +155,16 @@
155 155
   }
156 156
 }
157 157
 
158
+@font-face {
159
+  font-family: 'auditor'; // 根据自己用的去重命名字体名 
160
+  src: url('~@/static/font/auditor.ttf');
161
+}
162
+
163
+.auditor {
164
+  font-family: 'auditor';
165
+  font-size: 20px;
166
+}
167
+
158 168
 ::v-deep .uni-input-input:disabled {
159 169
   -webkit-text-fill-color: #333333;
160 170
 }

+ 25
- 0
oa-ui-app/uni_modules/mescroll/App.vue Voir le fichier

@@ -0,0 +1,25 @@
1
+<script>
2
+	export default {
3
+		onLaunch: function () {
4
+			console.log('App Launch')
5
+		},
6
+		onShow: function () {
7
+			console.log('App Show')
8
+		},
9
+		onHide: function () {
10
+			console.log('App Hide')
11
+		}
12
+	}
13
+</script>
14
+
15
+<style>
16
+	page{background-color: #fff}
17
+	
18
+	view,
19
+	text,
20
+	image,
21
+	input,
22
+	textarea {
23
+		box-sizing: border-box;
24
+	}
25
+</style>

+ 157
- 0
oa-ui-app/uni_modules/mescroll/api/goods-edit.js Voir le fichier

@@ -0,0 +1,157 @@
1
+export default [{
2
+	"id": "3",
3
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd3.jpg",
4
+	"goodName": "【3】 美素佳儿Friso婴儿配方奶粉3段 ( 商品【1】【2】 已删除 )",
5
+	"goodPrice": 195.00,
6
+	"goodSold": 968
7
+}, {
8
+	"id": "4",
9
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd4.jpg",
10
+	"goodName": "【4】  Fisher goodPrice费雪 费雪三轮儿童滑行车",
11
+	"goodPrice": 298.00,
12
+	"goodSold": 65
13
+}, {
14
+	"id": "5",
15
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd5.jpg",
16
+	"goodName": "【5】  Babylee巴布力 实木婴儿床 雷卡拉130*70cm",
17
+	"goodPrice": 1789.00,
18
+	"goodSold": 20
19
+}, {
20
+	"id": "6",
21
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd6.jpg",
22
+	"goodName": "【6】  Pigeon贝亲 独立三层奶粉盒 送小罐奶粉1段200g",
23
+	"goodPrice": 70.00,
24
+	"goodSold": 658
25
+}, {
26
+	"id": "7",
27
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd7.jpg",
28
+	"goodName": "【7】 TTBOO兔兔小布 肩纽扣套装",
29
+	"goodPrice": 268.00,
30
+	"goodSold": 128
31
+}, {
32
+	"id": "8",
33
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd8.jpg",
34
+	"goodName": "【8】  Nuna璐拉 婴儿布里奇果精纯嫩肤沐浴露婴儿精纯芦荟胶",
35
+	"goodPrice": 140.00,
36
+	"goodSold": 366
37
+}, {
38
+	"id": "9",
39
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd9.jpg",
40
+	"goodName": "【9】  illuma启赋 奶粉3段900g",
41
+	"goodPrice": 252.00,
42
+	"goodSold": 98
43
+}, {
44
+	"id": "10",
45
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd10.jpg",
46
+	"goodName": "【10】  Abbott雅培乳蛋白部分水解婴儿配方奶粉3段820g",
47
+	"goodPrice": 89.00,
48
+	"goodSold": 128
49
+}, {
50
+	"id": "11",
51
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd11.jpg",
52
+	"goodName": "【11】  韩蜜 酷炫唇蜜(礼盒套装)2.8g*4",
53
+	"goodPrice": 179.00,
54
+	"goodSold": 35
55
+}, {
56
+	"id": "12",
57
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd12.jpg",
58
+	"goodName": "【12】  保税区直发【3包装】日本Merries花王纸尿裤NB90",
59
+	"goodPrice": 289.00,
60
+	"goodSold": 1928
61
+}, {
62
+	"id": "13",
63
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd13.jpg",
64
+	"goodName": "【13】  Comotomo可么多么 硅胶奶瓶(0-3月奶嘴)150ml绿色",
65
+	"goodPrice": 203.00,
66
+	"goodSold": 87
67
+}, {
68
+	"id": "14",
69
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd14.jpg",
70
+	"goodName": "【14】  香港直邮德国瑞德露Rival de Loop芦荟精华安瓶",
71
+	"goodPrice": 152.00,
72
+	"goodSold": 61
73
+}, {
74
+	"id": "15",
75
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd15.jpg",
76
+	"goodName": "【15】  保税区直发药师堂尊马油香草味温和保湿无刺激面霜",
77
+	"goodPrice": 269.00,
78
+	"goodSold": 73
79
+}, {
80
+	"id": "16",
81
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd16.jpg",
82
+	"goodName": "【16】  香港直邮日本Spatreatment眼膜保湿去细纹法令纹",
83
+	"goodPrice": 219.00,
84
+	"goodSold": 13
85
+}, {
86
+	"id": "17",
87
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd17.jpg",
88
+	"goodName": "【17】  韩国MEDIHEALNMF可莱丝针剂睡眠面膜",
89
+	"goodPrice": 81.00,
90
+	"goodSold": 128
91
+}, {
92
+	"id": "18",
93
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd18.jpg",
94
+	"goodName": "【18】  DHC蝶翠诗橄榄蜂蜜滋养洗脸手工皂90g",
95
+	"goodPrice": 123.00,
96
+	"goodSold": 77
97
+}, {
98
+	"id": "19",
99
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd19.jpg",
100
+	"goodName": "【19】  日本资生堂CPB肌肤之钥新版隔离霜 清爽型 30ml",
101
+	"goodPrice": 429.00,
102
+	"goodSold": 36
103
+}, {
104
+	"id": "20",
105
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd20.jpg",
106
+	"goodName": "【20】 Heinz亨氏 婴儿面条优加面条全素套餐组合3口味3盒",
107
+	"goodPrice": 39.00,
108
+	"goodSold": 61
109
+}, {
110
+	"id": "21",
111
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd21.jpg",
112
+	"goodName": "【21】  Heinz亨氏 乐维滋果汁泥组合5口味15袋",
113
+	"goodPrice": 69.00,
114
+	"goodSold": 55
115
+}, {
116
+	"id": "22",
117
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd22.jpg",
118
+	"goodName": "【22】  保税区直发澳大利亚Swisse高浓度蔓越莓胶囊30粒",
119
+	"goodPrice": 271.00,
120
+	"goodSold": 19
121
+}, {
122
+	"id": "23",
123
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd23.jpg",
124
+	"goodName": "【23】  挪威Nordic Naturals小鱼婴幼儿鱼油DHA滴剂",
125
+	"goodPrice": 102.00,
126
+	"goodSold": 125
127
+}, {
128
+	"id": "24",
129
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd24.jpg",
130
+	"goodName": "【24】  澳大利亚Bio island DHA for Pregnancy海藻油DHA",
131
+	"goodPrice": 289.00,
132
+	"goodSold": 28
133
+}, {
134
+	"id": "25",
135
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd25.jpg",
136
+	"goodName": "【25】  澳大利亚Fatblaster Coconut Detox椰子水",
137
+	"goodPrice": 152.00,
138
+	"goodSold": 17
139
+}, {
140
+	"id": "26",
141
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd26.jpg",
142
+	"goodName": "【26】  Suitsky舒比奇 高护极薄舒爽纸尿片尿不湿XL60",
143
+	"goodPrice": 99.00,
144
+	"goodSold": 181
145
+}, {
146
+	"id": "27",
147
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd27.jpg",
148
+	"goodName": "【27】  英国JUST SOAP手工皂 玫瑰天竺葵蛋糕皂",
149
+	"goodPrice": 72.00,
150
+	"goodSold": 66
151
+}, {
152
+	"id": "28",
153
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd28.jpg",
154
+	"goodName": "【28】  德国NUK 多色婴幼儿带盖学饮杯",
155
+	"goodPrice": 92.00,
156
+	"goodSold": 138
157
+}]

+ 169
- 0
oa-ui-app/uni_modules/mescroll/api/goods.js Voir le fichier

@@ -0,0 +1,169 @@
1
+export default [{
2
+	"id": "1",
3
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd1.jpg",
4
+	"goodName": "【1】  六罐装荷兰美素佳儿金装2段900g",
5
+	"goodPrice": 1149.00,
6
+	"goodSold": 648
7
+}, {
8
+	"id": "2",
9
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd2.jpg",
10
+	"goodName": "【2】  韩国Amore爱茉莉红吕洗发水套装修复受损发质",
11
+	"goodPrice": 89.00,
12
+	"goodSold": 128
13
+}, {
14
+	"id": "3",
15
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd3.jpg",
16
+	"goodName": "【3】  Friso美素佳儿 金装婴儿配方奶粉3段900g",
17
+	"goodPrice": 195.00,
18
+	"goodSold": 968
19
+}, {
20
+	"id": "4",
21
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd4.jpg",
22
+	"goodName": "【4】  Fisher goodPrice费雪 费雪三轮儿童滑行车",
23
+	"goodPrice": 299.00,
24
+	"goodSold": 85
25
+}, {
26
+	"id": "5",
27
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd5.jpg",
28
+	"goodName": "【5】  Babylee巴布力 实木婴儿床 雷卡拉130*70cm",
29
+	"goodPrice": 1889.00,
30
+	"goodSold": 18
31
+}, {
32
+	"id": "6",
33
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd6.jpg",
34
+	"goodName": "【6】  Pigeon贝亲 独立三层奶粉盒 送小罐奶粉1段200g",
35
+	"goodPrice": 70.00,
36
+	"goodSold": 658
37
+}, {
38
+	"id": "7",
39
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd7.jpg",
40
+	"goodName": "【7】 TTBOO兔兔小布 肩纽扣套装",
41
+	"goodPrice": 268.00,
42
+	"goodSold": 128
43
+}, {
44
+	"id": "8",
45
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd8.jpg",
46
+	"goodName": "【8】  Nuna璐拉 婴儿布里奇果精纯嫩肤沐浴露婴儿精纯芦荟胶",
47
+	"goodPrice": 140.00,
48
+	"goodSold": 366
49
+}, {
50
+	"id": "9",
51
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd9.jpg",
52
+	"goodName": "【9】  illuma启赋 奶粉3段900g",
53
+	"goodPrice": 252.00,
54
+	"goodSold": 98
55
+}, {
56
+	"id": "10",
57
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd10.jpg",
58
+	"goodName": "【10】  Abbott雅培乳蛋白部分水解婴儿配方奶粉3段820g",
59
+	"goodPrice": 89.00,
60
+	"goodSold": 128
61
+}, {
62
+	"id": "11",
63
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd11.jpg",
64
+	"goodName": "【11】  韩蜜 酷炫唇蜜(礼盒套装)2.8g*4",
65
+	"goodPrice": 179.00,
66
+	"goodSold": 35
67
+}, {
68
+	"id": "12",
69
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd12.jpg",
70
+	"goodName": "【12】  保税区直发【3包装】日本Merries花王纸尿裤NB90",
71
+	"goodPrice": 289.00,
72
+	"goodSold": 1928
73
+}, {
74
+	"id": "13",
75
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd13.jpg",
76
+	"goodName": "【13】  Comotomo可么多么 硅胶奶瓶(0-3月奶嘴)150ml绿色",
77
+	"goodPrice": 203.00,
78
+	"goodSold": 87
79
+}, {
80
+	"id": "14",
81
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd14.jpg",
82
+	"goodName": "【14】  香港直邮德国瑞德露Rival de Loop芦荟精华安瓶",
83
+	"goodPrice": 152.00,
84
+	"goodSold": 61
85
+}, {
86
+	"id": "15",
87
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd15.jpg",
88
+	"goodName": "【15】  保税区直发药师堂尊马油香草味温和保湿无刺激面霜",
89
+	"goodPrice": 269.00,
90
+	"goodSold": 73
91
+}, {
92
+	"id": "16",
93
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd16.jpg",
94
+	"goodName": "【16】  香港直邮日本Spatreatment眼膜保湿去细纹法令纹",
95
+	"goodPrice": 219.00,
96
+	"goodSold": 13
97
+}, {
98
+	"id": "17",
99
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd17.jpg",
100
+	"goodName": "【17】  韩国MEDIHEALNMF可莱丝针剂睡眠面膜",
101
+	"goodPrice": 81.00,
102
+	"goodSold": 128
103
+}, {
104
+	"id": "18",
105
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd18.jpg",
106
+	"goodName": "【18】  DHC蝶翠诗橄榄蜂蜜滋养洗脸手工皂90g",
107
+	"goodPrice": 123.00,
108
+	"goodSold": 77
109
+}, {
110
+	"id": "19",
111
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd19.jpg",
112
+	"goodName": "【19】  日本资生堂CPB肌肤之钥新版隔离霜 清爽型 30ml",
113
+	"goodPrice": 429.00,
114
+	"goodSold": 36
115
+}, {
116
+	"id": "20",
117
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd20.jpg",
118
+	"goodName": "【20】 Heinz亨氏 婴儿面条优加面条全素套餐组合3口味3盒",
119
+	"goodPrice": 39.00,
120
+	"goodSold": 61
121
+}, {
122
+	"id": "21",
123
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd21.jpg",
124
+	"goodName": "【21】  Heinz亨氏 乐维滋果汁泥组合5口味15袋",
125
+	"goodPrice": 69.00,
126
+	"goodSold": 55
127
+}, {
128
+	"id": "22",
129
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd22.jpg",
130
+	"goodName": "【22】  保税区直发澳大利亚Swisse高浓度蔓越莓胶囊30粒",
131
+	"goodPrice": 271.00,
132
+	"goodSold": 19
133
+}, {
134
+	"id": "23",
135
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd23.jpg",
136
+	"goodName": "【23】  挪威Nordic Naturals小鱼婴幼儿鱼油DHA滴剂",
137
+	"goodPrice": 102.00,
138
+	"goodSold": 125
139
+}, {
140
+	"id": "24",
141
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd24.jpg",
142
+	"goodName": "【24】  澳大利亚Bio island DHA for Pregnancy海藻油DHA",
143
+	"goodPrice": 289.00,
144
+	"goodSold": 28
145
+}, {
146
+	"id": "25",
147
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd25.jpg",
148
+	"goodName": "【25】  澳大利亚Fatblaster Coconut Detox椰子水",
149
+	"goodPrice": 152.00,
150
+	"goodSold": 17
151
+}, {
152
+	"id": "26",
153
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd26.jpg",
154
+	"goodName": "【26】  Suitsky舒比奇 高护极薄舒爽纸尿片尿不湿XL60",
155
+	"goodPrice": 99.00,
156
+	"goodSold": 181
157
+}, {
158
+	"id": "27",
159
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd27.jpg",
160
+	"goodName": "【27】  英国JUST SOAP手工皂 玫瑰天竺葵蛋糕皂",
161
+	"goodPrice": 72.00,
162
+	"goodSold": 66
163
+}, {
164
+	"id": "28",
165
+	"goodImg": "https://www.mescroll.com/demo/res/img/pd28.jpg",
166
+	"goodName": "【28】  德国NUK 多色婴幼儿带盖学饮杯",
167
+	"goodPrice": 92.00,
168
+	"goodSold": 138
169
+}]

+ 169
- 0
oa-ui-app/uni_modules/mescroll/api/mock.js Voir le fichier

@@ -0,0 +1,169 @@
1
+/*
2
+本地模拟接口请求, 仅demo演示用.
3
+实际项目以您服务器接口返回的数据为准,无需本地处理分页.
4
+请参考官方写法: https://www.mescroll.com/uni.html?v=20200210#tagUpCallback
5
+* */
6
+
7
+// 模拟数据
8
+import goods from "./goods.js";
9
+import goodsEdit from "./goods-edit.js";
10
+
11
+// 获取新闻列表
12
+export function apiNewList(pageNum, pageSize) {
13
+	return new Promise((resolute, reject)=>{
14
+		//延时一秒,模拟联网
15
+		setTimeout(function() {
16
+			try {
17
+				let list = [];
18
+				if (!pageNum) {
19
+					//模拟下拉刷新返回的数据
20
+					let id=new Date().getTime();
21
+					let newObj = {
22
+						id:id,
23
+						title: "【新增新闻" + id + "】 标题",
24
+						content: "新增新闻的内容"
25
+					};
26
+					list.push(newObj);
27
+				} else {
28
+					//模拟上拉加载返回的数据
29
+					for (let i = 0; i < pageSize; i++) {
30
+						let upIndex = (pageNum - 1) * pageSize + i + 1;
31
+						let newObj = {
32
+							id:upIndex,
33
+							title: "【新闻" + upIndex + "】 标题标题标题标题标题",
34
+							content: "内容内容内容内容内容内容内容内容内容"
35
+						};
36
+						list.push(newObj);
37
+					}
38
+					console.log("page.num=" + pageNum + ", page.size=" + pageSize + ", curPageData.length=" + list.length);
39
+				}
40
+				//模拟接口请求成功
41
+				resolute(list);
42
+			} catch (e) {
43
+				//模拟接口请求失败
44
+				reject(e);
45
+			}
46
+		}, 1000)
47
+	})
48
+}
49
+
50
+// 获取商品列表数据
51
+export function apiGoods(pageNum, pageSize, isGoodsEdit) {
52
+	return new Promise((resolute, reject)=>{
53
+		//延时一秒,模拟联网
54
+		setTimeout(()=> {
55
+			try{
56
+				let data = isGoodsEdit ? goodsEdit : goods;
57
+				//模拟分页数据
58
+				let list=[];
59
+				for (let i = (pageNum-1)*pageSize; i < pageNum*pageSize; i++) {
60
+					if(i==data.length) break;
61
+					list.push(data[i]);
62
+				}
63
+				//模拟接口请求成功
64
+				console.log("page.num=" + pageNum + ", page.size=" + pageSize + ", curPageData.length=" + list.length);
65
+				resolute(list);
66
+			} catch (e) {
67
+				//模拟接口请求失败
68
+				reject(e);
69
+			}
70
+		},1000)
71
+	})
72
+}
73
+
74
+// 搜索商品
75
+export function apiSearch(pageNum, pageSize, keyword) {
76
+	return new Promise((resolute, reject)=>{
77
+		//延时一秒,模拟联网
78
+		setTimeout(()=> {
79
+			try{
80
+				// 模拟搜索
81
+				let list = []
82
+				if (!keyword || keyword == "全部") {
83
+					// 模拟搜索全部商品
84
+					for (let i = (pageNum - 1) * pageSize; i < pageNum * pageSize; i++) {
85
+						if (i === goods.length) break
86
+						list.push(goods[i])
87
+					}
88
+				} else{
89
+					// 模拟关键词搜索
90
+					if(keyword=="母婴") keyword="婴"; // 为这个关键词展示多几条数据
91
+					for (let i = 0; i < goods.length; i++) {
92
+						if (goods[i].goodName.indexOf(keyword) !== -1) {
93
+							list.push(goods[i])
94
+						}
95
+					}
96
+				}
97
+				//模拟接口请求成功
98
+				console.log("page.num=" + pageNum + ", page.size=" + pageSize + ", curPageData.length=" + list.length+", keyword="+keyword);
99
+				resolute(list);
100
+			} catch (e) {
101
+				//模拟接口请求失败
102
+				reject(e);
103
+			}
104
+		},1000)
105
+	})
106
+}
107
+
108
+// 获取微博列表
109
+export function apiWeiboList(pageNum, pageSize) {
110
+	return new Promise((resolute, reject)=>{
111
+		//延时2秒,模拟联网
112
+		setTimeout(function() {
113
+			try {
114
+				let list = [];
115
+				if(!pageNum){
116
+					//此处模拟下拉刷新返回的数据
117
+					let id=new Date().getTime();
118
+					let newObj={id:id, title:"【新增微博"+id+"】 新增微博", content:"新增微博的内容,新增微博的内容"};
119
+					list.push(newObj);
120
+				}else{
121
+					//此处模拟上拉加载返回的数据
122
+					for (let i = 0; i < pageSize; i++) {
123
+						let upIndex=(pageNum-1)*pageSize+i+1;
124
+						let newObj={id:upIndex, title:"【微博"+upIndex+"】 标题标题标题标题标题标题", content:"内容内容内容内容内容内容内容内容内容内容"};
125
+						list.push(newObj);
126
+					}
127
+					console.log("page.num=" + pageNum + ", page.size=" + pageSize + ", curPageData.length=" + list.length);
128
+				}
129
+				//模拟接口请求成功
130
+				resolute(list);
131
+			} catch (e) {
132
+				//模拟接口请求失败
133
+				reject(e);
134
+			}
135
+		}, 2000)
136
+	})
137
+}
138
+
139
+
140
+// 获取消息列表(共5页消息)
141
+export function apiMsgList(pageNum, pageSize) {
142
+	return new Promise((resolute, reject)=>{
143
+		//延时一秒,模拟联网
144
+		setTimeout(function() {
145
+			try {
146
+				let list = [];
147
+				//模拟下拉加载更多记录
148
+				for (let i = 0; i < pageSize; i++) {
149
+					let msgId = (pageNum - 1) * pageSize + i + 1;
150
+					let newObj = {
151
+						id: msgId,
152
+						title: "【消息" + msgId + "】",
153
+						content: "内容: 下拉获取聊天记录"
154
+					};
155
+					// 此处模拟只有5页的消息 (第5页只有3条)
156
+					if(pageNum>=5 && i>=3){}else{
157
+						list.unshift(newObj);
158
+					}
159
+				}
160
+				console.log("page.num=" + pageNum + ", page.size=" + pageSize + ", curPageData.length=" + list.length);
161
+				//模拟接口请求成功
162
+				resolute(list);
163
+			} catch (e) {
164
+				//模拟接口请求失败
165
+				reject(e);
166
+			}
167
+		}, 1000)
168
+	})
169
+}

+ 67
- 0
oa-ui-app/uni_modules/mescroll/components/good-list/good-list.vue Voir le fichier

@@ -0,0 +1,67 @@
1
+<!-- 商品列表组件 <good-list :list="xx"></good-list> -->
2
+<template>
3
+	<view class="good-list">
4
+		<view :id="'good'+good.id" class="good-li" v-for="good in list" :key="good.id">
5
+			<image class="good-img" :src="good.goodImg" mode="widthFix"/>
6
+			<view class="flex-item">
7
+				<view class="good-name">{{good.goodName}}</view>
8
+				<text class="good-price">{{good.goodPrice}} 元</text>
9
+				<text class="good-sold">已售{{good.goodSold}}件</text>
10
+			</view>
11
+		</view>
12
+	</view>
13
+</template>
14
+
15
+<script>
16
+	export default {
17
+		props:{
18
+			list: {
19
+				type: Array,
20
+				default(){
21
+					return []
22
+				}
23
+			}
24
+		}
25
+	}
26
+</script>
27
+
28
+<style lang="scss">
29
+	.good-list{
30
+		background-color: #fff;
31
+		
32
+		.good-li{
33
+			display: flex;
34
+			align-items: center;
35
+			padding: 20upx;
36
+			border-bottom: 1upx solid #eee;
37
+			
38
+			.good-img{
39
+				width: 160upx;
40
+				height: 160upx;
41
+				margin-right: 20rpx;
42
+			}
43
+			
44
+			.flex-item{
45
+				flex: 1;
46
+				
47
+				.good-name{
48
+					font-size: 26upx;
49
+					line-height: 40upx;
50
+					height: 80upx;
51
+					margin-bottom: 20upx;
52
+					overflow: hidden;
53
+				}
54
+				.good-price{
55
+					font-size: 26upx;
56
+					color: red;
57
+				}
58
+				.good-sold{
59
+					font-size: 24upx;
60
+					margin-left: 16upx;
61
+					color: gray;
62
+				}
63
+				
64
+			}
65
+		}
66
+	}
67
+</style>

+ 186
- 0
oa-ui-app/uni_modules/mescroll/components/me-tabs/me-tabs.vue Voir le fichier

@@ -0,0 +1,186 @@
1
+<!-- tab组件: <me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs> -->
2
+<template>
3
+	<view class="me-tabs" :class="{'tabs-fixed': fixed}" :style="{height: tabHeightVal}">
4
+		<scroll-view v-if="tabs.length" :id="viewId" :scroll-left="scrollLeft" scroll-x scroll-with-animation :scroll-animation-duration="300">
5
+			<view class="tabs-item" :class="{'tabs-flex':!isScroll, 'tabs-scroll':isScroll}">
6
+				<!-- tab -->
7
+				<view class="tab-item" :style="{width: tabWidthVal, height: tabHeightVal, 'line-height':tabHeightVal}" v-for="(tab, i) in tabs" :class="{'active': value===i}" :key="i" @click="tabClick(i)">
8
+					{{getTabName(tab)}}
9
+				</view>
10
+				<!-- 下划线 -->
11
+				<view class="tabs-line" :style="{left:lineLeft}"></view>
12
+			</view>
13
+		</scroll-view>
14
+	</view>
15
+</template>
16
+
17
+<script>
18
+	export default {
19
+		props:{
20
+			tabs: { // 支持格式: ['全部', '待付款'] 或 [{name:'全部'}, {name:'待付款'}]
21
+				type: Array,
22
+				default(){
23
+					return []
24
+				}
25
+			},
26
+			nameKey: { // 取name的字段
27
+				type: String,
28
+				default: 'name'
29
+			},
30
+			value: { // 当前显示的下标 (使用v-model语法糖: 1.props需为value; 2.需回调input事件)
31
+				type: [String, Number],
32
+				default: 0
33
+			},
34
+			fixed: Boolean, // 是否悬浮,默认false
35
+			tabWidth: Number, // 每个tab的宽度,默认不设置值,为flex平均分配; 如果指定宽度,则不使用flex,每个tab居左,超过则水平滑动(单位默认rpx)
36
+			height: { // 高度,单位rpx
37
+				type: Number,
38
+				default: 64
39
+			}
40
+		},
41
+		data() {
42
+			return {
43
+				viewId: 'id_' + Math.random().toString(36).substr(2,16),
44
+				scrollLeft: 0
45
+			}
46
+		},
47
+		computed: {
48
+			isScroll(){
49
+				return this.tabWidth && this.tabs.length // 指定了tabWidth的宽度,则支持水平滑动
50
+			},
51
+			tabHeightPx(){
52
+				return uni.upx2px(this.height)
53
+			},
54
+			tabHeightVal(){
55
+				return this.tabHeightPx+'px'
56
+			},
57
+			tabWidthPx(){
58
+				return uni.upx2px(this.tabWidth)
59
+			},
60
+			tabWidthVal(){
61
+				return this.isScroll ? this.tabWidthPx+'px' : ''
62
+			},
63
+			lineLeft() {
64
+				if (this.isScroll) {
65
+					return this.tabWidthPx * this.value + this.tabWidthPx/2 + 'px' // 需转为px (用rpx的话iOS真机显示有误差)
66
+				} else{
67
+					return 100/this.tabs.length*(this.value + 1) - 100/(this.tabs.length*2) + '%'
68
+				}
69
+			}
70
+		},
71
+		watch: {
72
+			tabs() {
73
+				this.warpWidth = null; // 重新计算容器宽度
74
+				this.scrollCenter(); // 水平滚动到中间
75
+			},
76
+			value() {
77
+				this.scrollCenter(); // 水平滚动到中间
78
+			}
79
+		},
80
+		methods: {
81
+			getTabName(tab){
82
+				return typeof tab === "object" ? tab[this.nameKey] : tab
83
+			},
84
+			tabClick(i){
85
+				if(this.value!=i){
86
+					this.$emit("input",i);
87
+					this.$emit("change",i);
88
+				}
89
+			},
90
+			async scrollCenter(){
91
+				if(!this.isScroll) return;
92
+				if(!this.warpWidth){ // tabs容器的宽度
93
+					let rect = await this.initWarpRect()
94
+					this.warpWidth = rect ? rect.width : uni.getSystemInfoSync().windowWidth; // 某些情况下取不到宽度,暂时取屏幕宽度
95
+				}
96
+				let tabLeft = this.tabWidthPx * this.value + this.tabWidthPx/2; // 当前tab中心点到左边的距离
97
+				let diff = tabLeft - this.warpWidth/2 // 如果超过tabs容器的一半,则滚动差值
98
+				this.scrollLeft = diff;
99
+				// #ifdef MP-TOUTIAO
100
+				this.scrollTimer && clearTimeout(this.scrollTimer)
101
+				this.scrollTimer = setTimeout(()=>{ // 字节跳动小程序,需延时再次设置scrollLeft,否则tab切换跨度较大时不生效
102
+					this.scrollLeft = Math.ceil(diff)
103
+				},400)
104
+				// #endif
105
+			},
106
+			initWarpRect(){
107
+				return new Promise(resolve=>{
108
+					setTimeout(()=>{ // 延时确保dom已渲染, 不使用$nextclick
109
+						let query = uni.createSelectorQuery();
110
+						// #ifndef MP-ALIPAY
111
+						query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
112
+						// #endif
113
+						query.select('#'+this.viewId).boundingClientRect(data => {
114
+							resolve(data)
115
+						}).exec();
116
+					},20)
117
+				})
118
+			}
119
+		},
120
+		mounted() {
121
+			this.scrollCenter() // 滚动到当前下标
122
+		}
123
+	}
124
+</script>
125
+
126
+<style lang="scss">
127
+	.me-tabs{
128
+		position: relative;
129
+		font-size: 24rpx;
130
+		background-color: #fff;
131
+		border-bottom: 1rpx solid #eee;
132
+		box-sizing: border-box;
133
+		overflow-y: hidden;
134
+		background-color: #fff;
135
+		&.tabs-fixed{
136
+			z-index: 990;
137
+			position: fixed;
138
+			top: var(--window-top);
139
+			left: 0;
140
+			width: 100%;
141
+		}
142
+		
143
+		.tabs-item{
144
+			position: relative;
145
+			white-space: nowrap;
146
+			padding-bottom: 30rpx; // 撑开高度,再配合me-tabs的overflow-y: hidden,以达到隐藏滚动条的目的
147
+			box-sizing: border-box;
148
+			.tab-item{
149
+				position: relative;
150
+				text-align: center;
151
+				box-sizing: border-box;
152
+				&.active{
153
+					font-weight: bold;
154
+					color: red;
155
+				}
156
+			}
157
+		}
158
+		
159
+		// 平分的方式显示item
160
+		.tabs-flex{
161
+			display: flex;
162
+			.tab-item{
163
+				flex: 1;
164
+			}
165
+		}
166
+		// 居左显示item,支持水平滑动
167
+		.tabs-scroll{
168
+			.tab-item{
169
+				display: inline-block;
170
+			}
171
+		}
172
+		
173
+		// 选中tab的线
174
+		.tabs-line{
175
+			z-index: 1;
176
+			position: absolute;
177
+			bottom: 30rpx; // 至少与.tabs-item的padding-bottom一致,才能保证在底部边缘
178
+			width: 50rpx;
179
+			height: 6rpx;
180
+			transform: translateX(-50%);
181
+			border-radius: 4rpx;
182
+			transition: left .3s;
183
+			background: red;
184
+		}
185
+	}
186
+</style>

+ 179
- 0
oa-ui-app/uni_modules/mescroll/components/me-video/me-video.vue Voir le fichier

@@ -0,0 +1,179 @@
1
+<!-- 视频组件: <me-video src="视频地址" poster="封面图"></me-video> 
2
+video标签在APP端是原生组件, 真机APP端下拉时会渲染不及时, 出现悬浮错位现象;
3
+me-video组件, 未播放时自动展示image封面, 播放时才显示video, 提高性能; 如果播放中执行下拉,会自动显示封面, 避免视频下拉悬浮错位;
4
+-->
5
+<template>
6
+	<view class="me-video" :style="{width:width, height:height}">
7
+		<!-- 播放的时候才渲染video标签 -->
8
+		<video v-if="showVideo" ref="videoRef" class="video" :class="{'full-play': fullplay&&!autoplay, 'mescroll-dowload': mescrollDownLoad}" :src="src" autoplay :loop="loop" @click="videoClick" x5-playsinline="true" x5-video-player-type="h5" playsinline="true" webkit-playsinline="true" x5-video-player-fullscreen="false"></video>
9
+		<!-- 播放按钮 -->
10
+		<view v-else class="btn-play"> <view class="triangle"></view> </view>
11
+		<!-- 封面 -->
12
+		<image v-if="(!showVideo || mescrollDownLoad) && poster" class="poster" :src="poster" @click="play()" mode="aspectFit"></image>
13
+	</view>
14
+</template>
15
+
16
+<script>
17
+	export default {
18
+		props: {
19
+			src: String, // 视频地址
20
+			poster: String, // 封面图
21
+			autoplay: { // 是否自动播放
22
+				type: Boolean,
23
+				default(){
24
+					return false
25
+				}
26
+			},
27
+			fullplay: { // 是否全屏播放,默认不全屏
28
+				type: Boolean,
29
+				default(){
30
+					return false
31
+				}
32
+			},
33
+			loop: { // 是否循环播放
34
+				type: Boolean,
35
+				default(){
36
+					return true // 循环播放可避免Android微信播放完毕显示广告
37
+				}
38
+			},
39
+			width: { // 宽度 (需带单位,支持格式: '100%', '300px', '300rpx')
40
+				type: String,
41
+				default: "100%"
42
+			},
43
+			height: { // 高度 (需带单位,支持格式: '100%', '300px', '300rpx')
44
+				type: String,
45
+				default: "225px"
46
+			},
47
+			mescroll: { // mescroll对象,APP端下拉刷新时显示封面,隐藏视频.缓解APP端视频下拉悬浮错位问题
48
+				type: Object,
49
+				default(){
50
+					return {}
51
+				}
52
+			}
53
+		},
54
+		data() {
55
+			return {
56
+				showVideo: this.autoplay // 是否播放视频
57
+			}
58
+		},
59
+		computed: {
60
+			// 是否下拉中 (下拉隐藏视频,显示封面, 仅APP端生效)
61
+			mescrollDownLoad() {
62
+				// #ifdef APP-PLUS
63
+				return this.mescroll.downLoadType
64
+				// #endif
65
+				// #ifndef APP-PLUS
66
+				return false
67
+				// #endif
68
+			}
69
+		},
70
+		watch: {
71
+			autoplay(val) {
72
+				if(val) this.play()
73
+			}
74
+		},
75
+		methods: {
76
+			// 播放
77
+			play(){
78
+				this.showVideo = true
79
+				this.wxAutoPlay()
80
+			},
81
+			// 视频点击事件
82
+			videoClick(){
83
+				// 全屏播放时,点击视频退出
84
+				if(this.fullplay) this.showVideo = false
85
+			},
86
+			// 解决微信端视频无法自动播放的问题
87
+			wxAutoPlay(){
88
+				// #ifdef H5
89
+				// 微信端
90
+				if(navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){
91
+					// iOS
92
+					let head = document.getElementsByTagName("head")[0]
93
+					let wxscript = document.createElement("script");
94
+					wxscript.type = "text/javascript"
95
+					wxscript.src = "https://res.wx.qq.com/open/js/jweixin-1.6.0.js"
96
+					head.appendChild(wxscript)
97
+					let vm = this
98
+					let doPlay = function(){
99
+						vm.$refs.videoRef && vm.$refs.videoRef.play()
100
+					}
101
+					wxscript.onload = function(){
102
+						window.wx.config({
103
+							debug: !1,
104
+							appId: "",
105
+							timestamp: 1,
106
+							nonceStr: "",
107
+							signature: "",
108
+							jsApiList: []
109
+						})
110
+						window.wx.ready(doPlay)
111
+					}
112
+					// Android
113
+					document.addEventListener("WeixinJSBridgeReady", doPlay, false);
114
+					// 先尝试播放
115
+					setTimeout(()=>{
116
+						doPlay()
117
+					},20)
118
+				}
119
+				// #endif
120
+			}
121
+		}
122
+	}
123
+</script>
124
+
125
+<style lang="scss">
126
+	.me-video{
127
+		position: relative;
128
+		background-color: #000;
129
+		overflow: hidden;
130
+		// 播放按钮
131
+		.btn-play{
132
+			z-index: 9;
133
+			position: absolute;
134
+			left: 50%;
135
+			top: 50%;
136
+			transform: translate(-50%, -50%);
137
+			width: 100rpx;
138
+			height: 100rpx;
139
+			border-radius: 50%;
140
+			background-color: rgba(0,0,0,.75);
141
+			pointer-events: none;
142
+			.triangle{
143
+				position: absolute;
144
+				left: 50%;
145
+				top: 50%;
146
+				transform: translate(-25%, -50%);
147
+				width: 0;
148
+				height: 0;
149
+				border-top: 16rpx solid transparent;
150
+				border-left: 24rpx solid #fff;
151
+				border-bottom: 16rpx solid transparent;
152
+			}
153
+		}
154
+		// 封面图
155
+		.poster{
156
+			width: 100%;
157
+			height: 100%;
158
+			vertical-align: bottom;
159
+		}
160
+		// 视频 (默认非全屏播放)
161
+		.video{
162
+			z-index: 8;
163
+			position: absolute;
164
+			top: 0;
165
+			left: 0;
166
+			width: 100%;
167
+			height: 100%;
168
+			// 全屏播放
169
+			&.full-play{
170
+				z-index: 999;
171
+				position: fixed;
172
+			}
173
+			// 下拉时隐藏视频
174
+			&.mescroll-dowload{
175
+				display: none;
176
+			}
177
+		}
178
+	}
179
+</style>

+ 47
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/components/mescroll-down.css Voir le fichier

@@ -0,0 +1,47 @@
1
+/*下拉刷新--标语*/
2
+.mescroll-downwarp .downwarp-slogan{
3
+	display: block;
4
+	width: 420rpx;
5
+	height: 168rpx;
6
+	margin: auto;
7
+}
8
+/*下拉刷新--向下进度动画*/
9
+.mescroll-downwarp .downwarp-progress{
10
+	display: inline-block;
11
+	width: 40rpx;
12
+	height: 40rpx;
13
+	border: none;
14
+	margin: auto;
15
+	background-size: contain;
16
+	background-repeat: no-repeat;
17
+	background-position: center;
18
+	background-image: url(https://www.mescroll.com/img/beibei/mescroll-progress.png);
19
+	transition: all 300ms;
20
+}
21
+/*下拉刷新--进度条*/
22
+.mescroll-downwarp .downwarp-loading{
23
+	display: inline-block;
24
+	width: 32rpx;
25
+	height: 32rpx;
26
+	border-radius: 50%;
27
+	border: 2rpx solid #FF8095;
28
+	border-bottom-color: transparent;
29
+}
30
+/*下拉刷新--吉祥物*/
31
+.mescroll-downwarp .downwarp-mascot{
32
+	position: absolute;
33
+	right: 16rpx;
34
+	bottom: 0;
35
+	width: 100rpx;
36
+	height: 100rpx;
37
+	background-size: contain;
38
+	background-repeat: no-repeat;
39
+	animation: animMascot .6s steps(1,end) infinite;
40
+}
41
+@keyframes animMascot {
42
+	0% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb1.png)}
43
+	25% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb2.png)}
44
+	50% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb3.png)}
45
+	75% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb4.png)}
46
+	100% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb1.png)}
47
+}

+ 39
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/components/mescroll-down.vue Voir le fichier

@@ -0,0 +1,39 @@
1
+<!-- 下拉刷新区域 -->
2
+<template>
3
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
4
+		<view class="downwarp-content">
5
+			<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
6
+			<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
7
+			<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
8
+			<view class="downwarp-mascot"></view>
9
+		</view>
10
+	</view>
11
+</template>
12
+
13
+<script>
14
+export default {
15
+	props: {
16
+		option: Object , // down的配置项
17
+		type: Number // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
18
+	},
19
+	computed: {
20
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
21
+		mOption(){
22
+			return this.option || {}
23
+		},
24
+		// 是否在加载中
25
+		isDownLoading(){
26
+			return this.type === 3
27
+		},
28
+		// 旋转的角度
29
+		downRotate(){
30
+			return this.type === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
31
+		}
32
+	}
33
+};
34
+</script>
35
+
36
+<style>
37
+@import "../../../mescroll-uni/components/mescroll-down.css";
38
+@import "./mescroll-down.css";
39
+</style>

+ 330
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/mescroll-body.vue Voir le fichier

@@ -0,0 +1,330 @@
1
+<template>
2
+	<view 
3
+		class="mescroll-body mescroll-render-touch" 
4
+		:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
5
+		:class="{'mescorll-sticky': sticky}"
6
+		@touchstart="wxsBiz.touchstartEvent" 
7
+		@touchmove="wxsBiz.touchmoveEvent" 
8
+		@touchend="wxsBiz.touchendEvent" 
9
+		@touchcancel="wxsBiz.touchendEvent"
10
+		:change:prop="wxsBiz.propObserver"
11
+		:prop="wxsProp"
12
+		>
13
+		
14
+		<!-- 状态栏 -->
15
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
16
+
17
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
18
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
19
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
20
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
21
+				<view class="downwarp-content">
22
+					<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
23
+					<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
24
+					<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
25
+					<view class="downwarp-mascot"></view>
26
+				</view>
27
+			</view>
28
+						
29
+			<!-- 列表内容 -->
30
+			<slot></slot>
31
+
32
+			<!-- 空布局 -->
33
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
34
+
35
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
36
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
37
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
38
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
39
+				<view v-show="upLoadType===1">
40
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
41
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
42
+				</view>
43
+				<!-- 无数据 -->
44
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
45
+			</view>
46
+		</view>
47
+		
48
+		<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
49
+		<!-- #ifdef H5 -->
50
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
51
+		<!-- #endif -->
52
+		
53
+		<!-- 适配iPhoneX -->
54
+		<view v-if="safearea" class="mescroll-safearea"></view>
55
+		
56
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
57
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
58
+
59
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
60
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
61
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
62
+		<!-- #endif -->
63
+	</view>
64
+</template>
65
+
66
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
67
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
68
+<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
69
+<!-- #endif -->
70
+
71
+<!-- app, h5使用renderjs -->
72
+<!-- #ifdef APP-PLUS || H5 -->
73
+<script module="renderBiz" lang="renderjs">
74
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
75
+	export default {
76
+		mixins: [renderBiz]
77
+	}
78
+</script>
79
+<!-- #endif -->
80
+
81
+<script>
82
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
83
+	import MescrollEmpty from '../../mescroll-uni/components/mescroll-empty.vue';
84
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
85
+	import GlobalOption from './mescroll-uni-option.js';
86
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
87
+	
88
+	export default {
89
+		mixins: [WxsMixin],
90
+		components: {
91
+			MescrollEmpty,
92
+			MescrollTop
93
+		},
94
+		data() {
95
+			return {
96
+				mescroll: null, // mescroll实例
97
+				downHight: 0, //下拉刷新: 容器高度
98
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
99
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
100
+				isShowEmpty: false, // 是否显示空布局
101
+				isShowToTop: false, // 是否显示回到顶部按钮
102
+				windowHeight: 0, // 可使用窗口的高度
103
+				windowBottom: 0, // 可使用窗口的底部位置
104
+				statusBarHeight: 0 // 状态栏高度
105
+			};
106
+		},
107
+		props: {
108
+			down: Object, // 下拉刷新的参数配置
109
+			up: Object, // 上拉加载的参数配置
110
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
111
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
112
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
113
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
114
+			height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
115
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
116
+				type: Boolean,
117
+				default: true
118
+			},
119
+			sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
120
+		},
121
+		computed: {
122
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
123
+			minHeight(){
124
+				return this.toPx(this.height || '100%') + 'px'
125
+			},
126
+			// 下拉布局往下偏移的距离 (px)
127
+			numTop() {
128
+				return this.toPx(this.top)
129
+			},
130
+			padTop() {
131
+				return this.numTop + 'px';
132
+			},
133
+			// 上拉布局往上偏移 (px)
134
+			numBottom() {
135
+				return this.toPx(this.bottom);
136
+			},
137
+			padBottom() {
138
+				return this.numBottom + 'px';
139
+			},
140
+			// 是否为重置下拉的状态
141
+			isDownReset() {
142
+				return this.downLoadType === 3 || this.downLoadType === 4;
143
+			},
144
+			// 过渡
145
+			transition() {
146
+				return this.isDownReset ? 'transform 300ms' : '';
147
+			},
148
+			translateY() {
149
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
150
+			},
151
+			// 是否在加载中
152
+			isDownLoading(){
153
+				return this.downLoadType === 3
154
+			},
155
+			// 旋转的角度
156
+			downRotate(){
157
+				return this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
158
+			}
159
+		},
160
+		methods: {
161
+			//number,rpx,upx,px,% --> px的数值
162
+			toPx(num) {
163
+				if (typeof num === 'string') {
164
+					if (num.indexOf('px') !== -1) {
165
+						if (num.indexOf('rpx') !== -1) {
166
+							// "10rpx"
167
+							num = num.replace('rpx', '');
168
+						} else if (num.indexOf('upx') !== -1) {
169
+							// "10upx"
170
+							num = num.replace('upx', '');
171
+						} else {
172
+							// "10px"
173
+							return Number(num.replace('px', ''));
174
+						}
175
+					} else if (num.indexOf('%') !== -1) {
176
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
177
+						let rate = Number(num.replace('%', '')) / 100;
178
+						return this.windowHeight * rate;
179
+					}
180
+				}
181
+				return num ? uni.upx2px(Number(num)) : 0;
182
+			},
183
+			// 点击空布局的按钮回调
184
+			emptyClick() {
185
+				this.$emit('emptyclick', this.mescroll);
186
+			},
187
+			// 点击回到顶部的按钮回调
188
+			toTopClick() {
189
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
190
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
191
+			}
192
+		},
193
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
194
+		created() {
195
+			let vm = this;
196
+
197
+			let diyOption = {
198
+				// 下拉刷新的配置
199
+				down: {
200
+					inOffset() {
201
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
202
+					},
203
+					outOffset() {
204
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
205
+					},
206
+					onMoving(mescroll, rate, downHight) {
207
+						// 下拉过程中的回调,滑动过程一直在执行;
208
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
209
+					},
210
+					showLoading(mescroll, downHight) {
211
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
212
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
213
+					},
214
+					endDownScroll() {
215
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
216
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
217
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
218
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
219
+							if(vm.downLoadType === 4) vm.downLoadType = 0
220
+						},300)
221
+					},
222
+					// 派发下拉刷新的回调
223
+					callback: function(mescroll) {
224
+						vm.$emit('down', mescroll);
225
+					}
226
+				},
227
+				// 上拉加载的配置
228
+				up: {
229
+					// 显示加载中的回调
230
+					showLoading() {
231
+						vm.upLoadType = 1;
232
+					},
233
+					// 显示无更多数据的回调
234
+					showNoMore() {
235
+						vm.upLoadType = 2;
236
+					},
237
+					// 隐藏上拉加载的回调
238
+					hideUpScroll(mescroll) {
239
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
240
+					},
241
+					// 空布局
242
+					empty: {
243
+						onShow(isShow) {
244
+							// 显示隐藏的回调
245
+							vm.isShowEmpty = isShow;
246
+						}
247
+					},
248
+					// 回到顶部
249
+					toTop: {
250
+						onShow(isShow) {
251
+							// 显示隐藏的回调
252
+							vm.isShowToTop = isShow;
253
+						}
254
+					},
255
+					// 派发上拉加载的回调
256
+					callback: function(mescroll) {
257
+						vm.$emit('up', mescroll);
258
+					}
259
+				}
260
+			};
261
+
262
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
263
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
264
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
265
+
266
+			// 初始化MeScroll对象
267
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
268
+			// init回调mescroll对象
269
+			vm.$emit('init', vm.mescroll);
270
+
271
+			// 设置高度
272
+			const sys = uni.getSystemInfoSync();
273
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
274
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
275
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
276
+			// 使down的bottomOffset生效
277
+			vm.mescroll.setBodyHeight(sys.windowHeight);
278
+
279
+			// 因为使用的是page的scroll,这里需自定义scrollTo
280
+			vm.mescroll.resetScrollTo((y, t) => {
281
+				if(typeof y === 'string'){
282
+					// 滚动到指定view (y为css选择器)
283
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
284
+						let selector;
285
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
286
+							selector = '#'+y // 不带#和. 则默认为id选择器
287
+						}else{
288
+							selector = y
289
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
290
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
291
+								selector = y.split('>>>')[1].trim()
292
+							}
293
+							// #endif
294
+						}
295
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
296
+							if (rect) {
297
+								let top = rect.top
298
+								top += vm.mescroll.getScrollTop()
299
+								uni.pageScrollTo({
300
+									scrollTop: top,
301
+									duration: t
302
+								})
303
+							} else{
304
+								console.error(selector + ' does not exist');
305
+							}
306
+						}).exec()
307
+					},30)
308
+				} else{
309
+					// 滚动到指定位置 (y必须为数字)
310
+					uni.pageScrollTo({
311
+						scrollTop: y,
312
+						duration: t
313
+					})
314
+				}
315
+			});
316
+
317
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
318
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
319
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
320
+			}
321
+		}
322
+	};
323
+</script>
324
+
325
+<style>
326
+	@import "../../mescroll-uni/mescroll-body.css";
327
+	@import "../../mescroll-uni/components/mescroll-down.css";
328
+	@import "../../mescroll-uni/components/mescroll-up.css";
329
+	@import "./components/mescroll-down.css";
330
+</style>

+ 29
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/mescroll-uni-option.js Voir le fichier

@@ -0,0 +1,29 @@
1
+// mescroll-uni和mescroll-body 的全局配置
2
+const GlobalOption = {
3
+	down: {
4
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
5
+		offset: uni.upx2px(140), // 在列表顶部,下拉大于140upx,松手即可触发下拉刷新的回调
6
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
7
+	},
8
+	up: {
9
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
10
+		textLoading: '加载中 ...', // 加载中的提示文本
11
+		textNoMore: '-- END --', // 没有更多数据的提示文本
12
+		offset: 150, // 距底部多远时,触发upCallback
13
+		toTop: {
14
+			// 回到顶部按钮,需配置src才显示
15
+			src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
16
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
17
+			right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
18
+			bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
19
+			width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
20
+		},
21
+		empty: {
22
+			use: true, // 是否显示空布局
23
+			icon: "https://www.mescroll.com/img/mescroll-empty.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
24
+			tip: '~ 暂无相关数据 ~' // 提示
25
+		}
26
+	}
27
+}
28
+
29
+export default GlobalOption

+ 406
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/beibei/mescroll-uni.vue Voir le fichier

@@ -0,0 +1,406 @@
1
+<template>
2
+	<view class="mescroll-uni-warp">
3
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
4
+			<view class="mescroll-uni-content mescroll-render-touch"
5
+			@touchstart="wxsBiz.touchstartEvent" 
6
+			@touchmove="wxsBiz.touchmoveEvent" 
7
+			@touchend="wxsBiz.touchendEvent" 
8
+			@touchcancel="wxsBiz.touchendEvent"
9
+			:change:prop="wxsBiz.propObserver"
10
+			:prop="wxsProp">
11
+						
12
+				<!-- 状态栏 -->
13
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
14
+							
15
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
16
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
17
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
18
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
19
+						<view class="downwarp-content">
20
+							<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
21
+							<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
22
+							<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
23
+							<view class="downwarp-mascot"></view>
24
+						</view>
25
+					</view>
26
+
27
+					<!-- 列表内容 -->
28
+					<slot></slot>
29
+
30
+					<!-- 空布局 -->
31
+					<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
32
+
33
+					<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
34
+					<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
35
+					<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
36
+						<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
37
+						<view v-show="upLoadType===1">
38
+							<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
39
+							<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
40
+						</view>
41
+						<!-- 无数据 -->
42
+						<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
43
+					</view>
44
+				</view>
45
+				
46
+				<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
47
+				<!-- #ifdef H5 -->
48
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
49
+				<!-- #endif -->
50
+				
51
+				<!-- 适配iPhoneX -->
52
+				<view v-if="safearea" class="mescroll-safearea"></view>
53
+			</view>
54
+		</scroll-view>
55
+
56
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
57
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
58
+
59
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
60
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
61
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
62
+		<!-- #endif -->
63
+	</view>
64
+</template>
65
+
66
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
67
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
68
+<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
69
+<!-- #endif -->
70
+
71
+<!-- app, h5使用renderjs -->
72
+<!-- #ifdef APP-PLUS || H5 -->
73
+<script module="renderBiz" lang="renderjs">
74
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
75
+	export default {
76
+		mixins: [renderBiz]
77
+	}
78
+</script>
79
+<!-- #endif -->
80
+
81
+<script>
82
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
83
+	import MescrollEmpty from '../../mescroll-uni/components/mescroll-empty.vue';
84
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
85
+	import GlobalOption from './mescroll-uni-option.js';
86
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
87
+	
88
+	export default {
89
+		mixins: [WxsMixin],
90
+		components: {
91
+			MescrollEmpty,
92
+			MescrollTop
93
+		},
94
+		data() {
95
+			return {
96
+				mescroll: null, // mescroll实例
97
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
98
+				downHight: 0, //下拉刷新: 容器高度
99
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
100
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
101
+				isShowEmpty: false, // 是否显示空布局
102
+				isShowToTop: false, // 是否显示回到顶部按钮
103
+				scrollTop: 0, // 滚动条的位置
104
+				scrollAnim: false, // 是否开启滚动动画
105
+				windowTop: 0, // 可使用窗口的顶部位置
106
+				windowBottom: 0, // 可使用窗口的底部位置
107
+				windowHeight: 0, // 可使用窗口的高度
108
+				statusBarHeight: 0 // 状态栏高度
109
+			}
110
+		},
111
+		props: {
112
+			down: Object, // 下拉刷新的参数配置
113
+			up: Object, // 上拉加载的参数配置
114
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
115
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
116
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
117
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
118
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
119
+				type: Boolean,
120
+				default: true
121
+			},
122
+			height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
123
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
124
+				type: Boolean,
125
+				default: true
126
+			}
127
+		},
128
+		computed: {
129
+			// 是否使用fixed定位 (当height有值,则不使用)
130
+			isFixed(){
131
+				return !this.height && this.fixed
132
+			},
133
+			// mescroll的高度
134
+			scrollHeight(){
135
+				if (this.isFixed) {
136
+					return "auto"
137
+				} else if(this.height){
138
+					return this.toPx(this.height) + 'px'
139
+				}else{
140
+					return "100%"
141
+				}
142
+			},
143
+			// 下拉布局往下偏移的距离 (px)
144
+			numTop() {
145
+				return this.toPx(this.top)
146
+			},
147
+			fixedTop() {
148
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
149
+			},
150
+			padTop() {
151
+				return !this.isFixed ? this.numTop + 'px' : 0
152
+			},
153
+			// 上拉布局往上偏移 (px)
154
+			numBottom() {
155
+				return this.toPx(this.bottom)
156
+			},
157
+			fixedBottom() {
158
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
159
+			},
160
+			padBottom() {
161
+				return !this.isFixed ? this.numBottom + 'px' : 0
162
+			},
163
+			// 是否为重置下拉的状态
164
+			isDownReset(){
165
+				return this.downLoadType===3 || this.downLoadType===4
166
+			},
167
+			// 过渡
168
+			transition() {
169
+				return this.isDownReset ? 'transform 300ms' : ''
170
+			},
171
+			translateY() {
172
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '' // transform会使fixed失效,需注意把fixed元素写在mescroll之外
173
+			},
174
+			// 列表是否可滑动
175
+			scrollable(){
176
+				return this.downLoadType===0 || this.isDownReset
177
+			},
178
+			// 是否在加载中
179
+			isDownLoading(){
180
+				return this.downLoadType === 3
181
+			},
182
+			// 旋转的角度
183
+			downRotate(){
184
+				return this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
185
+			}
186
+		},
187
+		methods: {
188
+			//number,rpx,upx,px,% --> px的数值
189
+			toPx(num){
190
+				if(typeof num === "string"){
191
+					if (num.indexOf('px') !== -1) {
192
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
193
+							num = num.replace('rpx', '');
194
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
195
+							num = num.replace('upx', '');
196
+						} else { // "10px"
197
+							return Number(num.replace('px', ''))
198
+						}
199
+					}else if (num.indexOf('%') !== -1){
200
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
201
+						let rate = Number(num.replace("%","")) / 100
202
+						return this.windowHeight * rate
203
+					}
204
+				}
205
+				return num ? uni.upx2px(Number(num)) : 0
206
+			},
207
+			//注册列表滚动事件,用于下拉刷新和上拉加载
208
+			scroll(e) {
209
+				this.mescroll.scroll(e.detail, () => {
210
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
211
+				})
212
+			},
213
+			// 点击空布局的按钮回调
214
+			emptyClick() {
215
+				this.$emit('emptyclick', this.mescroll)
216
+			},
217
+			// 点击回到顶部的按钮回调
218
+			toTopClick() {
219
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
220
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
221
+			},
222
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
223
+			setClientHeight() {
224
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
225
+					this.isExec = true; // 避免多次获取
226
+					this.$nextTick(() => { // 确保dom已渲染
227
+						this.getClientInfo(data=>{
228
+							this.isExec = false;
229
+							if (data) {
230
+								this.mescroll.setClientHeight(data.height);
231
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
232
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
233
+								setTimeout(() => {
234
+									this.setClientHeight()
235
+								}, this.clientNum * 100)
236
+							}
237
+						})
238
+					})
239
+				}
240
+			},
241
+			// 获取滚动区域的信息
242
+			getClientInfo(success){
243
+				let query = uni.createSelectorQuery();
244
+				// #ifndef MP-ALIPAY || MP-DINGTALK
245
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
246
+				// #endif
247
+				let view = query.select('#' + this.viewId);
248
+				view.boundingClientRect(data => {
249
+					success(data)
250
+				}).exec();
251
+			}
252
+		},
253
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
254
+		created() {
255
+			let vm = this;
256
+
257
+			let diyOption = {
258
+				// 下拉刷新的配置
259
+				down: {
260
+					inOffset() {
261
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
262
+					},
263
+					outOffset() {
264
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
265
+					},
266
+					onMoving(mescroll, rate, downHight) {
267
+						// 下拉过程中的回调,滑动过程一直在执行;
268
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
269
+					},
270
+					showLoading(mescroll, downHight) {
271
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
272
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
273
+					},
274
+					endDownScroll() {
275
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
276
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
277
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
278
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
279
+							if(vm.downLoadType===4) vm.downLoadType = 0
280
+						},300)
281
+					},
282
+					// 派发下拉刷新的回调
283
+					callback: function(mescroll) {
284
+						vm.$emit('down', mescroll)
285
+					}
286
+				},
287
+				// 上拉加载的配置
288
+				up: {
289
+					// 显示加载中的回调
290
+					showLoading() {
291
+						vm.upLoadType = 1;
292
+					},
293
+					// 显示无更多数据的回调
294
+					showNoMore() {
295
+						vm.upLoadType = 2;
296
+					},
297
+					// 隐藏上拉加载的回调
298
+					hideUpScroll(mescroll) {
299
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
300
+					},
301
+					// 空布局
302
+					empty: {
303
+						onShow(isShow) { // 显示隐藏的回调
304
+							vm.isShowEmpty = isShow;
305
+						}
306
+					},
307
+					// 回到顶部
308
+					toTop: {
309
+						onShow(isShow) { // 显示隐藏的回调
310
+							vm.isShowToTop = isShow;
311
+						}
312
+					},
313
+					// 派发上拉加载的回调
314
+					callback: function(mescroll) {
315
+						vm.$emit('up', mescroll);
316
+						// 更新容器的高度 (多mescroll的情况)
317
+						vm.setClientHeight()
318
+					}
319
+				}
320
+			}
321
+
322
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
323
+			let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
324
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
325
+
326
+			// 初始化MeScroll对象
327
+			vm.mescroll = new MeScroll(myOption);
328
+			vm.mescroll.viewId = vm.viewId; // 附带id
329
+			// init回调mescroll对象
330
+			vm.$emit('init', vm.mescroll);
331
+			
332
+			// 设置高度
333
+			const sys = uni.getSystemInfoSync();
334
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
335
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
336
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
337
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
338
+			// 使down的bottomOffset生效
339
+			vm.mescroll.setBodyHeight(sys.windowHeight);
340
+
341
+			// 因为使用的是scrollview,这里需自定义scrollTo
342
+			vm.mescroll.resetScrollTo((y, t) => {
343
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
344
+				if(typeof y === 'string'){
345
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
346
+					vm.getClientInfo(function(rect){
347
+						let mescrollTop = rect.top // mescroll到顶部的距离
348
+						let selector;
349
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
350
+							selector = '#'+y // 不带#和. 则默认为id选择器
351
+						}else{
352
+							selector = y
353
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
354
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
355
+								selector = y.split('>>>')[1].trim()
356
+							}
357
+							// #endif
358
+						}
359
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
360
+							if (rect) {
361
+								let curY = vm.mescroll.getScrollTop()
362
+								let top = rect.top - mescrollTop
363
+								top += curY
364
+								if(!vm.isFixed) top -= vm.numTop
365
+								vm.scrollTop = curY;
366
+								vm.$nextTick(function() {
367
+									vm.scrollTop = top
368
+								})
369
+							} else{
370
+								console.error(selector + ' does not exist');
371
+							}
372
+						}).exec()
373
+					})
374
+					return;
375
+				}
376
+				let curY = vm.mescroll.getScrollTop()
377
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
378
+					vm.scrollTop = curY;
379
+					vm.$nextTick(function() {
380
+						vm.scrollTop = y
381
+					})
382
+				} else {
383
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
384
+						vm.scrollTop = step
385
+					}, t)
386
+				}
387
+			})
388
+			
389
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
390
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
391
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
392
+			}
393
+		},
394
+		mounted() {
395
+			// 设置容器的高度
396
+			this.setClientHeight()
397
+		}
398
+	}
399
+</script>
400
+
401
+<style>
402
+	@import "../../mescroll-uni/mescroll-uni.css";
403
+	@import "../../mescroll-uni/components/mescroll-down.css";
404
+	@import "../../mescroll-uni/components/mescroll-up.css";
405
+	@import "./components/mescroll-down.css";
406
+</style>

+ 44
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-down.css Voir le fichier

@@ -0,0 +1,44 @@
1
+/*下拉刷新--上下箭头*/
2
+.mescroll-downwarp .downwarp-arrow {
3
+	display: inline-block;
4
+	width: 20px;
5
+	height: 20px;
6
+	margin: 10px;
7
+	background-image: url(https://www.mescroll.com/img/xinlang/mescroll-arrow.png);
8
+	background-size: contain;
9
+	vertical-align: middle;
10
+	transition: all 300ms;
11
+}
12
+
13
+/*下拉刷新--旋转进度条*/
14
+.mescroll-downwarp .downwarp-progress{
15
+	width: 36px;
16
+	height: 36px;
17
+	border: none;
18
+	margin: auto;
19
+	background-size: contain;
20
+	animation: progressRotate 0.6s steps(6, start) infinite;
21
+}
22
+@keyframes progressRotate {
23
+	0% {
24
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
25
+	}
26
+	16% {
27
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress2.png);
28
+	}
29
+	32% {
30
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress3.png);
31
+	}
32
+	48% {
33
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress4.png);
34
+	}
35
+	64% {
36
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress5.png);
37
+	}
38
+	80% {
39
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress6.png);
40
+	}
41
+	100% {
42
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
43
+	}
44
+}

+ 53
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-down.vue Voir le fichier

@@ -0,0 +1,53 @@
1
+<!-- 下拉刷新区域 -->
2
+<template>
3
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
4
+		<view class="downwarp-content">
5
+			<view v-if="isDownLoading" class="downwarp-progress"></view>
6
+			<view v-else class="downwarp-arrow" :style="{ transform: downRotate }"></view>
7
+			<view class="downwarp-tip">{{ downText }}</view>
8
+		</view>
9
+	</view>
10
+</template>
11
+
12
+<script>
13
+export default {
14
+	props: {
15
+		option: Object, // down的配置项
16
+		type: Number // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
17
+	},
18
+	computed: {
19
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
20
+		mOption() {
21
+			return this.option || {};
22
+		},
23
+		// 是否在加载中
24
+		isDownLoading() {
25
+			return this.type === 3;
26
+		},
27
+		// 旋转的角度
28
+		downRotate() {
29
+			return this.type === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
30
+		},
31
+		// 文本提示
32
+		downText() {
33
+			switch (this.type) {
34
+				case 1:
35
+					return this.mOption.textInOffset;
36
+				case 2:
37
+					return this.mOption.textOutOffset;
38
+				case 3:
39
+					return this.mOption.textLoading;
40
+				case 4:
41
+					return this.mOption.textLoading;
42
+				default:
43
+					return this.mOption.textInOffset;
44
+			}
45
+		}
46
+	}
47
+};
48
+</script>
49
+
50
+<style>
51
+@import '../../../mescroll-uni/components/mescroll-down.css';
52
+@import './mescroll-down.css';
53
+</style>

+ 32
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-up.css Voir le fichier

@@ -0,0 +1,32 @@
1
+/*上拉加载--旋转进度条*/
2
+.mescroll-upwarp .upwarp-progress {
3
+	width: 36px;
4
+	height: 36px;
5
+	border: none;
6
+	margin: auto;
7
+	background-size: contain;
8
+	animation: progressRotate 0.6s steps(6, start) infinite;
9
+}
10
+@keyframes progressRotate {
11
+	0% {
12
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
13
+	}
14
+	16% {
15
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress2.png);
16
+	}
17
+	32% {
18
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress3.png);
19
+	}
20
+	48% {
21
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress4.png);
22
+	}
23
+	64% {
24
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress5.png);
25
+	}
26
+	80% {
27
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress6.png);
28
+	}
29
+	100% {
30
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
31
+	}
32
+}

+ 40
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/components/mescroll-up.vue Voir le fichier

@@ -0,0 +1,40 @@
1
+<!-- 上拉加载区域 -->
2
+<template>
3
+	<view class="mescroll-upwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
4
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
5
+		<view v-show="isUpLoading">
6
+			<view class="upwarp-progress mescroll-rotate"></view>
7
+			<view class="upwarp-tip">{{ mOption.textLoading }}</view>
8
+		</view>
9
+		<!-- 无数据 -->
10
+		<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
11
+	</view>
12
+</template>
13
+
14
+<script>
15
+export default {
16
+	props: {
17
+		option: Object, // up的配置项
18
+		type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
19
+	},
20
+	computed: {
21
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
22
+		mOption() {
23
+			return this.option || {};
24
+		},
25
+		// 加载中
26
+		isUpLoading() {
27
+			return this.type === 1;
28
+		},
29
+		// 没有更多了
30
+		isUpNoMore() {
31
+			return this.type === 2;
32
+		}
33
+	}
34
+};
35
+</script>
36
+
37
+<style>
38
+@import '../../../mescroll-uni/components/mescroll-up.css';
39
+@import './mescroll-up.css';
40
+</style>

+ 350
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/mescroll-body.vue Voir le fichier

@@ -0,0 +1,350 @@
1
+<template>
2
+	<view 
3
+		class="mescroll-body mescroll-render-touch" 
4
+		:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
5
+		:class="{'mescorll-sticky': sticky}"
6
+		@touchstart="wxsBiz.touchstartEvent" 
7
+		@touchmove="wxsBiz.touchmoveEvent" 
8
+		@touchend="wxsBiz.touchendEvent" 
9
+		@touchcancel="wxsBiz.touchendEvent"
10
+		:change:prop="wxsBiz.propObserver"
11
+		:prop="wxsProp"
12
+		>
13
+		
14
+		<!-- 状态栏 -->
15
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
16
+		
17
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
18
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
19
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
20
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
21
+				<view class="downwarp-content">
22
+					<view v-if="isDownLoading" class="downwarp-progress"></view>
23
+					<view v-else class="downwarp-arrow" :style="{ transform: downRotate }"></view>
24
+					<view class="downwarp-tip">{{ downText }}</view>
25
+				</view>
26
+			</view>
27
+			
28
+			<!-- 列表内容 -->
29
+			<slot></slot>
30
+
31
+			<!-- 空布局 -->
32
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
33
+
34
+			<!-- 上拉加载区域 (下拉刷新时不显示,支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
35
+			<!-- <mescroll-up v-if="mescroll.optUp.use && downLoadType !== 3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
36
+			<view class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
37
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
38
+				<view v-show="upLoadType===1">
39
+					<view class="upwarp-progress mescroll-rotate"></view>
40
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
41
+				</view>
42
+				<!-- 无数据 -->
43
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
44
+			</view>
45
+		</view>
46
+		
47
+		<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
48
+		<!-- #ifdef H5 -->
49
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
50
+		<!-- #endif -->
51
+		
52
+		<!-- 适配iPhoneX -->
53
+		<view v-if="safearea" class="mescroll-safearea"></view>
54
+		
55
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
56
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
57
+		
58
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
59
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
60
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
61
+		<!-- #endif -->
62
+	</view>
63
+</template>
64
+
65
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
66
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
67
+<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
68
+<!-- #endif -->
69
+
70
+<!-- app, h5使用renderjs -->
71
+<!-- #ifdef APP-PLUS || H5 -->
72
+<script module="renderBiz" lang="renderjs">
73
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
74
+	export default {
75
+		mixins: [renderBiz]
76
+	}
77
+</script>
78
+<!-- #endif -->
79
+
80
+<script>
81
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
82
+	import GlobalOption from './mescroll-uni-option.js';
83
+	import MescrollEmpty from '../../mescroll-uni/components/mescroll-empty.vue';
84
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
85
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
86
+
87
+	export default {
88
+		mixins: [WxsMixin],
89
+		components: {
90
+			MescrollEmpty,
91
+			MescrollTop
92
+		},
93
+		data() {
94
+			return {
95
+				mescroll: null, // mescroll实例
96
+				downHight: 0, //下拉刷新: 容器高度
97
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
98
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
99
+				isShowEmpty: false, // 是否显示空布局
100
+				isShowToTop: false, // 是否显示回到顶部按钮
101
+				windowHeight: 0, // 可使用窗口的高度
102
+				windowBottom: 0, // 可使用窗口的底部位置
103
+				statusBarHeight: 0 // 状态栏高度
104
+			};
105
+		},
106
+		props: {
107
+			down: Object, // 下拉刷新的参数配置
108
+			up: Object, // 上拉加载的参数配置
109
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
110
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
111
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
112
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
113
+			height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
114
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
115
+				type: Boolean,
116
+				default: true
117
+			},
118
+			sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
119
+		},
120
+		computed: {
121
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
122
+			minHeight(){
123
+				return this.toPx(this.height || '100%') + 'px'
124
+			},
125
+			// 下拉布局往下偏移的距离 (px)
126
+			numTop() {
127
+				return this.toPx(this.top)
128
+			},
129
+			padTop() {
130
+				return this.numTop + 'px';
131
+			},
132
+			// 上拉布局往上偏移 (px)
133
+			numBottom() {
134
+				return this.toPx(this.bottom);
135
+			},
136
+			padBottom() {
137
+				return this.numBottom + 'px';
138
+			},
139
+			// 是否为重置下拉的状态
140
+			isDownReset() {
141
+				return this.downLoadType === 3 || this.downLoadType === 4;
142
+			},
143
+			// 过渡
144
+			transition() {
145
+				return this.isDownReset ? 'transform 300ms' : '';
146
+			},
147
+			translateY() {
148
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
149
+			},
150
+			// 是否在加载中
151
+			isDownLoading() {
152
+				return this.downLoadType === 3;
153
+			},
154
+			// 旋转的角度
155
+			downRotate() {
156
+				return this.downLoadType === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
157
+			},
158
+			// 文本提示
159
+			downText() {
160
+				if(!this.mescroll) return "";
161
+				switch (this.downLoadType) {
162
+					case 1:
163
+						return this.mescroll.optDown.textInOffset;
164
+					case 2:
165
+						return this.mescroll.optDown.textOutOffset;
166
+					case 3:
167
+						return this.mescroll.optDown.textLoading;
168
+					case 4:
169
+						return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
170
+					default:
171
+						return this.mescroll.optDown.textInOffset;
172
+				}
173
+			}
174
+		},
175
+		methods: {
176
+			//number,rpx,upx,px,% --> px的数值
177
+			toPx(num) {
178
+				if (typeof num === 'string') {
179
+					if (num.indexOf('px') !== -1) {
180
+						if (num.indexOf('rpx') !== -1) {
181
+							// "10rpx"
182
+							num = num.replace('rpx', '');
183
+						} else if (num.indexOf('upx') !== -1) {
184
+							// "10upx"
185
+							num = num.replace('upx', '');
186
+						} else {
187
+							// "10px"
188
+							return Number(num.replace('px', ''));
189
+						}
190
+					} else if (num.indexOf('%') !== -1) {
191
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
192
+						let rate = Number(num.replace('%', '')) / 100;
193
+						return this.windowHeight * rate;
194
+					}
195
+				}
196
+				return num ? uni.upx2px(Number(num)) : 0;
197
+			},
198
+			// 点击空布局的按钮回调
199
+			emptyClick() {
200
+				this.$emit('emptyclick', this.mescroll);
201
+			},
202
+			// 点击回到顶部的按钮回调
203
+			toTopClick() {
204
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
205
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
206
+			}
207
+		},
208
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
209
+		created() {
210
+			let vm = this;
211
+
212
+			let diyOption = {
213
+				// 下拉刷新的配置
214
+				down: {
215
+					inOffset() {
216
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
217
+					},
218
+					outOffset() {
219
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
220
+					},
221
+					onMoving(mescroll, rate, downHight) {
222
+						// 下拉过程中的回调,滑动过程一直在执行;
223
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
224
+					},
225
+					showLoading(mescroll, downHight) {
226
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
227
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
228
+					},
229
+					beforeEndDownScroll(mescroll){
230
+						vm.downLoadType = 4; 
231
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
232
+					},
233
+					endDownScroll() {
234
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
235
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
236
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
237
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
238
+							if(vm.downLoadType === 4) vm.downLoadType = 0
239
+						},300)
240
+					},
241
+					// 派发下拉刷新的回调
242
+					callback: function(mescroll) {
243
+						vm.$emit('down', mescroll);
244
+					}
245
+				},
246
+				// 上拉加载的配置
247
+				up: {
248
+					// 显示加载中的回调
249
+					showLoading() {
250
+						vm.upLoadType = 1;
251
+					},
252
+					// 显示无更多数据的回调
253
+					showNoMore() {
254
+						vm.upLoadType = 2;
255
+					},
256
+					// 隐藏上拉加载的回调
257
+					hideUpScroll(mescroll) {
258
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
259
+					},
260
+					// 空布局
261
+					empty: {
262
+						onShow(isShow) {
263
+							// 显示隐藏的回调
264
+							vm.isShowEmpty = isShow;
265
+						}
266
+					},
267
+					// 回到顶部
268
+					toTop: {
269
+						onShow(isShow) {
270
+							// 显示隐藏的回调
271
+							vm.isShowToTop = isShow;
272
+						}
273
+					},
274
+					// 派发上拉加载的回调
275
+					callback: function(mescroll) {
276
+						vm.$emit('up', mescroll);
277
+					}
278
+				}
279
+			};
280
+
281
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
282
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
283
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
284
+
285
+			// 初始化MeScroll对象
286
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
287
+			// init回调mescroll对象
288
+			vm.$emit('init', vm.mescroll);
289
+
290
+			// 设置高度
291
+			const sys = uni.getSystemInfoSync();
292
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
293
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
294
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
295
+			// 使down的bottomOffset生效
296
+			vm.mescroll.setBodyHeight(sys.windowHeight);
297
+			
298
+			// 因为使用的是page的scroll,这里需自定义scrollTo
299
+			vm.mescroll.resetScrollTo((y, t) => {
300
+				if(typeof y === 'string'){
301
+					// 滚动到指定view (y为css选择器)
302
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
303
+						let selector;
304
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
305
+							selector = '#'+y // 不带#和. 则默认为id选择器
306
+						}else{
307
+							selector = y
308
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
309
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
310
+								selector = y.split('>>>')[1].trim()
311
+							}
312
+							// #endif
313
+						}
314
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
315
+							if (rect) {
316
+								let top = rect.top
317
+								top += vm.mescroll.getScrollTop()
318
+								uni.pageScrollTo({
319
+									scrollTop: top,
320
+									duration: t
321
+								})
322
+							} else{
323
+								console.error(selector + ' does not exist');
324
+							}
325
+						}).exec()
326
+					},30)
327
+				} else{
328
+					// 滚动到指定位置 (y必须为数字)
329
+					uni.pageScrollTo({
330
+						scrollTop: y,
331
+						duration: t
332
+					})
333
+				}
334
+			});
335
+			
336
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
337
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
338
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
339
+			}
340
+		}
341
+	};
342
+</script>
343
+
344
+<style>
345
+	@import "../../mescroll-uni/mescroll-uni.css";
346
+	@import "../../mescroll-uni/components/mescroll-down.css";
347
+	@import "../../mescroll-uni/components/mescroll-up.css";
348
+	@import "./components/mescroll-down.css";
349
+	@import "./components/mescroll-up.css";
350
+</style>

+ 36
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/mescroll-uni-option.js Voir le fichier

@@ -0,0 +1,36 @@
1
+// 全局配置
2
+// mescroll-body 和 mescroll-uni 通用
3
+const GlobalOption = {
4
+	down: {
5
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
6
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
7
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
8
+		textLoading: '加载中 ...', // 加载中的提示文本
9
+		textSuccess: '加载成功', // 加载成功的文本
10
+		textErr: '加载失败', // 加载失败的文本
11
+		beforeEndDelay: 0, // 延时结束的时长 (此处设置为0)
12
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
13
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
14
+	},
15
+	up: {
16
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
17
+		textLoading: '加载中 ...', // 加载中的提示文本
18
+		textNoMore: '-- END --', // 没有更多数据的提示文本
19
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
20
+		toTop: {
21
+			// 回到顶部按钮,需配置src才显示
22
+			src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
23
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
24
+			right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
25
+			bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
26
+			width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
27
+		},
28
+		empty: {
29
+			use: true, // 是否显示空布局
30
+			icon: "https://www.mescroll.com/img/mescroll-empty.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
31
+			tip: '~ 空空如也 ~' // 提示
32
+		}
33
+	}
34
+}
35
+
36
+export default GlobalOption

+ 430
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-diy/xinlang/mescroll-uni.vue Voir le fichier

@@ -0,0 +1,430 @@
1
+<template>
2
+	<view class="mescroll-uni-warp">
3
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll"  :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
4
+			<view class="mescroll-uni-content mescroll-render-touch"
5
+			@touchstart="wxsBiz.touchstartEvent" 
6
+			@touchmove="wxsBiz.touchmoveEvent" 
7
+			@touchend="wxsBiz.touchendEvent" 
8
+			@touchcancel="wxsBiz.touchendEvent"
9
+			:change:prop="wxsBiz.propObserver"
10
+			:prop="wxsProp">
11
+			
12
+				<!-- 状态栏 -->
13
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
14
+				
15
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
16
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
17
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
18
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
19
+						<view class="downwarp-content">
20
+							<view v-if="isDownLoading" class="downwarp-progress"></view>
21
+							<view v-else class="downwarp-arrow" :style="{ transform: downRotate }"></view>
22
+							<view class="downwarp-tip">{{ downText }}</view>
23
+						</view>
24
+					</view>
25
+
26
+					<!-- 列表内容 -->
27
+					<slot></slot>
28
+
29
+					<!-- 空布局 -->
30
+					<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
31
+
32
+					<!-- 上拉加载区域 (下拉刷新时不显示,支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
33
+					<!-- <mescroll-up v-if="mescroll.optUp.use && downLoadType !== 3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
34
+					<view class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
35
+						<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
36
+						<view v-show="upLoadType===1">
37
+							<view class="upwarp-progress mescroll-rotate"></view>
38
+							<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
39
+						</view>
40
+						<!-- 无数据 -->
41
+						<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
42
+					</view>
43
+				</view>
44
+				
45
+				<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
46
+				<!-- #ifdef H5 -->
47
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
48
+				<!-- #endif -->
49
+				
50
+				<!-- 适配iPhoneX -->
51
+				<view v-if="safearea" class="mescroll-safearea"></view>
52
+			
53
+			</view>
54
+		</scroll-view>
55
+
56
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
57
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
58
+		
59
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
60
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
61
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
62
+		<!-- #endif -->
63
+	</view>
64
+</template>
65
+
66
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
67
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
68
+<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
69
+<!-- #endif -->
70
+
71
+<!-- app, h5使用renderjs -->
72
+<!-- #ifdef APP-PLUS || H5 -->
73
+<script module="renderBiz" lang="renderjs">
74
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
75
+	export default {
76
+		mixins: [renderBiz]
77
+	}
78
+</script>
79
+<!-- #endif -->
80
+
81
+<script>
82
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
83
+	import GlobalOption from './mescroll-uni-option.js';
84
+	import MescrollEmpty from '../../mescroll-uni/components/mescroll-empty.vue';
85
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
86
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
87
+	
88
+	export default {
89
+		mixins: [WxsMixin],
90
+		components: {
91
+			MescrollEmpty,
92
+			MescrollTop
93
+		},
94
+		data() {
95
+			return {
96
+				mescroll: null, // mescroll实例
97
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
98
+				downHight: 0, //下拉刷新: 容器高度
99
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
100
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
101
+				isShowEmpty: false, // 是否显示空布局
102
+				isShowToTop: false, // 是否显示回到顶部按钮
103
+				scrollTop: 0, // 滚动条的位置
104
+				scrollAnim: false, // 是否开启滚动动画
105
+				windowTop: 0, // 可使用窗口的顶部位置
106
+				windowBottom: 0, // 可使用窗口的底部位置
107
+				windowHeight: 0, // 可使用窗口的高度
108
+				statusBarHeight: 0 // 状态栏高度
109
+			}
110
+		},
111
+		props: {
112
+			down: Object, // 下拉刷新的参数配置
113
+			up: Object, // 上拉加载的参数配置
114
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
115
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
116
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
117
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
118
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
119
+				type: Boolean,
120
+				default: true
121
+			},
122
+			height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
123
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
124
+				type: Boolean,
125
+				default: true
126
+			}
127
+		},
128
+		computed: {
129
+			// 是否使用fixed定位 (当height有值,则不使用)
130
+			isFixed(){
131
+				return !this.height && this.fixed
132
+			},
133
+			// mescroll的高度
134
+			scrollHeight(){
135
+				if (this.isFixed) {
136
+					return "auto"
137
+				} else if(this.height){
138
+					return this.toPx(this.height) + 'px'
139
+				}else{
140
+					return "100%"
141
+				}
142
+			},
143
+			// 下拉布局往下偏移的距离 (px)
144
+			numTop() {
145
+				return this.toPx(this.top)
146
+			},
147
+			fixedTop() {
148
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
149
+			},
150
+			padTop() {
151
+				return !this.isFixed ? this.numTop + 'px' : 0
152
+			},
153
+			// 上拉布局往上偏移 (px)
154
+			numBottom() {
155
+				return this.toPx(this.bottom)
156
+			},
157
+			fixedBottom() {
158
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
159
+			},
160
+			padBottom() {
161
+				return !this.isFixed ? this.numBottom + 'px' : 0
162
+			},
163
+			// 是否为重置下拉的状态
164
+			isDownReset(){
165
+				return this.downLoadType===3 || this.downLoadType===4
166
+			},
167
+			// 过渡
168
+			transition() {
169
+				return this.isDownReset ? 'transform 300ms' : ''
170
+			},
171
+			translateY() {
172
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '' // transform会使fixed失效,需注意把fixed元素写在mescroll之外
173
+			},
174
+			// 列表是否可滑动
175
+			scrollable(){
176
+				return this.downLoadType===0 || this.isDownReset
177
+			},
178
+			// 是否在加载中
179
+			isDownLoading() {
180
+				return this.downLoadType === 3;
181
+			},
182
+			// 旋转的角度
183
+			downRotate() {
184
+				return this.downLoadType === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
185
+			},
186
+			// 文本提示
187
+			downText() {
188
+				if(!this.mescroll) return "";
189
+				switch (this.downLoadType) {
190
+					case 1:
191
+						return this.mescroll.optDown.textInOffset;
192
+					case 2:
193
+						return this.mescroll.optDown.textOutOffset;
194
+					case 3:
195
+						return this.mescroll.optDown.textLoading;
196
+					case 4:
197
+						return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
198
+					default:
199
+						return this.mescroll.optDown.textInOffset;
200
+				}
201
+			}
202
+		},
203
+		methods: {
204
+			//number,rpx,upx,px,% --> px的数值
205
+			toPx(num){
206
+				if(typeof num === "string"){
207
+					if (num.indexOf('px') !== -1) {
208
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
209
+							num = num.replace('rpx', '');
210
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
211
+							num = num.replace('upx', '');
212
+						} else { // "10px"
213
+							return Number(num.replace('px', ''))
214
+						}
215
+					}else if (num.indexOf('%') !== -1){
216
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
217
+						let rate = Number(num.replace("%","")) / 100
218
+						return this.windowHeight * rate
219
+					}
220
+				}
221
+				return num ? uni.upx2px(Number(num)) : 0
222
+			},
223
+			//注册列表滚动事件,用于下拉刷新和上拉加载
224
+			scroll(e) {
225
+				this.mescroll.scroll(e.detail, () => {
226
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
227
+				})
228
+			},
229
+			// 点击空布局的按钮回调
230
+			emptyClick() {
231
+				this.$emit('emptyclick', this.mescroll)
232
+			},
233
+			// 点击回到顶部的按钮回调
234
+			toTopClick() {
235
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
236
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
237
+			},
238
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
239
+			setClientHeight() {
240
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
241
+					this.isExec = true; // 避免多次获取
242
+					this.$nextTick(() => { // 确保dom已渲染
243
+						this.getClientInfo(data=>{
244
+							this.isExec = false;
245
+							if (data) {
246
+								this.mescroll.setClientHeight(data.height);
247
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
248
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
249
+								setTimeout(() => {
250
+									this.setClientHeight()
251
+								}, this.clientNum * 100)
252
+							}
253
+						})
254
+					})
255
+				}
256
+			},
257
+			// 获取滚动区域的信息
258
+			getClientInfo(success){
259
+				let query = uni.createSelectorQuery();
260
+				// #ifndef MP-ALIPAY || MP-DINGTALK
261
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
262
+				// #endif
263
+				let view = query.select('#' + this.viewId);
264
+				view.boundingClientRect(data => {
265
+					success(data)
266
+				}).exec();
267
+			}
268
+		},
269
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
270
+		created() {
271
+			let vm = this;
272
+
273
+			let diyOption = {
274
+				// 下拉刷新的配置
275
+				down: {
276
+					inOffset() {
277
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
278
+					},
279
+					outOffset() {
280
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
281
+					},
282
+					onMoving(mescroll, rate, downHight) {
283
+						// 下拉过程中的回调,滑动过程一直在执行;
284
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
285
+					},
286
+					showLoading(mescroll, downHight) {
287
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
288
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
289
+					},
290
+					beforeEndDownScroll(mescroll){
291
+						vm.downLoadType = 4; 
292
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
293
+					},
294
+					endDownScroll() {
295
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
296
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
297
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
298
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
299
+							if(vm.downLoadType===4) vm.downLoadType = 0
300
+						},300)
301
+					},
302
+					// 派发下拉刷新的回调
303
+					callback: function(mescroll) {
304
+						vm.$emit('down', mescroll)
305
+					}
306
+				},
307
+				// 上拉加载的配置
308
+				up: {
309
+					// 显示加载中的回调
310
+					showLoading() {
311
+						vm.upLoadType = 1;
312
+					},
313
+					// 显示无更多数据的回调
314
+					showNoMore() {
315
+						vm.upLoadType = 2;
316
+					},
317
+					// 隐藏上拉加载的回调
318
+					hideUpScroll(mescroll) {
319
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
320
+					},
321
+					// 空布局
322
+					empty: {
323
+						onShow(isShow) { // 显示隐藏的回调
324
+							vm.isShowEmpty = isShow;
325
+						}
326
+					},
327
+					// 回到顶部
328
+					toTop: {
329
+						onShow(isShow) { // 显示隐藏的回调
330
+							vm.isShowToTop = isShow;
331
+						}
332
+					},
333
+					// 派发上拉加载的回调
334
+					callback: function(mescroll) {
335
+						vm.$emit('up', mescroll);
336
+						// 更新容器的高度 (多mescroll的情况)
337
+						vm.setClientHeight()
338
+					}
339
+				}
340
+			}
341
+
342
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
343
+			let myOption = JSON.parse(JSON.stringify({
344
+				'down': vm.down,
345
+				'up': vm.up
346
+			})) // 深拷贝,避免对props的影响
347
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
348
+
349
+			// 初始化MeScroll对象
350
+			vm.mescroll = new MeScroll(myOption);
351
+			vm.mescroll.viewId = vm.viewId; // 附带id
352
+			// init回调mescroll对象
353
+			vm.$emit('init', vm.mescroll);
354
+			
355
+			// 设置高度
356
+			const sys = uni.getSystemInfoSync();
357
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
358
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
359
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
360
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
361
+			// 使down的bottomOffset生效
362
+			vm.mescroll.setBodyHeight(sys.windowHeight);
363
+
364
+			// 因为使用的是scrollview,这里需自定义scrollTo
365
+			vm.mescroll.resetScrollTo((y, t) => {
366
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
367
+				if(typeof y === 'string'){
368
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
369
+					vm.getClientInfo(function(rect){
370
+						let mescrollTop = rect.top // mescroll到顶部的距离
371
+						let selector;
372
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
373
+							selector = '#'+y // 不带#和. 则默认为id选择器
374
+						}else{
375
+							selector = y
376
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
377
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
378
+								selector = y.split('>>>')[1].trim()
379
+							}
380
+							// #endif
381
+						}
382
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
383
+							if (rect) {
384
+								let curY = vm.mescroll.getScrollTop()
385
+								let top = rect.top - mescrollTop
386
+								top += curY
387
+								if(!vm.isFixed) top -= vm.numTop
388
+								vm.scrollTop = curY;
389
+								vm.$nextTick(function() {
390
+									vm.scrollTop = top
391
+								})
392
+							} else{
393
+								console.error(selector + ' does not exist');
394
+							}
395
+						}).exec()
396
+					})
397
+					return;
398
+				}
399
+				let curY = vm.mescroll.getScrollTop()
400
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
401
+					vm.scrollTop = curY;
402
+					vm.$nextTick(function() {
403
+						vm.scrollTop = y
404
+					})
405
+				} else {
406
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
407
+						vm.scrollTop = step
408
+					}, t)
409
+				}
410
+			})
411
+			
412
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
413
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
414
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
415
+			}
416
+		},
417
+		mounted() {
418
+			// 设置容器的高度
419
+			this.setClientHeight()
420
+		}
421
+	}
422
+</script>
423
+
424
+<style>
425
+	@import "../../mescroll-uni/mescroll-uni.css";
426
+	@import "../../mescroll-uni/components/mescroll-down.css";
427
+	@import "../../mescroll-uni/components/mescroll-up.css";
428
+	@import "./components/mescroll-down.css";
429
+	@import "./components/mescroll-up.css";
430
+</style>

+ 55
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-down.css Voir le fichier

@@ -0,0 +1,55 @@
1
+/* 下拉刷新区域 */
2
+.mescroll-downwarp {
3
+	position: absolute;
4
+	top: -100%;
5
+	left: 0;
6
+	width: 100%;
7
+	height: 100%;
8
+	text-align: center;
9
+}
10
+
11
+/* 下拉刷新--内容区,定位于区域底部 */
12
+.mescroll-downwarp .downwarp-content {
13
+	position: absolute;
14
+	left: 0;
15
+	bottom: 0;
16
+	width: 100%;
17
+	min-height: 60rpx;
18
+	padding: 20rpx 0;
19
+	text-align: center;
20
+}
21
+
22
+/* 下拉刷新--提示文本 */
23
+.mescroll-downwarp .downwarp-tip {
24
+	display: inline-block;
25
+	font-size: 28rpx;
26
+	vertical-align: middle;
27
+	margin-left: 16rpx;
28
+	/* color: gray; 已在style设置color,此处删去*/
29
+}
30
+
31
+/* 下拉刷新--旋转进度条 */
32
+.mescroll-downwarp .downwarp-progress {
33
+	display: inline-block;
34
+	width: 32rpx;
35
+	height: 32rpx;
36
+	border-radius: 50%;
37
+	border: 2rpx solid gray;
38
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
39
+	vertical-align: middle;
40
+}
41
+
42
+/* 旋转动画 */
43
+.mescroll-downwarp .mescroll-rotate {
44
+	animation: mescrollDownRotate 0.6s linear infinite;
45
+}
46
+
47
+@keyframes mescrollDownRotate {
48
+	0% {
49
+		transform: rotate(0deg);
50
+	}
51
+
52
+	100% {
53
+		transform: rotate(360deg);
54
+	}
55
+}

+ 47
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-down.vue Voir le fichier

@@ -0,0 +1,47 @@
1
+<!-- 下拉刷新区域 -->
2
+<template>
3
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
4
+		<view class="downwarp-content">
5
+			<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.textColor, 'transform':downRotate}"></view>
6
+			<view class="downwarp-tip">{{downText}}</view>
7
+		</view>
8
+	</view>
9
+</template>
10
+
11
+<script>
12
+export default {
13
+	props: {
14
+		option: Object , // down的配置项
15
+		type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
16
+		rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
17
+	},
18
+	computed: {
19
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
20
+		mOption(){
21
+			return this.option || {}
22
+		},
23
+		// 是否在加载中
24
+		isDownLoading(){
25
+			return this.type === 3
26
+		},
27
+		// 旋转的角度
28
+		downRotate(){
29
+			return 'rotate(' + 360 * this.rate + 'deg)'
30
+		},
31
+		// 文本提示
32
+		downText(){
33
+			switch (this.type){
34
+				case 1: return this.mOption.textInOffset;
35
+				case 2: return this.mOption.textOutOffset;
36
+				case 3: return this.mOption.textLoading;
37
+				case 4: return this.mOption.textLoading;
38
+				default: return this.mOption.textInOffset;
39
+			}
40
+		}
41
+	}
42
+};
43
+</script>
44
+
45
+<style>
46
+@import "./mescroll-down.css";
47
+</style>

+ 90
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-empty.vue Voir le fichier

@@ -0,0 +1,90 @@
1
+<!--空布局
2
+
3
+可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
4
+import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
5
+<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
6
+
7
+-->
8
+<template>
9
+	<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
10
+		<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
11
+		<view v-if="tip" class="empty-tip">{{ tip }}</view>
12
+		<view v-if="option.btnText" class="empty-btn" @click="emptyClick">{{ option.btnText }}</view>
13
+	</view>
14
+</template>
15
+
16
+<script>
17
+// 引入全局配置
18
+import GlobalOption from './../mescroll-uni-option.js';
19
+export default {
20
+	props: {
21
+		// empty的配置项: 默认为GlobalOption.up.empty
22
+		option: {
23
+			type: Object,
24
+			default() {
25
+				return {};
26
+			}
27
+		}
28
+	},
29
+	// 使用computed获取配置,用于支持option的动态配置
30
+	computed: {
31
+		// 图标
32
+		icon() {
33
+			return this.option.icon == null ? GlobalOption.up.empty.icon : this.option.icon; // 此处不使用短路求值, 用于支持传空串不显示图标
34
+		},
35
+		// 文本提示
36
+		tip() {
37
+			return this.option.tip == null ? GlobalOption.up.empty.tip : this.option.tip; // 此处不使用短路求值, 用于支持传空串不显示文本提示
38
+		}
39
+	},
40
+	methods: {
41
+		// 点击按钮
42
+		emptyClick() {
43
+			this.$emit('emptyclick');
44
+		}
45
+	}
46
+};
47
+</script>
48
+
49
+<style>
50
+/* 无任何数据的空布局 */
51
+.mescroll-empty {
52
+	box-sizing: border-box;
53
+	width: 100%;
54
+	padding: 100rpx 50rpx;
55
+	text-align: center;
56
+}
57
+
58
+.mescroll-empty.empty-fixed {
59
+	z-index: 99;
60
+	position: absolute; /*transform会使fixed失效,最终会降级为absolute */
61
+	top: 100rpx;
62
+	left: 0;
63
+}
64
+
65
+.mescroll-empty .empty-icon {
66
+	width: 280rpx;
67
+	height: 280rpx;
68
+}
69
+
70
+.mescroll-empty .empty-tip {
71
+	margin-top: 20rpx;
72
+	font-size: 24rpx;
73
+	color: gray;
74
+}
75
+
76
+.mescroll-empty .empty-btn {
77
+	display: inline-block;
78
+	margin-top: 40rpx;
79
+	min-width: 200rpx;
80
+	padding: 18rpx;
81
+	font-size: 28rpx;
82
+	border: 1rpx solid #e04b28;
83
+	border-radius: 60rpx;
84
+	color: #e04b28;
85
+}
86
+
87
+.mescroll-empty .empty-btn:active {
88
+	opacity: 0.75;
89
+}
90
+</style>

+ 83
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-top.vue Voir le fichier

@@ -0,0 +1,83 @@
1
+<!-- 回到顶部的按钮 -->
2
+<template>
3
+	<image
4
+		v-if="mOption.src"
5
+		class="mescroll-totop"
6
+		:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
7
+		:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
8
+		:src="mOption.src"
9
+		mode="widthFix"
10
+		@click="toTopClick"
11
+	/>
12
+</template>
13
+
14
+<script>
15
+export default {
16
+	props: {
17
+		// up.toTop的配置项
18
+		option: Object,
19
+		// 是否显示
20
+		value: false
21
+	},
22
+	computed: {
23
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
24
+		mOption(){
25
+			return this.option || {}
26
+		},
27
+		// 优先显示左边
28
+		left(){
29
+			return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
30
+		},
31
+		// 右边距离 (优先显示左边)
32
+		right() {
33
+			return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
34
+		}
35
+	},
36
+	methods: {
37
+		addUnit(num){
38
+			if(!num) return 0;
39
+			if(typeof num === 'number') return num + 'rpx';
40
+			return num
41
+		},
42
+		toTopClick() {
43
+			this.$emit('input', false); // 使v-model生效
44
+			this.$emit('click'); // 派发点击事件
45
+		}
46
+	}
47
+};
48
+</script>
49
+
50
+<style>
51
+/* 回到顶部的按钮 */
52
+.mescroll-totop {
53
+	z-index: 9990;
54
+	position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
55
+	right: 20rpx;
56
+	bottom: 120rpx;
57
+	width: 72rpx;
58
+	height: auto;
59
+	border-radius: 50%;
60
+	opacity: 0;
61
+	transition: opacity 0.5s; /* 过渡 */
62
+	margin-bottom: var(--window-bottom); /* css变量 */
63
+}
64
+
65
+/* 适配 iPhoneX */
66
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
67
+	.mescroll-totop-safearea {
68
+		margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
69
+		margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
70
+	}
71
+}
72
+
73
+/* 显示 -- 淡入 */
74
+.mescroll-totop-in {
75
+	opacity: 1;
76
+}
77
+
78
+/* 隐藏 -- 淡出且不接收事件*/
79
+.mescroll-totop-out {
80
+	opacity: 0;
81
+	pointer-events: none;
82
+}
83
+</style>

+ 47
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-up.css Voir le fichier

@@ -0,0 +1,47 @@
1
+/* 上拉加载区域 */
2
+.mescroll-upwarp {
3
+	box-sizing: border-box;
4
+	min-height: 110rpx;
5
+	padding: 30rpx 0;
6
+	text-align: center;
7
+	clear: both;
8
+}
9
+
10
+/*提示文本 */
11
+.mescroll-upwarp .upwarp-tip,
12
+.mescroll-upwarp .upwarp-nodata {
13
+	display: inline-block;
14
+	font-size: 28rpx;
15
+	vertical-align: middle;
16
+	/* color: gray; 已在style设置color,此处删去*/
17
+}
18
+
19
+.mescroll-upwarp .upwarp-tip {
20
+	margin-left: 16rpx;
21
+}
22
+
23
+/*旋转进度条 */
24
+.mescroll-upwarp .upwarp-progress {
25
+	display: inline-block;
26
+	width: 32rpx;
27
+	height: 32rpx;
28
+	border-radius: 50%;
29
+	border: 2rpx solid gray;
30
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
31
+	vertical-align: middle;
32
+}
33
+
34
+/* 旋转动画 */
35
+.mescroll-upwarp .mescroll-rotate {
36
+	animation: mescrollUpRotate 0.6s linear infinite;
37
+}
38
+
39
+@keyframes mescrollUpRotate {
40
+	0% {
41
+		transform: rotate(0deg);
42
+	}
43
+
44
+	100% {
45
+		transform: rotate(360deg);
46
+	}
47
+}

+ 39
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/components/mescroll-up.vue Voir le fichier

@@ -0,0 +1,39 @@
1
+<!-- 上拉加载区域 -->
2
+<template>
3
+	<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
4
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
5
+		<view v-show="isUpLoading">
6
+			<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.textColor}"></view>
7
+			<view class="upwarp-tip">{{ mOption.textLoading }}</view>
8
+		</view>
9
+		<!-- 无数据 -->
10
+		<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
11
+	</view>
12
+</template>
13
+
14
+<script>
15
+export default {
16
+	props: {
17
+		option: Object, // up的配置项
18
+		type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
19
+	},
20
+	computed: {
21
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
22
+		mOption() {
23
+			return this.option || {};
24
+		},
25
+		// 加载中
26
+		isUpLoading() {
27
+			return this.type === 1;
28
+		},
29
+		// 没有更多了
30
+		isUpNoMore() {
31
+			return this.type === 2;
32
+		}
33
+	}
34
+};
35
+</script>
36
+
37
+<style>
38
+@import './mescroll-up.css';
39
+</style>

+ 19
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-body.css Voir le fichier

@@ -0,0 +1,19 @@
1
+.mescroll-body {
2
+	position: relative; /* 下拉刷新区域相对自身定位 */
3
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
4
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
5
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
6
+}
7
+
8
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
9
+.mescroll-body.mescorll-sticky{
10
+	overflow: unset !important
11
+}
12
+
13
+/* 适配 iPhoneX */
14
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
15
+	.mescroll-safearea {
16
+		padding-bottom: constant(safe-area-inset-bottom);
17
+		padding-bottom: env(safe-area-inset-bottom);
18
+	}
19
+}

+ 348
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-body.vue Voir le fichier

@@ -0,0 +1,348 @@
1
+<template>
2
+	<view 
3
+	class="mescroll-body mescroll-render-touch" 
4
+	:class="{'mescorll-sticky': sticky}"
5
+	:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
6
+	@touchstart="wxsBiz.touchstartEvent" 
7
+	@touchmove="wxsBiz.touchmoveEvent" 
8
+	@touchend="wxsBiz.touchendEvent" 
9
+	@touchcancel="wxsBiz.touchendEvent"
10
+	:change:prop="wxsBiz.propObserver"
11
+	:prop="wxsProp"
12
+	>
13
+		<!-- 状态栏 -->
14
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
15
+		
16
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
17
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
18
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
19
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
20
+				<view class="downwarp-content">
21
+					<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
22
+					<view class="downwarp-tip">{{downText}}</view>
23
+				</view>
24
+			</view>
25
+	
26
+			<!-- 列表内容 -->
27
+			<slot></slot>
28
+
29
+			<!-- 空布局 -->
30
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
31
+
32
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
33
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
34
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
35
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
36
+				<view v-show="upLoadType===1">
37
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
38
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
39
+				</view>
40
+				<!-- 无数据 -->
41
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
42
+			</view>
43
+		</view>
44
+		
45
+		<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
46
+		<!-- #ifdef H5 -->
47
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
48
+		<!-- #endif -->
49
+		
50
+		<!-- 适配iPhoneX -->
51
+		<view v-if="safearea" class="mescroll-safearea"></view>
52
+		
53
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
54
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
55
+		
56
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
57
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
58
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
59
+		<!-- #endif -->
60
+	</view>
61
+</template>
62
+
63
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
64
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
65
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
66
+<!-- #endif -->
67
+
68
+<!-- app, h5使用renderjs -->
69
+<!-- #ifdef APP-PLUS || H5 -->
70
+<script module="renderBiz" lang="renderjs">
71
+	import renderBiz from './wxs/renderjs.js';
72
+	export default {
73
+		mixins: [renderBiz]
74
+	}
75
+</script>
76
+<!-- #endif -->
77
+
78
+<script>
79
+	// 引入mescroll-uni.js,处理核心逻辑
80
+	import MeScroll from './mescroll-uni.js';
81
+	// 引入全局配置
82
+	import GlobalOption from './mescroll-uni-option.js';
83
+	// 引入空布局组件
84
+	import MescrollEmpty from './components/mescroll-empty.vue';
85
+	// 引入回到顶部组件
86
+	import MescrollTop from './components/mescroll-top.vue';
87
+	// 引入兼容wxs(含renderjs)写法的mixins
88
+	import WxsMixin from './wxs/mixins.js';
89
+	
90
+	export default {
91
+		mixins: [WxsMixin],
92
+		components: {
93
+			MescrollEmpty,
94
+			MescrollTop
95
+		},
96
+		data() {
97
+			return {
98
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
99
+				downHight: 0, //下拉刷新: 容器高度
100
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
101
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
102
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
103
+				isShowEmpty: false, // 是否显示空布局
104
+				isShowToTop: false, // 是否显示回到顶部按钮
105
+				windowHeight: 0, // 可使用窗口的高度
106
+				windowBottom: 0, // 可使用窗口的底部位置
107
+				statusBarHeight: 0 // 状态栏高度
108
+			};
109
+		},
110
+		props: {
111
+			down: Object, // 下拉刷新的参数配置
112
+			up: Object, // 上拉加载的参数配置
113
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
114
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
115
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
116
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
117
+			height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
118
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
119
+				type: Boolean,
120
+				default: true
121
+			},
122
+			sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
123
+		},
124
+		computed: {
125
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
126
+			minHeight(){
127
+				return this.toPx(this.height || '100%') + 'px'
128
+			},
129
+			// 下拉布局往下偏移的距离 (px)
130
+			numTop() {
131
+				return this.toPx(this.top)
132
+			},
133
+			padTop() {
134
+				return this.numTop + 'px';
135
+			},
136
+			// 上拉布局往上偏移 (px)
137
+			numBottom() {
138
+				return this.toPx(this.bottom);
139
+			},
140
+			padBottom() {
141
+				return this.numBottom + 'px';
142
+			},
143
+			// 是否为重置下拉的状态
144
+			isDownReset() {
145
+				return this.downLoadType === 3 || this.downLoadType === 4;
146
+			},
147
+			// 过渡
148
+			transition() {
149
+				return this.isDownReset ? 'transform 300ms' : '';
150
+			},
151
+			translateY() {
152
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
153
+			},
154
+			// 是否在加载中
155
+			isDownLoading(){
156
+				return this.downLoadType === 3
157
+			},
158
+			// 旋转的角度
159
+			downRotate(){
160
+				return 'rotate(' + 360 * this.downRate + 'deg)'
161
+			},
162
+			// 文本提示
163
+			downText(){
164
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
165
+				switch (this.downLoadType){
166
+					case 1: return this.mescroll.optDown.textInOffset;
167
+					case 2: return this.mescroll.optDown.textOutOffset;
168
+					case 3: return this.mescroll.optDown.textLoading;
169
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
170
+					default: return this.mescroll.optDown.textInOffset;
171
+				}
172
+			}
173
+		},
174
+		methods: {
175
+			//number,rpx,upx,px,% --> px的数值
176
+			toPx(num) {
177
+				if (typeof num === 'string') {
178
+					if (num.indexOf('px') !== -1) {
179
+						if (num.indexOf('rpx') !== -1) {
180
+							// "10rpx"
181
+							num = num.replace('rpx', '');
182
+						} else if (num.indexOf('upx') !== -1) {
183
+							// "10upx"
184
+							num = num.replace('upx', '');
185
+						} else {
186
+							// "10px"
187
+							return Number(num.replace('px', ''));
188
+						}
189
+					} else if (num.indexOf('%') !== -1) {
190
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
191
+						let rate = Number(num.replace('%', '')) / 100;
192
+						return this.windowHeight * rate;
193
+					}
194
+				}
195
+				return num ? uni.upx2px(Number(num)) : 0;
196
+			},
197
+			// 点击空布局的按钮回调
198
+			emptyClick() {
199
+				this.$emit('emptyclick', this.mescroll);
200
+			},
201
+			// 点击回到顶部的按钮回调
202
+			toTopClick() {
203
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
204
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
205
+			}
206
+		},
207
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
208
+		created() {
209
+			let vm = this;
210
+
211
+			let diyOption = {
212
+				// 下拉刷新的配置
213
+				down: {
214
+					inOffset() {
215
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
216
+					},
217
+					outOffset() {
218
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
219
+					},
220
+					onMoving(mescroll, rate, downHight) {
221
+						// 下拉过程中的回调,滑动过程一直在执行;
222
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
223
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
224
+					},
225
+					showLoading(mescroll, downHight) {
226
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
227
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
228
+					},
229
+					beforeEndDownScroll(mescroll){
230
+						vm.downLoadType = 4; 
231
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
232
+					},
233
+					endDownScroll() {
234
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
235
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
236
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
237
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
238
+							if(vm.downLoadType === 4) vm.downLoadType = 0
239
+						},300)
240
+					},
241
+					// 派发下拉刷新的回调
242
+					callback: function(mescroll) {
243
+						vm.$emit('down', mescroll);
244
+					}
245
+				},
246
+				// 上拉加载的配置
247
+				up: {
248
+					// 显示加载中的回调
249
+					showLoading() {
250
+						vm.upLoadType = 1;
251
+					},
252
+					// 显示无更多数据的回调
253
+					showNoMore() {
254
+						vm.upLoadType = 2;
255
+					},
256
+					// 隐藏上拉加载的回调
257
+					hideUpScroll(mescroll) {
258
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
259
+					},
260
+					// 空布局
261
+					empty: {
262
+						onShow(isShow) {
263
+							// 显示隐藏的回调
264
+							vm.isShowEmpty = isShow;
265
+						}
266
+					},
267
+					// 回到顶部
268
+					toTop: {
269
+						onShow(isShow) {
270
+							// 显示隐藏的回调
271
+							vm.isShowToTop = isShow;
272
+						}
273
+					},
274
+					// 派发上拉加载的回调
275
+					callback: function(mescroll) {
276
+						vm.$emit('up', mescroll);
277
+					}
278
+				}
279
+			};
280
+
281
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
282
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
283
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
284
+
285
+			// 初始化MeScroll对象
286
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
287
+			// init回调mescroll对象
288
+			vm.$emit('init', vm.mescroll);
289
+
290
+			// 设置高度
291
+			const sys = uni.getSystemInfoSync();
292
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
293
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
294
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
295
+			// 使down的bottomOffset生效
296
+			vm.mescroll.setBodyHeight(sys.windowHeight);
297
+
298
+			// 因为使用的是page的scroll,这里需自定义scrollTo
299
+			vm.mescroll.resetScrollTo((y, t) => {
300
+				if(typeof y === 'string'){
301
+					// 滚动到指定view (y为css选择器)
302
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
303
+						let selector;
304
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
305
+							selector = '#'+y // 不带#和. 则默认为id选择器
306
+						}else{
307
+							selector = y
308
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
309
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
310
+								selector = y.split('>>>')[1].trim()
311
+							}
312
+							// #endif
313
+						}
314
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
315
+							if (rect) {
316
+								let top = rect.top
317
+								top += vm.mescroll.getScrollTop()
318
+								uni.pageScrollTo({
319
+									scrollTop: top,
320
+									duration: t
321
+								})
322
+							} else{
323
+								console.error(selector + ' does not exist');
324
+							}
325
+						}).exec()
326
+					},30)
327
+				} else{
328
+					// 滚动到指定位置 (y必须为数字)
329
+					uni.pageScrollTo({
330
+						scrollTop: y,
331
+						duration: t
332
+					})
333
+				}
334
+			});
335
+
336
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
337
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
338
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
339
+			}
340
+		}
341
+	};
342
+</script>
343
+
344
+<style>
345
+	@import "./mescroll-body.css";
346
+	@import "./components/mescroll-down.css";
347
+	@import './components/mescroll-up.css';
348
+</style>

+ 65
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js Voir le fichier

@@ -0,0 +1,65 @@
1
+// mescroll-body 和 mescroll-uni 通用
2
+
3
+// import MescrollUni from "./mescroll-uni.vue";
4
+// import MescrollBody from "./mescroll-body.vue";
5
+
6
+const MescrollMixin = {
7
+	// components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
8
+	// 	MescrollUni,
9
+	// 	MescrollBody
10
+	// },
11
+	data() {
12
+		return {
13
+			mescroll: null //mescroll实例对象
14
+		}
15
+	},
16
+	// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
17
+	onPullDownRefresh(){
18
+		this.mescroll && this.mescroll.onPullDownRefresh();
19
+	},
20
+	// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
21
+	onPageScroll(e) {
22
+		this.mescroll && this.mescroll.onPageScroll(e);
23
+	},
24
+	// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
25
+	onReachBottom() {
26
+		this.mescroll && this.mescroll.onReachBottom();
27
+	},
28
+	methods: {
29
+		// mescroll组件初始化的回调,可获取到mescroll对象
30
+		mescrollInit(mescroll) {
31
+			this.mescroll = mescroll;
32
+			this.mescrollInitByRef(); // 兼容字节跳动小程序
33
+		},
34
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
35
+		mescrollInitByRef() {
36
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
37
+				let mescrollRef = this.$refs.mescrollRef;
38
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
39
+			}
40
+		},
41
+		// 下拉刷新的回调 (mixin默认resetUpScroll)
42
+		downCallback() {
43
+			if(this.mescroll.optUp.use){
44
+				this.mescroll.resetUpScroll()
45
+			}else{
46
+				setTimeout(()=>{
47
+					this.mescroll.endSuccess();
48
+				}, 500)
49
+			}
50
+		},
51
+		// 上拉加载的回调
52
+		upCallback() {
53
+			// mixin默认延时500自动结束加载
54
+			setTimeout(()=>{
55
+				this.mescroll.endErr();
56
+			}, 500)
57
+		}
58
+	},
59
+	mounted() {
60
+		this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
61
+	}
62
+	
63
+}
64
+
65
+export default MescrollMixin;

+ 36
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni-option.js Voir le fichier

@@ -0,0 +1,36 @@
1
+// 全局配置
2
+// mescroll-body 和 mescroll-uni 通用
3
+const GlobalOption = {
4
+	down: {
5
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
6
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
7
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
8
+		textLoading: '加载中 ...', // 加载中的提示文本
9
+		textSuccess: '加载成功', // 加载成功的文本
10
+		textErr: '加载失败', // 加载失败的文本
11
+		beforeEndDelay: 100, // 延时结束的时长 (显示加载成功/失败的时长)
12
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
13
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
14
+	},
15
+	up: {
16
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
17
+		textLoading: '加载中 ...', // 加载中的提示文本
18
+		textNoMore: '-- END --', // 没有更多数据的提示文本
19
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
20
+		toTop: {
21
+			// 回到顶部按钮,需配置src才显示
22
+			src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
23
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
24
+			right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
25
+			bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
26
+			width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
27
+		},
28
+		empty: {
29
+			use: true, // 是否显示空布局
30
+			icon: "https://www.mescroll.com/img/mescroll-empty.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
31
+			tip: '~ 空空如也 ~' // 提示
32
+		}
33
+	}
34
+}
35
+
36
+export default GlobalOption

+ 36
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni.css Voir le fichier

@@ -0,0 +1,36 @@
1
+.mescroll-uni-warp{
2
+	height: 100%;
3
+}
4
+
5
+.mescroll-uni-content{
6
+	height: 100%;
7
+}
8
+
9
+.mescroll-uni {
10
+	position: relative;
11
+	width: 100%;
12
+	height: 100%;
13
+	min-height: 200rpx;
14
+	overflow-y: auto;
15
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
16
+}
17
+
18
+/* 定位的方式固定高度 */
19
+.mescroll-uni-fixed{
20
+	z-index: 1;
21
+	position: fixed;
22
+	top: 0;
23
+	left: 0;
24
+	right: 0;
25
+	bottom: 0;
26
+	width: auto; /* 使right生效 */
27
+	height: auto; /* 使bottom生效 */
28
+}
29
+
30
+/* 适配 iPhoneX */
31
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
32
+	.mescroll-safearea {
33
+		padding-bottom: constant(safe-area-inset-bottom);
34
+		padding-bottom: env(safe-area-inset-bottom);
35
+	}
36
+}

+ 799
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni.js Voir le fichier

@@ -0,0 +1,799 @@
1
+/* mescroll
2
+ * version 1.3.3
3
+ * 2020-09-15 wenju
4
+ * https://www.mescroll.com
5
+ */
6
+
7
+export default function MeScroll(options, isScrollBody) {
8
+	let me = this;
9
+	me.version = '1.3.3'; // mescroll版本号
10
+	me.options = options || {}; // 配置
11
+	me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
12
+
13
+	me.isDownScrolling = false; // 是否在执行下拉刷新的回调
14
+	me.isUpScrolling = false; // 是否在执行上拉加载的回调
15
+	let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
16
+
17
+	// 初始化下拉刷新
18
+	me.initDownScroll();
19
+	// 初始化上拉加载,则初始化
20
+	me.initUpScroll();
21
+
22
+	// 自动加载
23
+	setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
24
+		// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
25
+		if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
26
+			if (me.optDown.autoShowLoading) {
27
+				me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
28
+			} else {
29
+				me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
30
+			}
31
+		}
32
+		// 自动触发上拉加载
33
+		if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
34
+			setTimeout(function(){
35
+				me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
36
+			},100)
37
+		}
38
+	}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
39
+}
40
+
41
+/* 配置参数:下拉刷新 */
42
+MeScroll.prototype.extendDownScroll = function(optDown) {
43
+	// 下拉刷新的配置
44
+	MeScroll.extend(optDown, {
45
+		use: true, // 是否启用下拉刷新; 默认true
46
+		auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
47
+		native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
48
+		autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
49
+		isLock: false, // 是否锁定下拉刷新,默认false;
50
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
51
+		startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
52
+		inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
53
+		outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
54
+		bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
55
+		minAngle: 45, // 向下滑动最少偏移的角度,取值区间  [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
56
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
57
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
58
+		textLoading: '加载中 ...', // 加载中的提示文本
59
+		textSuccess: '加载成功', // 加载成功的文本
60
+		textErr: '加载失败', // 加载失败的文本
61
+		beforeEndDelay: 100, // 延时结束的时长 (显示加载成功/失败的时长)
62
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
63
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
64
+		inited: null, // 下拉刷新初始化完毕的回调
65
+		inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
66
+		outOffset: null, // 下拉的距离大于offset那一刻的回调
67
+		onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
68
+		beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
69
+		showLoading: null, // 显示下拉刷新进度的回调
70
+		afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
71
+		beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
72
+		endDownScroll: null, // 结束下拉刷新的回调
73
+		afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
74
+		callback: function(mescroll) {
75
+			// 下拉刷新的回调;默认重置上拉加载列表为第一页
76
+			mescroll.resetUpScroll();
77
+		}
78
+	})
79
+}
80
+
81
+/* 配置参数:上拉加载 */
82
+MeScroll.prototype.extendUpScroll = function(optUp) {
83
+	// 上拉加载的配置
84
+	MeScroll.extend(optUp, {
85
+		use: true, // 是否启用上拉加载; 默认true
86
+		auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
87
+		isLock: false, // 是否锁定上拉加载,默认false;
88
+		isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
89
+		callback: null, // 上拉加载的回调;function(page,mescroll){ }
90
+		page: {
91
+			num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
92
+			size: 10, // 每页数据的数量
93
+			time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
94
+		},
95
+		noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
96
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
97
+		textLoading: '加载中 ...', // 加载中的提示文本
98
+		textNoMore: '-- END --', // 没有更多数据的提示文本
99
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
100
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
101
+		inited: null, // 初始化完毕的回调
102
+		showLoading: null, // 显示加载中的回调
103
+		showNoMore: null, // 显示无更多数据的回调
104
+		hideUpScroll: null, // 隐藏上拉加载的回调
105
+		errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
106
+		toTop: {
107
+			// 回到顶部按钮,需配置src才显示
108
+			src: null, // 图片路径,默认null (绝对路径或网络图)
109
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
110
+			duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
111
+			btnClick: null, // 点击按钮的回调
112
+			onShow: null, // 是否显示的回调
113
+			zIndex: 9990, // fixed定位z-index值
114
+			left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
115
+			right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
116
+			bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
117
+			safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
118
+			width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
119
+			radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
120
+		},
121
+		empty: {
122
+			use: true, // 是否显示空布局
123
+			icon: null, // 图标路径
124
+			tip: '~ 暂无相关数据 ~', // 提示
125
+			btnText: '', // 按钮
126
+			btnClick: null, // 点击按钮的回调
127
+			onShow: null, // 是否显示的回调
128
+			fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
129
+			top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
130
+			zIndex: 99 // fixed定位z-index值
131
+		},
132
+		onScroll: false // 是否监听滚动事件
133
+	})
134
+}
135
+
136
+/* 配置参数 */
137
+MeScroll.extend = function(userOption, defaultOption) {
138
+	if (!userOption) return defaultOption;
139
+	for (let key in defaultOption) {
140
+		if (userOption[key] == null) {
141
+			let def = defaultOption[key];
142
+			if (def != null && typeof def === 'object') {
143
+				userOption[key] = MeScroll.extend({}, def); // 深度匹配
144
+			} else {
145
+				userOption[key] = def;
146
+			}
147
+		} else if (typeof userOption[key] === 'object') {
148
+			MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
149
+		}
150
+	}
151
+	return userOption;
152
+}
153
+
154
+/* 简单判断是否配置了颜色 (非透明,非白色) */
155
+MeScroll.prototype.hasColor = function(color) {
156
+	if(!color) return false;
157
+	let c = color.toLowerCase();
158
+	return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
159
+}
160
+
161
+/* -------初始化下拉刷新------- */
162
+MeScroll.prototype.initDownScroll = function() {
163
+	let me = this;
164
+	// 配置参数
165
+	me.optDown = me.options.down || {};
166
+	if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
167
+	me.extendDownScroll(me.optDown);
168
+	
169
+	// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
170
+	if(me.isScrollBody && me.optDown.native){
171
+		me.optDown.use = false
172
+	}else{
173
+		me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
174
+	}
175
+	
176
+	me.downHight = 0; // 下拉区域的高度
177
+
178
+	// 在页面中加入下拉布局
179
+	if (me.optDown.use && me.optDown.inited) {
180
+		// 初始化完毕的回调
181
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
182
+			me.optDown.inited(me);
183
+		}, 0)
184
+	}
185
+}
186
+
187
+/* 列表touchstart事件 */
188
+MeScroll.prototype.touchstartEvent = function(e) {
189
+	if (!this.optDown.use) return;
190
+
191
+	this.startPoint = this.getPoint(e); // 记录起点
192
+	this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
193
+	this.startAngle = 0; // 初始角度
194
+	this.lastPoint = this.startPoint; // 重置上次move的点
195
+	this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
196
+	this.inTouchend = false; // 标记不是touchend
197
+}
198
+
199
+/* 列表touchmove事件 */
200
+MeScroll.prototype.touchmoveEvent = function(e) {
201
+	if (!this.optDown.use) return;
202
+	let me = this;
203
+
204
+	let scrollTop = me.getScrollTop(); // 当前滚动条的距离
205
+	let curPoint = me.getPoint(e); // 当前点
206
+
207
+	let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
208
+
209
+	// 向下拉 && 在顶部
210
+	// mescroll-body,直接判定在顶部即可
211
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
212
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
213
+	if (moveY > 0 && (
214
+			(me.isScrollBody && scrollTop <= 0)
215
+			||
216
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
217
+		)) {
218
+		// 可下拉的条件
219
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
220
+				me.optUp.isBoth))) {
221
+
222
+			// 下拉的初始角度是否在配置的范围内
223
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
224
+			if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
225
+
226
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
227
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
228
+				me.inTouchend = true; // 标记执行touchend
229
+				me.touchendEvent(); // 提前触发touchend
230
+				return;
231
+			}
232
+			
233
+			me.preventDefault(e); // 阻止默认事件
234
+
235
+			let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
236
+
237
+			// 下拉距离  < 指定距离
238
+			if (me.downHight < me.optDown.offset) {
239
+				if (me.movetype !== 1) {
240
+					me.movetype = 1; // 加入标记,保证只执行一次
241
+					me.isDownEndSuccess = null; // 重置是否加载成功的状态 (wxs执行的是wxs.wxs)
242
+					me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
243
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
244
+				}
245
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
246
+
247
+				// 指定距离  <= 下拉距离
248
+			} else {
249
+				if (me.movetype !== 2) {
250
+					me.movetype = 2; // 加入标记,保证只执行一次
251
+					me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
252
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
253
+				}
254
+				if (diff > 0) { // 向下拉
255
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
256
+				} else { // 向上收
257
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
258
+				}
259
+			}
260
+			
261
+			me.downHight = Math.round(me.downHight) // 取整
262
+			let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
263
+			me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
264
+		}
265
+	}
266
+
267
+	me.lastPoint = curPoint; // 记录本次移动的点
268
+}
269
+
270
+/* 列表touchend事件 */
271
+MeScroll.prototype.touchendEvent = function(e) {
272
+	if (!this.optDown.use) return;
273
+	// 如果下拉区域高度已改变,则需重置回来
274
+	if (this.isMoveDown) {
275
+		if (this.downHight >= this.optDown.offset) {
276
+			// 符合触发刷新的条件
277
+			this.triggerDownScroll();
278
+		} else {
279
+			// 不符合的话 则重置
280
+			this.downHight = 0;
281
+			this.endDownScrollCall(this);
282
+		}
283
+		this.movetype = 0;
284
+		this.isMoveDown = false;
285
+	} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
286
+		let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
287
+		// 上滑
288
+		if (isScrollUp) {
289
+			// 需检查滑动的角度
290
+			let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
291
+			if (angle > 80) {
292
+				// 检查并触发上拉
293
+				this.triggerUpScroll(true);
294
+			}
295
+		}
296
+	}
297
+}
298
+
299
+/* 根据点击滑动事件获取第一个手指的坐标 */
300
+MeScroll.prototype.getPoint = function(e) {
301
+	if (!e) {
302
+		return {
303
+			x: 0,
304
+			y: 0
305
+		}
306
+	}
307
+	if (e.touches && e.touches[0]) {
308
+		return {
309
+			x: e.touches[0].pageX,
310
+			y: e.touches[0].pageY
311
+		}
312
+	} else if (e.changedTouches && e.changedTouches[0]) {
313
+		return {
314
+			x: e.changedTouches[0].pageX,
315
+			y: e.changedTouches[0].pageY
316
+		}
317
+	} else {
318
+		return {
319
+			x: e.clientX,
320
+			y: e.clientY
321
+		}
322
+	}
323
+}
324
+
325
+/* 计算两点之间的角度: 区间 [0,90]*/
326
+MeScroll.prototype.getAngle = function(p1, p2) {
327
+	let x = Math.abs(p1.x - p2.x);
328
+	let y = Math.abs(p1.y - p2.y);
329
+	let z = Math.sqrt(x * x + y * y);
330
+	let angle = 0;
331
+	if (z !== 0) {
332
+		angle = Math.asin(y / z) / Math.PI * 180;
333
+	}
334
+	return angle
335
+}
336
+
337
+/* 触发下拉刷新 */
338
+MeScroll.prototype.triggerDownScroll = function() {
339
+	if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
340
+		//return true则处于完全自定义状态
341
+	} else {
342
+		this.showDownScroll(); // 下拉刷新中...
343
+		!this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
344
+	}
345
+}
346
+
347
+/* 显示下拉进度布局 */
348
+MeScroll.prototype.showDownScroll = function() {
349
+	this.isDownScrolling = true; // 标记下拉中
350
+	if (this.optDown.native) {
351
+		uni.startPullDownRefresh(); // 系统自带的下拉刷新
352
+		this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
353
+	} else{
354
+		this.downHight = this.optDown.offset; // 更新下拉区域高度
355
+		this.showDownLoadingCall(this.downHight); // 下拉刷新中...
356
+	}
357
+}
358
+
359
+MeScroll.prototype.showDownLoadingCall = function(downHight) {
360
+	this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
361
+	this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
362
+}
363
+
364
+/* 显示系统自带的下拉刷新时需要处理的业务 */
365
+MeScroll.prototype.onPullDownRefresh = function() {
366
+	this.isDownScrolling = true; // 标记下拉中
367
+	this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
368
+	this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
369
+}
370
+
371
+/* 结束下拉刷新 */
372
+MeScroll.prototype.endDownScroll = function() {
373
+	if (this.optDown.native) { // 结束原生下拉刷新
374
+		this.isDownScrolling = false;
375
+		this.endDownScrollCall(this);
376
+		uni.stopPullDownRefresh();
377
+		return
378
+	}
379
+	let me = this;
380
+	// 结束下拉刷新的方法
381
+	let endScroll = function() {
382
+		me.downHight = 0;
383
+		me.isDownScrolling = false;
384
+		me.endDownScrollCall(me);
385
+		if(!me.isScrollBody){
386
+			me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
387
+			me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
388
+		}
389
+	}
390
+	// 结束下拉刷新时的回调
391
+	let delay = 0;
392
+	if (me.optDown.beforeEndDownScroll) {
393
+		delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
394
+		if(me.isDownEndSuccess == null) delay = 0; // 没有执行加载中,则不延时
395
+	}
396
+	if (typeof delay === 'number' && delay > 0) {
397
+		setTimeout(endScroll, delay);
398
+	} else {
399
+		endScroll();
400
+	}
401
+}
402
+
403
+MeScroll.prototype.endDownScrollCall = function() {
404
+	this.optDown.endDownScroll && this.optDown.endDownScroll(this);
405
+	this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
406
+}
407
+
408
+/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
409
+MeScroll.prototype.lockDownScroll = function(isLock) {
410
+	if (isLock == null) isLock = true;
411
+	this.optDown.isLock = isLock;
412
+}
413
+
414
+/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
415
+MeScroll.prototype.lockUpScroll = function(isLock) {
416
+	if (isLock == null) isLock = true;
417
+	this.optUp.isLock = isLock;
418
+}
419
+
420
+/* -------初始化上拉加载------- */
421
+MeScroll.prototype.initUpScroll = function() {
422
+	let me = this;
423
+	// 配置参数
424
+	me.optUp = me.options.up || {use: false}
425
+	if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
426
+	me.extendUpScroll(me.optUp);
427
+
428
+	if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
429
+	me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
430
+	me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
431
+
432
+	// 初始化完毕的回调
433
+	if (me.optUp.inited) {
434
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
435
+			me.optUp.inited(me);
436
+		}, 0)
437
+	}
438
+}
439
+
440
+/*滚动到底部的事件 (仅mescroll-body生效)*/
441
+MeScroll.prototype.onReachBottom = function() {
442
+	if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
443
+		if (!this.optUp.isLock && this.optUp.hasNext) {
444
+			this.triggerUpScroll();
445
+		}
446
+	}
447
+}
448
+
449
+/*列表滚动事件 (仅mescroll-body生效)*/
450
+MeScroll.prototype.onPageScroll = function(e) {
451
+	if (!this.isScrollBody) return;
452
+	
453
+	// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
454
+	this.setScrollTop(e.scrollTop);
455
+
456
+	// 顶部按钮的显示隐藏
457
+	if (e.scrollTop >= this.optUp.toTop.offset) {
458
+		this.showTopBtn();
459
+	} else {
460
+		this.hideTopBtn();
461
+	}
462
+}
463
+
464
+/*列表滚动事件*/
465
+MeScroll.prototype.scroll = function(e, onScroll) {
466
+	// 更新滚动条的位置
467
+	this.setScrollTop(e.scrollTop);
468
+	// 更新滚动内容高度
469
+	this.setScrollHeight(e.scrollHeight);
470
+
471
+	// 向上滑还是向下滑动
472
+	if (this.preScrollY == null) this.preScrollY = 0;
473
+	this.isScrollUp = e.scrollTop - this.preScrollY > 0;
474
+	this.preScrollY = e.scrollTop;
475
+
476
+	// 上滑 && 检查并触发上拉
477
+	this.isScrollUp && this.triggerUpScroll(true);
478
+
479
+	// 顶部按钮的显示隐藏
480
+	if (e.scrollTop >= this.optUp.toTop.offset) {
481
+		this.showTopBtn();
482
+	} else {
483
+		this.hideTopBtn();
484
+	}
485
+
486
+	// 滑动监听
487
+	this.optUp.onScroll && onScroll && onScroll()
488
+}
489
+
490
+/* 触发上拉加载 */
491
+MeScroll.prototype.triggerUpScroll = function(isCheck) {
492
+	if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
493
+		// 是否校验在底部; 默认不校验
494
+		if (isCheck === true) {
495
+			let canUp = false;
496
+			// 还有下一页 && 没有锁定 && 不在下拉中
497
+			if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
498
+				if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
499
+					canUp = true; // 标记可上拉
500
+				}
501
+			}
502
+			if (canUp === false) return;
503
+		}
504
+		this.showUpScroll(); // 上拉加载中...
505
+		this.optUp.page.num++; // 预先加一页,如果失败则减回
506
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
507
+		this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
508
+		this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
509
+		this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
510
+		this.optUp.callback(this); // 执行回调,联网加载数据
511
+	}
512
+}
513
+
514
+/* 显示上拉加载中 */
515
+MeScroll.prototype.showUpScroll = function() {
516
+	this.isUpScrolling = true; // 标记上拉加载中
517
+	this.optUp.showLoading && this.optUp.showLoading(this); // 回调
518
+}
519
+
520
+/* 显示上拉无更多数据 */
521
+MeScroll.prototype.showNoMore = function() {
522
+	this.optUp.hasNext = false; // 标记无更多数据
523
+	this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
524
+}
525
+
526
+/* 隐藏上拉区域**/
527
+MeScroll.prototype.hideUpScroll = function() {
528
+	this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
529
+}
530
+
531
+/* 结束上拉加载 */
532
+MeScroll.prototype.endUpScroll = function(isShowNoMore) {
533
+	if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
534
+		if (isShowNoMore) {
535
+			this.showNoMore(); // isShowNoMore=true,显示无更多数据
536
+		} else {
537
+			this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
538
+		}
539
+	}
540
+	this.isUpScrolling = false; // 标记结束上拉加载
541
+}
542
+
543
+/* 重置上拉加载列表为第一页
544
+ *isShowLoading 是否显示进度布局;
545
+ * 1.默认null,不传参,则显示上拉加载的进度布局
546
+ * 2.传参true, 则显示下拉刷新的进度布局
547
+ * 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
548
+ */
549
+MeScroll.prototype.resetUpScroll = function(isShowLoading) {
550
+	if (this.optUp && this.optUp.use) {
551
+		let page = this.optUp.page;
552
+		this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
553
+		this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
554
+		page.num = this.startNum; // 重置为第一页
555
+		page.time = null; // 重置时间为空
556
+		if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
557
+			if (isShowLoading == null) {
558
+				this.removeEmpty(); // 移除空布局
559
+				this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
560
+			} else {
561
+				this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
562
+			}
563
+		}
564
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
565
+		this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
566
+		this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
567
+		this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
568
+		this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
569
+	}
570
+}
571
+
572
+/* 设置page.num的值 */
573
+MeScroll.prototype.setPageNum = function(num) {
574
+	this.optUp.page.num = num - 1;
575
+}
576
+
577
+/* 设置page.size的值 */
578
+MeScroll.prototype.setPageSize = function(size) {
579
+	this.optUp.page.size = size;
580
+}
581
+
582
+/* 联网回调成功,结束下拉刷新和上拉加载
583
+ * dataSize: 当前页的数据量(必传)
584
+ * totalPage: 总页数(必传)
585
+ * systime: 服务器时间 (可空)
586
+ */
587
+MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
588
+	let hasNext;
589
+	if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
590
+	this.endSuccess(dataSize, hasNext, systime);
591
+}
592
+
593
+/* 联网回调成功,结束下拉刷新和上拉加载
594
+ * dataSize: 当前页的数据量(必传)
595
+ * totalSize: 列表所有数据总数量(必传)
596
+ * systime: 服务器时间 (可空)
597
+ */
598
+MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
599
+	let hasNext;
600
+	if (this.optUp.use && totalSize != null) {
601
+		let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
602
+		hasNext = loadSize < totalSize; // 是否还有下一页
603
+	}
604
+	this.endSuccess(dataSize, hasNext, systime);
605
+}
606
+
607
+/* 联网回调成功,结束下拉刷新和上拉加载
608
+ * dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
609
+ * hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
610
+ * systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
611
+ */
612
+MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
613
+	let me = this;
614
+	// 结束下拉刷新
615
+	if (me.isDownScrolling) {
616
+		me.isDownEndSuccess = true
617
+		me.endDownScroll();
618
+	}
619
+
620
+	// 结束上拉加载
621
+	if (me.optUp.use) {
622
+		let isShowNoMore; // 是否已无更多数据
623
+		if (dataSize != null) {
624
+			let pageNum = me.optUp.page.num; // 当前页码
625
+			let pageSize = me.optUp.page.size; // 每页长度
626
+			// 如果是第一页
627
+			if (pageNum === 1) {
628
+				if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
629
+			}
630
+			if (dataSize < pageSize || hasNext === false) {
631
+				// 返回的数据不满一页时,则说明已无更多数据
632
+				me.optUp.hasNext = false;
633
+				if (dataSize === 0 && pageNum === 1) {
634
+					// 如果第一页无任何数据且配置了空布局
635
+					isShowNoMore = false;
636
+					me.showEmpty();
637
+				} else {
638
+					// 总列表数少于配置的数量,则不显示无更多数据
639
+					let allDataSize = (pageNum - 1) * pageSize + dataSize;
640
+					if (allDataSize < me.optUp.noMoreSize) {
641
+						isShowNoMore = false;
642
+					} else {
643
+						isShowNoMore = true;
644
+					}
645
+					me.removeEmpty(); // 移除空布局
646
+				}
647
+			} else {
648
+				// 还有下一页
649
+				isShowNoMore = false;
650
+				me.optUp.hasNext = true;
651
+				me.removeEmpty(); // 移除空布局
652
+			}
653
+		}
654
+
655
+		// 隐藏上拉
656
+		me.endUpScroll(isShowNoMore);
657
+	}
658
+}
659
+
660
+/* 回调失败,结束下拉刷新和上拉加载 */
661
+MeScroll.prototype.endErr = function(errDistance) {
662
+	// 结束下拉,回调失败重置回原来的页码和时间
663
+	if (this.isDownScrolling) {
664
+		this.isDownEndSuccess = false
665
+		let page = this.optUp.page;
666
+		if (page && this.prePageNum) {
667
+			page.num = this.prePageNum;
668
+			page.time = this.prePageTime;
669
+		}
670
+		this.endDownScroll();
671
+	}
672
+	// 结束上拉,回调失败重置回原来的页码
673
+	if (this.isUpScrolling) {
674
+		this.optUp.page.num--;
675
+		this.endUpScroll(false);
676
+		// 如果是mescroll-body,则需往回滚一定距离
677
+		if(this.isScrollBody && errDistance !== 0){ // 不处理0
678
+			if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
679
+			this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
680
+		}
681
+	}
682
+}
683
+
684
+/* 显示空布局 */
685
+MeScroll.prototype.showEmpty = function() {
686
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
687
+}
688
+
689
+/* 移除空布局 */
690
+MeScroll.prototype.removeEmpty = function() {
691
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
692
+}
693
+
694
+/* 显示回到顶部的按钮 */
695
+MeScroll.prototype.showTopBtn = function() {
696
+	if (!this.topBtnShow) {
697
+		this.topBtnShow = true;
698
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
699
+	}
700
+}
701
+
702
+/* 隐藏回到顶部的按钮 */
703
+MeScroll.prototype.hideTopBtn = function() {
704
+	if (this.topBtnShow) {
705
+		this.topBtnShow = false;
706
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
707
+	}
708
+}
709
+
710
+/* 获取滚动条的位置 */
711
+MeScroll.prototype.getScrollTop = function() {
712
+	return this.scrollTop || 0
713
+}
714
+
715
+/* 记录滚动条的位置 */
716
+MeScroll.prototype.setScrollTop = function(y) {
717
+	this.scrollTop = y;
718
+}
719
+
720
+/* 滚动到指定位置 */
721
+MeScroll.prototype.scrollTo = function(y, t) {
722
+	this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
723
+}
724
+
725
+/* 自定义scrollTo */
726
+MeScroll.prototype.resetScrollTo = function(myScrollTo) {
727
+	this.myScrollTo = myScrollTo
728
+}
729
+
730
+/* 滚动条到底部的距离 */
731
+MeScroll.prototype.getScrollBottom = function() {
732
+	return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
733
+}
734
+
735
+/* 计步器
736
+ star: 开始值
737
+ end: 结束值
738
+ callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
739
+ t: 计步时长,传0则直接回调end值;不传则默认300ms
740
+ rate: 周期;不传则默认30ms计步一次
741
+ * */
742
+MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
743
+	let diff = end - star; // 差值
744
+	if (t === 0 || diff === 0) {
745
+		callback && callback(end);
746
+		return;
747
+	}
748
+	t = t || 300; // 时长 300ms
749
+	rate = rate || 30; // 周期 30ms
750
+	let count = t / rate; // 次数
751
+	let step = diff / count; // 步长
752
+	let i = 0; // 计数
753
+	let timer = setInterval(function() {
754
+		if (i < count - 1) {
755
+			star += step;
756
+			callback && callback(star, timer);
757
+			i++;
758
+		} else {
759
+			callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
760
+			clearInterval(timer);
761
+		}
762
+	}, rate);
763
+}
764
+
765
+/* 滚动容器的高度 */
766
+MeScroll.prototype.getClientHeight = function(isReal) {
767
+	let h = this.clientHeight || 0
768
+	if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
769
+		h = this.getBodyHeight()
770
+	}
771
+	return h
772
+}
773
+MeScroll.prototype.setClientHeight = function(h) {
774
+	this.clientHeight = h;
775
+}
776
+
777
+/* 滚动内容的高度 */
778
+MeScroll.prototype.getScrollHeight = function() {
779
+	return this.scrollHeight || 0;
780
+}
781
+MeScroll.prototype.setScrollHeight = function(h) {
782
+	this.scrollHeight = h;
783
+}
784
+
785
+/* body的高度 */
786
+MeScroll.prototype.getBodyHeight = function() {
787
+	return this.bodyHeight || 0;
788
+}
789
+MeScroll.prototype.setBodyHeight = function(h) {
790
+	this.bodyHeight = h;
791
+}
792
+
793
+/* 阻止浏览器默认滚动事件 */
794
+MeScroll.prototype.preventDefault = function(e) {
795
+	// 小程序不支持e.preventDefault, 已在wxs中禁止
796
+	// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
797
+	// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
798
+	if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
799
+}

+ 424
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mescroll-uni.vue Voir le fichier

@@ -0,0 +1,424 @@
1
+<template>
2
+	<view class="mescroll-uni-warp">
3
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
4
+			<view class="mescroll-uni-content mescroll-render-touch"
5
+			@touchstart="wxsBiz.touchstartEvent" 
6
+			@touchmove="wxsBiz.touchmoveEvent" 
7
+			@touchend="wxsBiz.touchendEvent" 
8
+			@touchcancel="wxsBiz.touchendEvent"
9
+			:change:prop="wxsBiz.propObserver"
10
+			:prop="wxsProp">
11
+				<!-- 状态栏 -->
12
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
13
+		
14
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
15
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
16
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
17
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
18
+						<view class="downwarp-content">
19
+							<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
20
+							<view class="downwarp-tip">{{downText}}</view>
21
+						</view>
22
+					</view>
23
+
24
+					<!-- 列表内容 -->
25
+					<slot></slot>
26
+
27
+					<!-- 空布局 -->
28
+					<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
29
+
30
+					<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
31
+					<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
32
+					<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
33
+						<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
34
+						<view v-show="upLoadType===1">
35
+							<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
36
+							<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
37
+						</view>
38
+						<!-- 无数据 -->
39
+						<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
40
+					</view>
41
+				</view>
42
+			
43
+				<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
44
+				<!-- #ifdef H5 -->
45
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
46
+				<!-- #endif -->
47
+				
48
+				<!-- 适配iPhoneX -->
49
+				<view v-if="safearea" class="mescroll-safearea"></view>
50
+			</view>
51
+		</scroll-view>
52
+
53
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
54
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
55
+		
56
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
57
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
58
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
59
+		<!-- #endif -->
60
+	</view>
61
+</template>
62
+
63
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
64
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
65
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
66
+<!-- #endif -->
67
+
68
+<!-- app, h5使用renderjs -->
69
+<!-- #ifdef APP-PLUS || H5 -->
70
+<script module="renderBiz" lang="renderjs">
71
+	import renderBiz from './wxs/renderjs.js';
72
+	export default {
73
+		mixins:[renderBiz]
74
+	}
75
+</script>
76
+<!-- #endif -->
77
+
78
+<script>
79
+	// 引入mescroll-uni.js,处理核心逻辑
80
+	import MeScroll from './mescroll-uni.js';
81
+	// 引入全局配置
82
+	import GlobalOption from './mescroll-uni-option.js';
83
+	// 引入空布局组件
84
+	import MescrollEmpty from './components/mescroll-empty.vue';
85
+	// 引入回到顶部组件
86
+	import MescrollTop from './components/mescroll-top.vue';
87
+	// 引入兼容wxs(含renderjs)写法的mixins
88
+	import WxsMixin from './wxs/mixins.js';
89
+	
90
+	export default {
91
+		mixins: [WxsMixin],
92
+		components: {
93
+			MescrollEmpty,
94
+			MescrollTop
95
+		},
96
+		data() {
97
+			return {
98
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
99
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
100
+				downHight: 0, //下拉刷新: 容器高度
101
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
102
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
103
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
104
+				isShowEmpty: false, // 是否显示空布局
105
+				isShowToTop: false, // 是否显示回到顶部按钮
106
+				scrollTop: 0, // 滚动条的位置
107
+				scrollAnim: false, // 是否开启滚动动画
108
+				windowTop: 0, // 可使用窗口的顶部位置
109
+				windowBottom: 0, // 可使用窗口的底部位置
110
+				windowHeight: 0, // 可使用窗口的高度
111
+				statusBarHeight: 0 // 状态栏高度
112
+			}
113
+		},
114
+		props: {
115
+			down: Object, // 下拉刷新的参数配置
116
+			up: Object, // 上拉加载的参数配置
117
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
118
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
119
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
120
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
121
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
122
+				type: Boolean,
123
+				default: true
124
+			},
125
+			height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
126
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
127
+				type: Boolean,
128
+				default: true
129
+			}
130
+		},
131
+		computed: {
132
+			// 是否使用fixed定位 (当height有值,则不使用)
133
+			isFixed(){
134
+				return !this.height && this.fixed
135
+			},
136
+			// mescroll的高度
137
+			scrollHeight(){
138
+				if (this.isFixed) {
139
+					return "auto"
140
+				} else if(this.height){
141
+					return this.toPx(this.height) + 'px'
142
+				}else{
143
+					return "100%"
144
+				}
145
+			},
146
+			// 下拉布局往下偏移的距离 (px)
147
+			numTop() {
148
+				return this.toPx(this.top)
149
+			},
150
+			fixedTop() {
151
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
152
+			},
153
+			padTop() {
154
+				return !this.isFixed ? this.numTop + 'px' : 0
155
+			},
156
+			// 上拉布局往上偏移 (px)
157
+			numBottom() {
158
+				return this.toPx(this.bottom)
159
+			},
160
+			fixedBottom() {
161
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
162
+			},
163
+			padBottom() {
164
+				return !this.isFixed ? this.numBottom + 'px' : 0
165
+			},
166
+			// 是否为重置下拉的状态
167
+			isDownReset(){
168
+				return this.downLoadType===3 || this.downLoadType===4
169
+			},
170
+			// 过渡
171
+			transition() {
172
+				return this.isDownReset ? 'transform 300ms' : '';
173
+			},
174
+			translateY() {
175
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
176
+			},
177
+			// 列表是否可滑动
178
+			scrollable(){
179
+				return this.downLoadType===0 || this.isDownReset
180
+			},
181
+			// 是否在加载中
182
+			isDownLoading(){
183
+				return this.downLoadType === 3
184
+			},
185
+			// 旋转的角度
186
+			downRotate(){
187
+				return 'rotate(' + 360 * this.downRate + 'deg)'
188
+			},
189
+			// 文本提示
190
+			downText(){
191
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
192
+				switch (this.downLoadType){
193
+					case 1: return this.mescroll.optDown.textInOffset;
194
+					case 2: return this.mescroll.optDown.textOutOffset;
195
+					case 3: return this.mescroll.optDown.textLoading;
196
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
197
+					default: return this.mescroll.optDown.textInOffset;
198
+				}
199
+			}
200
+		},
201
+		methods: {
202
+			//number,rpx,upx,px,% --> px的数值
203
+			toPx(num){
204
+				if(typeof num === "string"){
205
+					if (num.indexOf('px') !== -1) {
206
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
207
+							num = num.replace('rpx', '');
208
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
209
+							num = num.replace('upx', '');
210
+						} else { // "10px"
211
+							return Number(num.replace('px', ''))
212
+						}
213
+					}else if (num.indexOf('%') !== -1){
214
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
215
+						let rate = Number(num.replace("%","")) / 100
216
+						return this.windowHeight * rate
217
+					}
218
+				}
219
+				return num ? uni.upx2px(Number(num)) : 0
220
+			},
221
+			//注册列表滚动事件,用于下拉刷新和上拉加载
222
+			scroll(e) {
223
+				this.mescroll.scroll(e.detail, () => {
224
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
225
+				})
226
+			},
227
+			// 点击空布局的按钮回调
228
+			emptyClick() {
229
+				this.$emit('emptyclick', this.mescroll)
230
+			},
231
+			// 点击回到顶部的按钮回调
232
+			toTopClick() {
233
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
234
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
235
+			},
236
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
237
+			setClientHeight() {
238
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
239
+					this.isExec = true; // 避免多次获取
240
+					this.$nextTick(() => { // 确保dom已渲染
241
+						this.getClientInfo(data=>{
242
+							this.isExec = false;
243
+							if (data) {
244
+								this.mescroll.setClientHeight(data.height);
245
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
246
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
247
+								setTimeout(() => {
248
+									this.setClientHeight()
249
+								}, this.clientNum * 100)
250
+							}
251
+						})
252
+					})
253
+				}
254
+			},
255
+			// 获取滚动区域的信息
256
+			getClientInfo(success){
257
+				let query = uni.createSelectorQuery();
258
+				// #ifndef MP-ALIPAY || MP-DINGTALK
259
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
260
+				// #endif
261
+				let view = query.select('#' + this.viewId);
262
+				view.boundingClientRect(data => {
263
+					success(data)
264
+				}).exec();
265
+			}
266
+		},
267
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
268
+		created() {
269
+			let vm = this;
270
+
271
+			let diyOption = {
272
+				// 下拉刷新的配置
273
+				down: {
274
+					inOffset() {
275
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
276
+					},
277
+					outOffset() {
278
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
279
+					},
280
+					onMoving(mescroll, rate, downHight) {
281
+						// 下拉过程中的回调,滑动过程一直在执行;
282
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
283
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
284
+					},
285
+					showLoading(mescroll, downHight) {
286
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
287
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
288
+					},
289
+					beforeEndDownScroll(mescroll){
290
+						vm.downLoadType = 4; 
291
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
292
+					},
293
+					endDownScroll() {
294
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
295
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
296
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
297
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
298
+							if(vm.downLoadType===4) vm.downLoadType = 0
299
+						},300)
300
+					},
301
+					// 派发下拉刷新的回调
302
+					callback: function(mescroll) {
303
+						vm.$emit('down', mescroll)
304
+					}
305
+				},
306
+				// 上拉加载的配置
307
+				up: {
308
+					// 显示加载中的回调
309
+					showLoading() {
310
+						vm.upLoadType = 1;
311
+					},
312
+					// 显示无更多数据的回调
313
+					showNoMore() {
314
+						vm.upLoadType = 2;
315
+					},
316
+					// 隐藏上拉加载的回调
317
+					hideUpScroll(mescroll) {
318
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
319
+					},
320
+					// 空布局
321
+					empty: {
322
+						onShow(isShow) { // 显示隐藏的回调
323
+							vm.isShowEmpty = isShow;
324
+						}
325
+					},
326
+					// 回到顶部
327
+					toTop: {
328
+						onShow(isShow) { // 显示隐藏的回调
329
+							vm.isShowToTop = isShow;
330
+						}
331
+					},
332
+					// 派发上拉加载的回调
333
+					callback: function(mescroll) {
334
+						vm.$emit('up', mescroll);
335
+						// 更新容器的高度 (多mescroll的情况)
336
+						vm.setClientHeight()
337
+					}
338
+				}
339
+			}
340
+
341
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
342
+			let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
343
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
344
+
345
+			// 初始化MeScroll对象
346
+			vm.mescroll = new MeScroll(myOption);
347
+			vm.mescroll.viewId = vm.viewId; // 附带id
348
+			// init回调mescroll对象
349
+			vm.$emit('init', vm.mescroll);
350
+			
351
+			// 设置高度
352
+			const sys = uni.getSystemInfoSync();
353
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
354
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
355
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
356
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
357
+			// 使down的bottomOffset生效
358
+			vm.mescroll.setBodyHeight(sys.windowHeight);
359
+
360
+			// 因为使用的是scrollview,这里需自定义scrollTo
361
+			vm.mescroll.resetScrollTo((y, t) => {
362
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
363
+				if(typeof y === 'string'){
364
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
365
+					vm.getClientInfo(function(rect){
366
+						let mescrollTop = rect.top // mescroll到顶部的距离
367
+						let selector;
368
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
369
+							selector = '#'+y // 不带#和. 则默认为id选择器
370
+						}else{
371
+							selector = y
372
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
373
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
374
+								selector = y.split('>>>')[1].trim()
375
+							}
376
+							// #endif
377
+						}
378
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
379
+							if (rect) {
380
+								let curY = vm.mescroll.getScrollTop()
381
+								let top = rect.top - mescrollTop
382
+								top += curY
383
+								if(!vm.isFixed) top -= vm.numTop
384
+								vm.scrollTop = curY;
385
+								vm.$nextTick(function() {
386
+									vm.scrollTop = top
387
+								})
388
+							} else{
389
+								console.error(selector + ' does not exist');
390
+							}
391
+						}).exec()
392
+					})
393
+					return;
394
+				}
395
+				let curY = vm.mescroll.getScrollTop()
396
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
397
+					vm.scrollTop = curY;
398
+					vm.$nextTick(function() {
399
+						vm.scrollTop = y
400
+					})
401
+				} else {
402
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
403
+						vm.scrollTop = step
404
+					}, t)
405
+				}
406
+			})
407
+			
408
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
409
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
410
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
411
+			}
412
+		},
413
+		mounted() {
414
+			// 设置容器的高度
415
+			this.setClientHeight()
416
+		}
417
+	}
418
+</script>
419
+
420
+<style>
421
+	@import "./mescroll-uni.css";
422
+	@import "./components/mescroll-down.css";
423
+	@import './components/mescroll-up.css';
424
+</style>

+ 48
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mixins/mescroll-comp.js Voir le fichier

@@ -0,0 +1,48 @@
1
+/**
2
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期
3
+ */
4
+const MescrollCompMixin = {
5
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
6
+	onPageScroll(e) {
7
+		this.handlePageScroll(e)
8
+	},
9
+	onReachBottom() {
10
+		this.handleReachBottom()
11
+	},
12
+	// 当down的native: true时, 还需传递此方法进到子组件
13
+	onPullDownRefresh(){
14
+		this.handlePullDownRefresh()
15
+	},
16
+	// mescroll-body写在子子子...组件的情况 (多级)
17
+	data() {
18
+		return {
19
+			mescroll: {
20
+				onPageScroll: e=>{
21
+					this.handlePageScroll(e)
22
+				},
23
+				onReachBottom: ()=>{
24
+					this.handleReachBottom()
25
+				},
26
+				onPullDownRefresh: ()=>{
27
+					this.handlePullDownRefresh()
28
+				}
29
+			}
30
+		}
31
+	},
32
+	methods:{
33
+		handlePageScroll(e){
34
+			let item = this.$refs["mescrollItem"];
35
+			if(item && item.mescroll) item.mescroll.onPageScroll(e);
36
+		},
37
+		handleReachBottom(){
38
+			let item = this.$refs["mescrollItem"];
39
+			if(item && item.mescroll) item.mescroll.onReachBottom();
40
+		},
41
+		handlePullDownRefresh(){
42
+			let item = this.$refs["mescrollItem"];
43
+			if(item && item.mescroll) item.mescroll.onPullDownRefresh();
44
+		}
45
+	}
46
+}
47
+
48
+export default MescrollCompMixin;

+ 59
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mixins/mescroll-more-item.js Voir le fichier

@@ -0,0 +1,59 @@
1
+/**
2
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
3
+ */
4
+const MescrollMoreItemMixin = {
5
+	// 支付宝小程序不支持props的mixin,需写在具体的页面中
6
+	// #ifndef MP-ALIPAY || MP-DINGTALK
7
+	props:{
8
+		i: Number, // 每个tab页的专属下标
9
+		index: { // 当前tab的下标
10
+			type: Number,
11
+			default(){
12
+				return 0
13
+			}
14
+		}
15
+	},
16
+	// #endif
17
+	data() {
18
+		return {
19
+			downOption:{
20
+				auto:false // 不自动加载
21
+			},
22
+			upOption:{
23
+				auto:false // 不自动加载
24
+			},
25
+			isInit: false // 当前tab是否已初始化
26
+		}
27
+	},
28
+	watch:{
29
+		// 监听下标的变化
30
+		index(val){
31
+			if (this.i === val && !this.isInit) {
32
+				this.isInit = true; // 标记为true
33
+				this.mescroll && this.mescroll.triggerDownScroll();
34
+			}
35
+		}
36
+	},
37
+	methods: {
38
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
39
+		mescrollInitByRef() {
40
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
41
+				// 字节跳动小程序编辑器不支持一个页面存在相同的ref, 多mescroll的ref需动态生成, 格式为'mescrollRef下标'
42
+				let mescrollRef = this.$refs.mescrollRef || this.$refs['mescrollRef'+this.i];
43
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
44
+			}
45
+		},
46
+		// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
47
+		mescrollInit(mescroll) {
48
+			this.mescroll = mescroll;
49
+			this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
50
+			// 自动加载当前tab的数据
51
+			if(this.i === this.index){
52
+				this.isInit = true; // 标记为true
53
+				this.mescroll.triggerDownScroll();
54
+			}
55
+		},
56
+	}
57
+}
58
+
59
+export default MescrollMoreItemMixin;

+ 74
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/mixins/mescroll-more.js Voir le fichier

@@ -0,0 +1,74 @@
1
+/**
2
+ * mescroll-body写在子组件时, 需通过mescroll的mixins补充子组件缺少的生命周期
3
+ */
4
+const MescrollMoreMixin = {
5
+	data() {
6
+		return {
7
+			tabIndex: 0, // 当前tab下标
8
+			mescroll: {
9
+				onPageScroll: e=>{
10
+					this.handlePageScroll(e)
11
+				},
12
+				onReachBottom: ()=>{
13
+					this.handleReachBottom()
14
+				},
15
+				onPullDownRefresh: ()=>{
16
+					this.handlePullDownRefresh()
17
+				}
18
+			}
19
+		}
20
+	},
21
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
22
+	onPageScroll(e) {
23
+		this.handlePageScroll(e)
24
+	},
25
+	onReachBottom() {
26
+		this.handleReachBottom()
27
+	},
28
+	// 当down的native: true时, 还需传递此方法进到子组件
29
+	onPullDownRefresh(){
30
+		this.handlePullDownRefresh()
31
+	},
32
+	methods:{
33
+		handlePageScroll(e){
34
+			let mescroll = this.getMescroll(this.tabIndex);
35
+			mescroll && mescroll.onPageScroll(e);
36
+		},
37
+		handleReachBottom(){
38
+			let mescroll = this.getMescroll(this.tabIndex);
39
+			mescroll && mescroll.onReachBottom();
40
+		},
41
+		handlePullDownRefresh(){
42
+			let mescroll = this.getMescroll(this.tabIndex);
43
+			mescroll && mescroll.onPullDownRefresh();
44
+		},
45
+		// 根据下标获取对应子组件的mescroll
46
+		getMescroll(i){
47
+			if(!this.mescrollItems) this.mescrollItems = [];
48
+			if(!this.mescrollItems[i]) {
49
+				// v-for中的refs
50
+				let vForItem = this.$refs["mescrollItem"];
51
+				if(vForItem){
52
+					this.mescrollItems[i] = vForItem[i]
53
+				}else{
54
+					// 普通的refs,不可重复
55
+					this.mescrollItems[i] = this.$refs["mescrollItem"+i];
56
+				}
57
+			}
58
+			let item = this.mescrollItems[i]
59
+			return item ? item.mescroll : null
60
+		},
61
+		// 切换tab,恢复滚动条位置
62
+		tabChange(i){
63
+			let mescroll = this.getMescroll(i);
64
+			if(mescroll){
65
+				// 延时(比$nextTick靠谱一些),确保元素已渲染
66
+				setTimeout(()=>{
67
+					mescroll.scrollTo(mescroll.getScrollTop(),0)
68
+				},30)
69
+			}
70
+		}
71
+	}
72
+}
73
+
74
+export default MescrollMoreMixin;

+ 109
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/wxs/mixins.js Voir le fichier

@@ -0,0 +1,109 @@
1
+// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
2
+const WxsMixin = {
3
+	data() {
4
+		return {
5
+			// 传入wxs视图层的数据 (响应式)
6
+			wxsProp: {
7
+				optDown:{}, // 下拉刷新的配置
8
+				scrollTop:0, // 滚动条的距离
9
+				bodyHeight:0, // body的高度
10
+				isDownScrolling:false, // 是否正在下拉刷新中
11
+				isUpScrolling:false, // 是否正在上拉加载中
12
+				isScrollBody:true, // 是否为mescroll-body滚动
13
+				isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
14
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
15
+			},
16
+			
17
+			// 标记调用wxs视图层的方法
18
+			callProp: {
19
+				callType: '', // 方法名
20
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
21
+			},
22
+			
23
+			// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
24
+			// #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
25
+			wxsBiz: {
26
+				//注册列表touchstart事件,用于下拉刷新
27
+				touchstartEvent: e=> {
28
+					this.mescroll.touchstartEvent(e);
29
+				},
30
+				//注册列表touchmove事件,用于下拉刷新
31
+				touchmoveEvent: e=> {
32
+					this.mescroll.touchmoveEvent(e);
33
+				},
34
+				//注册列表touchend事件,用于下拉刷新
35
+				touchendEvent: e=> {
36
+					this.mescroll.touchendEvent(e);
37
+				},
38
+				propObserver(){}, // 抹平wxs的写法
39
+				callObserver(){} // 抹平wxs的写法
40
+			},
41
+			// #endif
42
+			
43
+			// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
44
+			// #ifndef APP-PLUS || H5
45
+			renderBiz: {
46
+				propObserver(){} // 抹平renderjs的写法
47
+			}
48
+			// #endif
49
+		}
50
+	},
51
+	methods: {
52
+		// wxs视图层调用逻辑层的回调
53
+		wxsCall(msg){
54
+			if(msg.type === 'setWxsProp'){
55
+				// 更新wxsProp数据 (值改变才触发更新)
56
+				this.wxsProp = {
57
+					optDown: this.mescroll.optDown,
58
+					scrollTop: this.mescroll.getScrollTop(),
59
+					bodyHeight: this.mescroll.getBodyHeight(),
60
+					isDownScrolling: this.mescroll.isDownScrolling,
61
+					isUpScrolling: this.mescroll.isUpScrolling,
62
+					isUpBoth: this.mescroll.optUp.isBoth,
63
+					isScrollBody:this.mescroll.isScrollBody,
64
+					t: Date.now()
65
+				}
66
+			}else if(msg.type === 'setLoadType'){
67
+				// 设置inOffset,outOffset的状态
68
+				this.downLoadType = msg.downLoadType
69
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
70
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
71
+				// 重置是否加载成功的状态
72
+				this.$set(this.mescroll, 'isDownEndSuccess', null)
73
+			}else if(msg.type === 'triggerDownScroll'){
74
+				// 主动触发下拉刷新
75
+				this.mescroll.triggerDownScroll();
76
+			}else if(msg.type === 'endDownScroll'){
77
+				// 结束下拉刷新
78
+				this.mescroll.endDownScroll();
79
+			}else if(msg.type === 'triggerUpScroll'){
80
+				// 主动触发上拉加载
81
+				this.mescroll.triggerUpScroll(true);
82
+			}
83
+		}
84
+	},
85
+	mounted() {
86
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
87
+		// 配置主动触发wxs显示加载进度的回调
88
+		this.mescroll.optDown.afterLoading = ()=>{
89
+			this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
90
+		}
91
+		// 配置主动触发wxs隐藏加载进度的回调
92
+		this.mescroll.optDown.afterEndDownScroll = ()=>{
93
+			this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
94
+			let delay = 300 + (this.mescroll.optDown.beforeEndDelay || 0)
95
+			setTimeout(()=>{
96
+				if(this.downLoadType === 4 || this.downLoadType === 0){
97
+					this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
98
+				}
99
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
100
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
101
+			}, delay)
102
+		}
103
+		// 初始化wxs的数据
104
+		this.wxsCall({type: 'setWxsProp'})
105
+		// #endif
106
+	}
107
+}
108
+
109
+export default WxsMixin;

+ 92
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/wxs/renderjs.js Voir le fichier

@@ -0,0 +1,92 @@
1
+// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
2
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
3
+// https://uniapp.dcloud.io/frame?id=renderjs
4
+
5
+// 与wxs的me实例一致
6
+var me = {}
7
+
8
+// 初始化window对象的touch事件 (仅初始化一次)
9
+if(window && !window.$mescrollRenderInit){
10
+	window.$mescrollRenderInit = true
11
+	
12
+	
13
+	window.addEventListener('touchstart', function(e){
14
+		if (me.disabled()) return;
15
+		me.startPoint = me.getPoint(e); // 记录起点
16
+	}, {passive: true})
17
+	
18
+	
19
+	window.addEventListener('touchmove', function(e){
20
+		if (me.disabled()) return;
21
+		if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
22
+		
23
+		var curPoint = me.getPoint(e); // 当前点
24
+		var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
25
+		// 向下拉
26
+		if (moveY > 0) {
27
+			// 可下拉的条件
28
+			if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
29
+				
30
+				// 只有touch在mescroll的view上面,才禁止bounce
31
+				var el = e.target;
32
+				var isMescrollTouch = false;
33
+				while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
34
+					var cls = el.classList;
35
+					if (cls && cls.contains('mescroll-render-touch')) {
36
+						isMescrollTouch = true
37
+						break;
38
+					}
39
+					el = el.parentNode; // 继续检查其父元素
40
+				}
41
+				// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
42
+				if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
43
+			}
44
+		}
45
+	}, {passive: false})
46
+}
47
+
48
+/* 获取滚动条的位置 */
49
+me.getScrollTop = function() {
50
+	return me.scrollTop || 0
51
+}
52
+
53
+/* 是否禁用下拉刷新 */
54
+me.disabled = function(){
55
+	return !me.optDown || !me.optDown.use || me.optDown.native
56
+}
57
+
58
+/* 根据点击滑动事件获取第一个手指的坐标 */
59
+me.getPoint = function(e) {
60
+	if (!e) {
61
+		return {x: 0,y: 0}
62
+	}
63
+	if (e.touches && e.touches[0]) {
64
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
65
+	} else if (e.changedTouches && e.changedTouches[0]) {
66
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
67
+	} else {
68
+		return {x: e.clientX,y: e.clientY}
69
+	}
70
+}
71
+
72
+/**
73
+ * 监听逻辑层数据的变化 (实时更新数据)
74
+ */
75
+function propObserver(wxsProp) {
76
+	me.optDown = wxsProp.optDown
77
+	me.scrollTop = wxsProp.scrollTop
78
+	me.isDownScrolling = wxsProp.isDownScrolling
79
+	me.isUpScrolling = wxsProp.isUpScrolling
80
+	me.isUpBoth = wxsProp.isUpBoth
81
+}
82
+
83
+/* 导出模块 */
84
+const renderBiz = {
85
+	data() {
86
+		return {
87
+			propObserver: propObserver,
88
+		}
89
+	}
90
+}
91
+
92
+export default renderBiz;

+ 268
- 0
oa-ui-app/uni_modules/mescroll/components/mescroll-uni/wxs/wxs.wxs Voir le fichier

@@ -0,0 +1,268 @@
1
+// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
2
+// https://uniapp.dcloud.io/frame?id=wxs
3
+// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html 
4
+
5
+// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
6
+var me = {}
7
+
8
+// ------ 自定义下拉刷新动画 start ------
9
+
10
+/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
11
+me.onMoving = function (ins, rate, downHight){
12
+	ins.requestAnimationFrame(function () {
13
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
14
+			'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
15
+			'transform': 'translateY(' + downHight + 'px)',
16
+			'transition': ''
17
+		})
18
+		// 环形进度条
19
+		var progress = ins.selectComponent('.mescroll-wxs-progress')
20
+		progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
21
+	})
22
+}
23
+
24
+/* 显示下拉刷新进度 */
25
+me.showLoading = function (ins){
26
+	me.downHight = me.optDown.offset
27
+	ins.requestAnimationFrame(function () {
28
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
29
+			'will-change': 'auto',
30
+			'transform': 'translateY(' + me.downHight + 'px)',
31
+			'transition': 'transform 300ms'
32
+		})
33
+	})
34
+}
35
+
36
+/* 结束下拉 */
37
+me.endDownScroll = function (ins){
38
+	me.downHight = 0;
39
+	me.isDownScrolling = false;
40
+	ins.requestAnimationFrame(function () {
41
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
42
+			'will-change': 'auto',
43
+			'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
44
+			'transition': 'transform 300ms'
45
+		})
46
+	})
47
+}
48
+
49
+/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
50
+me.clearTransform = function (ins){
51
+	ins.requestAnimationFrame(function () {
52
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
53
+			'will-change': '',
54
+			'transform': '',
55
+			'transition': ''
56
+		})
57
+	})
58
+}
59
+
60
+// ------ 自定义下拉刷新动画 end ------
61
+
62
+/**
63
+ * 监听逻辑层数据的变化 (实时更新数据)
64
+ */
65
+function propObserver(wxsProp) {
66
+	me.optDown = wxsProp.optDown
67
+	me.scrollTop = wxsProp.scrollTop
68
+	me.bodyHeight = wxsProp.bodyHeight
69
+	me.isDownScrolling = wxsProp.isDownScrolling
70
+	me.isUpScrolling = wxsProp.isUpScrolling
71
+	me.isUpBoth = wxsProp.isUpBoth
72
+	me.isScrollBody = wxsProp.isScrollBody
73
+	me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
74
+}
75
+
76
+/**
77
+ * 监听逻辑层数据的变化 (调用wxs的方法)
78
+ */
79
+function callObserver(callProp, oldValue, ins) {
80
+	if (me.disabled()) return;
81
+	if(callProp.callType){
82
+		// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
83
+		if(callProp.callType === 'showLoading'){
84
+			me.showLoading(ins)
85
+		}else if(callProp.callType === 'endDownScroll'){
86
+			me.endDownScroll(ins)
87
+		}else if(callProp.callType === 'clearTransform'){
88
+			me.clearTransform(ins)
89
+		}
90
+	}
91
+}
92
+
93
+/**
94
+ * touch事件
95
+ */
96
+function touchstartEvent(e, ins) {
97
+	me.downHight = 0; // 下拉的距离
98
+	me.startPoint = me.getPoint(e); // 记录起点
99
+	me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
100
+	me.startAngle = 0; // 初始角度
101
+	me.lastPoint = me.startPoint; // 重置上次move的点
102
+	me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
103
+	me.inTouchend = false; // 标记不是touchend
104
+	
105
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
106
+}
107
+
108
+function touchmoveEvent(e, ins) {
109
+	var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
110
+	
111
+	if (me.disabled()) return isPrevent;
112
+	
113
+	var scrollTop = me.getScrollTop(); // 当前滚动条的距离
114
+	var curPoint = me.getPoint(e); // 当前点
115
+	
116
+	var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
117
+	
118
+	// 向下拉 && 在顶部
119
+	// mescroll-body,直接判定在顶部即可
120
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
121
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
122
+	if (moveY > 0 && (
123
+			(me.isScrollBody && scrollTop <= 0)
124
+			||
125
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
126
+		)) {
127
+		// 可下拉的条件
128
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
129
+				me.isUpBoth))) {
130
+	
131
+			// 下拉的角度是否在配置的范围内
132
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
133
+			if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
134
+	
135
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
136
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
137
+				me.inTouchend = true; // 标记执行touchend
138
+				touchendEvent(e, ins); // 提前触发touchend
139
+				return isPrevent;
140
+			}
141
+			
142
+			isPrevent = false // 小程序是return false
143
+	
144
+			var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
145
+	
146
+			// 下拉距离  < 指定距离
147
+			if (me.downHight < me.optDown.offset) {
148
+				if (me.movetype !== 1) {
149
+					me.movetype = 1; // 加入标记,保证只执行一次
150
+					// me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
151
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
152
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
153
+				}
154
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
155
+	
156
+				// 指定距离  <= 下拉距离
157
+			} else {
158
+				if (me.movetype !== 2) {
159
+					me.movetype = 2; // 加入标记,保证只执行一次
160
+					// me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
161
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
162
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
163
+				}
164
+				if (diff > 0) { // 向下拉
165
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
166
+				} else { // 向上收
167
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
168
+				}
169
+			}
170
+			
171
+			me.downHight = Math.round(me.downHight) // 取整
172
+			var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
173
+			// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
174
+			me.onMoving(ins, rate, me.downHight)
175
+		}
176
+	}
177
+	
178
+	me.lastPoint = curPoint; // 记录本次移动的点
179
+	
180
+	return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
181
+}
182
+
183
+function touchendEvent(e, ins) {
184
+	// 如果下拉区域高度已改变,则需重置回来
185
+	if (me.isMoveDown) {
186
+		if (me.downHight >= me.optDown.offset) {
187
+			// 符合触发刷新的条件
188
+			me.downHight = me.optDown.offset; // 更新下拉区域高度
189
+			// me.triggerDownScroll();
190
+			me.callMethod(ins, {type: 'triggerDownScroll'})
191
+		} else {
192
+			// 不符合的话 则重置
193
+			me.downHight = 0;
194
+			// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
195
+			me.callMethod(ins, {type: 'endDownScroll'})
196
+		}
197
+		me.movetype = 0;
198
+		me.isMoveDown = false;
199
+	} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
200
+		var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
201
+		// 上滑
202
+		if (isScrollUp) {
203
+			// 需检查滑动的角度
204
+			var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
205
+			if (angle > 80) {
206
+				// 检查并触发上拉
207
+				// me.triggerUpScroll(true);
208
+				me.callMethod(ins, {type: 'triggerUpScroll'})
209
+			}
210
+		}
211
+	}
212
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
213
+}
214
+
215
+/* 是否禁用下拉刷新 */
216
+me.disabled = function(){
217
+	return !me.optDown || !me.optDown.use || me.optDown.native
218
+}
219
+
220
+/* 根据点击滑动事件获取第一个手指的坐标 */
221
+me.getPoint = function(e) {
222
+	if (!e) {
223
+		return {x: 0,y: 0}
224
+	}
225
+	if (e.touches && e.touches[0]) {
226
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
227
+	} else if (e.changedTouches && e.changedTouches[0]) {
228
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
229
+	} else {
230
+		return {x: e.clientX,y: e.clientY}
231
+	}
232
+}
233
+
234
+/* 计算两点之间的角度: 区间 [0,90]*/
235
+me.getAngle = function (p1, p2) {
236
+	var x = Math.abs(p1.x - p2.x);
237
+	var y = Math.abs(p1.y - p2.y);
238
+	var z = Math.sqrt(x * x + y * y);
239
+	var angle = 0;
240
+	if (z !== 0) {
241
+		angle = Math.asin(y / z) / Math.PI * 180;
242
+	}
243
+	return angle
244
+}
245
+
246
+/* 获取滚动条的位置 */
247
+me.getScrollTop = function() {
248
+	return me.scrollTop || 0
249
+}
250
+
251
+/* 获取body的高度 */
252
+me.getBodyHeight = function() {
253
+	return me.bodyHeight || 0;
254
+}
255
+
256
+/* 调用逻辑层的方法 */
257
+me.callMethod = function(ins, param) {
258
+	if(ins) ins.callMethod('wxsCall', param)
259
+}
260
+
261
+/* 导出模块 */
262
+module.exports = {
263
+	propObserver: propObserver,
264
+	callObserver: callObserver,
265
+	touchstartEvent: touchstartEvent,
266
+	touchmoveEvent: touchmoveEvent,
267
+	touchendEvent: touchendEvent
268
+}

+ 17
- 0
oa-ui-app/uni_modules/mescroll/main.js Voir le fichier

@@ -0,0 +1,17 @@
1
+import Vue from 'vue'
2
+import App from './App'
3
+
4
+// 注册全局组件
5
+import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"
6
+import MescrollUni from "@/components/mescroll-uni/mescroll-uni.vue"
7
+Vue.component('mescroll-body', MescrollBody)
8
+Vue.component('mescroll-uni', MescrollUni)
9
+
10
+Vue.config.productionTip = false
11
+
12
+App.mpType = 'app'
13
+
14
+const app = new Vue({
15
+    ...App
16
+})
17
+app.$mount()

+ 68
- 0
oa-ui-app/uni_modules/mescroll/manifest.json Voir le fichier

@@ -0,0 +1,68 @@
1
+{
2
+    "name" : "mescroll高性能的下拉刷新上拉加载组件",
3
+    "appid" : "",
4
+    "description" : "mescroll的uni版本 -支持uni-app的下拉刷新上拉加载组件,支持原生页面和局部区域滚动",
5
+    "versionName" : "1.0.0",
6
+    "versionCode" : "100",
7
+    "transformPx" : false,
8
+    "app-plus" : {
9
+        /* 5+App特有相关 */
10
+        "usingComponents" : true,
11
+        "modules" : {},
12
+        /* 模块配置 */
13
+        "distribute" : {
14
+            /* 应用发布信息 */
15
+            "android" : {
16
+                /* android打包配置 */
17
+                "permissions" : [
18
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
19
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
20
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
21
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
22
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
23
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
24
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
25
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
26
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
27
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
28
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
29
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
30
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
31
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
32
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
33
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
34
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
35
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
36
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
37
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
38
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
39
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
40
+                ]
41
+            },
42
+            "ios" : {},
43
+            /* ios打包配置 */
44
+            "sdkConfigs" : {}
45
+        }
46
+    },
47
+    /* SDK配置 */
48
+    "quickapp" : {},
49
+    /* 快应用特有相关 */
50
+    "mp-weixin" : {
51
+        /* 小程序特有相关 */
52
+        "appid" : "",
53
+        "setting" : {
54
+            "urlCheck" : true
55
+        },
56
+        "usingComponents" : true
57
+    },
58
+    "mp-alipay" : {
59
+        "usingComponents" : true
60
+    },
61
+    "mp-baidu" : {
62
+        "usingComponents" : true
63
+    },
64
+    "mp-toutiao" : {
65
+        "usingComponents" : true
66
+    },
67
+    "vueVersion" : "2"
68
+}

+ 26
- 0
oa-ui-app/uni_modules/mescroll/package.json Voir le fichier

@@ -0,0 +1,26 @@
1
+{
2
+    "uni-app": {
3
+        "scripts": {
4
+            "mp-dingtalk": {
5
+                "title": "钉钉小程序",
6
+                "env": {
7
+                    "UNI_PLATFORM": "mp-alipay"
8
+                },
9
+                "define": {
10
+                    "MP-DINGTALK": true
11
+                }
12
+            }
13
+        }
14
+    },
15
+    "id": "nx-mescroll",
16
+    "name": "mescroll高性能的下拉刷新上拉加载组件",
17
+    "version": "1.3.3",
18
+    "description": "mescroll - 支持uni-app的下拉刷新和上拉加载的组件,支持原生页面和局部区域滚动",
19
+    "keywords": [
20
+        "mescroll",
21
+        "下拉",
22
+        "刷新",
23
+        "上拉",
24
+        "分页"
25
+    ]
26
+}

+ 161
- 0
oa-ui-app/uni_modules/mescroll/pages.json Voir le fichier

@@ -0,0 +1,161 @@
1
+{
2
+    "pages" : [
3
+        //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
4
+        {
5
+            "path" : "pages/index/index",
6
+            "style" : {
7
+                "navigationBarTitleText" : "mescroll"
8
+            }
9
+        },
10
+        {
11
+            "path" : "pages/base/list-msg",
12
+            "style" : {
13
+				"navigationBarTitleText" : "list-msg"
14
+			}
15
+        },
16
+		{
17
+		    "path" : "pages/base/list-news",
18
+		    "style" : {
19
+				"navigationBarTitleText" : "list-news"
20
+			}
21
+		},
22
+        {
23
+            "path" : "pages/base/list-products",
24
+            "style" : {
25
+				"navigationBarTitleText" : "list-products"
26
+			}
27
+        },
28
+        {
29
+            "path" : "pages/base/mescroll-options",
30
+            "style" : {
31
+				"navigationBarTitleText" : "mescroll-options",
32
+				"backgroundColorTop": "#E75A7C" // iOS APP真机bounce回弹区域颜色
33
+			}
34
+        },
35
+		{
36
+		    "path" : "pages/base/sticky",
37
+		    "style" : {
38
+				"navigationBarTitleText" : "sticky吸顶悬浮 (切换刷新列表,原生css实现)"
39
+			}
40
+		},
41
+		{
42
+		    "path" : "pages/base/sticky-data",
43
+		    "style" : {
44
+				"navigationBarTitleText" : "sticky吸顶悬浮 (缓存列表数据,原生css实现)"
45
+			}
46
+		},
47
+		{
48
+		    "path" : "pages/base/sticky-scroll",
49
+		    "style" : {
50
+				"navigationBarTitleText" : "sticky吸顶悬浮 (切换刷新列表,监听滚动实现)"
51
+			}
52
+		},
53
+		{
54
+		    "path" : "pages/base/sticky-scroll-data",
55
+		    "style" : {
56
+				"navigationBarTitleText" : "sticky吸顶悬浮 (缓存列表数据,监听滚动实现)"
57
+			}
58
+		},
59
+		{
60
+		    "path" : "pages/base/sticky-uni",
61
+		    "style" : {
62
+				"navigationBarTitleText" : "sticky吸顶悬浮 (mescroll-uni使用sticky)"
63
+			}
64
+		},
65
+        {
66
+            "path" : "pages/base/mescroll-comp",
67
+            "style" : {
68
+				"navigationBarTitleText" : "mescroll-body写在子组件"
69
+			}
70
+        },
71
+		{
72
+            "path" : "pages/base/mescroll-one",
73
+            "style" : {
74
+				"navigationBarTitleText" : "mescroll-one"
75
+			}
76
+        },
77
+        {
78
+            "path" : "pages/base/mescroll-more",
79
+            "style" : {
80
+				"navigationBarTitleText" : "mescroll-more"
81
+			}
82
+        },
83
+        {
84
+            "path" : "pages/base/list-search",
85
+            "style" : {
86
+				"navigationBarTitleText" : "list-search"
87
+			}
88
+        },
89
+		{
90
+		    "path" : "pages/base/mescroll-uni-part",
91
+		    "style" : {
92
+				"navigationBarTitleText" : "mescroll-uni-part",
93
+				"disableScroll": true, // mescroll-uni需禁止滚动, 解决scroll-view在Android小程序卡顿的问题
94
+				"app-plus" : {
95
+					"bounce" : "none" // mescroll-uni需取消iOS回弹和安卓顶部底部的半月形阴影
96
+				},
97
+				"mp-alipay":{"allowsBounceVertical":"NO"} // mescroll-uni需取消支付宝和钉钉小程序自带的回弹
98
+			}
99
+		},
100
+		{
101
+		    "path" : "pages/base/mescroll-body-part",
102
+		    "style" : {
103
+				"navigationBarTitleText" : "mescroll-body-part"
104
+			}
105
+		},
106
+		{
107
+		    "path" : "pages/base/mescroll-native",
108
+		    "style" : {
109
+				"navigationBarTitleText" : "mescroll-native",
110
+				"enablePullDownRefresh" : true, // 开启系统自带的下拉刷新
111
+				//ifdef MP 支持条件编译,如您可以配置小程序端使用系统自带的,其他平台使用mescroll的下拉样式
112
+				//"enablePullDownRefresh" : true
113
+				//endif
114
+				//ifndef MP
115
+				//"enablePullDownRefresh" : false
116
+				//endif
117
+				"mp-alipay":{"allowsBounceVertical":"YES"} // 开启支付宝和钉钉小程序自带的回弹
118
+			}
119
+		},
120
+        {
121
+            "path" : "pages/base/mescroll-uni",
122
+            "style" : {
123
+				"navigationBarTitleText" : "mescroll-uni",
124
+				"disableScroll": true, // mescroll-uni需禁止滚动, 解决scroll-view在Android小程序卡顿的问题
125
+				"app-plus" : {
126
+					"bounce" : "none" // mescroll-uni需取消iOS回弹和安卓顶部底部的半月形阴影
127
+				},
128
+				"mp-alipay":{"allowsBounceVertical":"NO"} // mescroll-uni需取消支付宝和钉钉小程序自带的回弹
129
+			}
130
+        },
131
+		{
132
+		    "path" : "pages/intermediate/mescroll-swiper",
133
+		    "style" : {
134
+				"navigationBarTitleText" : "轮播菜单导航 mescroll-swiper.vue",
135
+				"disableScroll": true, // mescroll-uni需禁止滚动, 解决scroll-view在Android小程序卡顿的问题
136
+				"app-plus" : {
137
+					"bounce" : "none" // mescroll-uni需取消iOS回弹和安卓顶部底部的半月形阴影
138
+				},
139
+				"mp-alipay":{"allowsBounceVertical":"NO"} // mescroll-uni需取消支付宝和钉钉小程序自带的回弹
140
+			}
141
+		},
142
+		{
143
+            "path" : "pages/intermediate/beibei",
144
+            "style" : {
145
+				"navigationBarTitleText" : "贝贝 mescroll-beibei.vue"
146
+			}
147
+        },
148
+        {
149
+            "path" : "pages/intermediate/xinlang",
150
+            "style" : {
151
+				"navigationBarTitleText" : "新浪微博 mescroll-xinlang.vue"
152
+			}
153
+        }
154
+    ],
155
+    "globalStyle" : {
156
+		"backgroundColorTop":"#FFFFFF", // iOS APP真机bounce回弹区域默认灰色,需重置为白色
157
+		"mp-alipay":{"allowsBounceVertical":"NO"}, // 支付宝和钉钉小程序取消自带的回弹
158
+        "navigationBarTextStyle" : "black",
159
+        "navigationBarBackgroundColor" : "#fff"
160
+    }
161
+}

+ 125
- 0
oa-ui-app/uni_modules/mescroll/pages/base/list-msg.vue Voir le fichier

@@ -0,0 +1,125 @@
1
+<template>
2
+	<!-- 需配置bottom的偏移量, 用于底部留白 -->
3
+	<mescroll-body ref="mescrollRef" bottom="50%" @init="mescrollInit" :down="downOption" @down="downCallback" :up="upOption">
4
+		<!-- 无更多消息 -->
5
+		<view v-if="isEnd" class="msg-end">没有更多消息了</view>
6
+		
7
+		<!-- 消息列表 (必须配置id,以便定位) -->
8
+		<view class="msg" v-for="msg in msgList" :key="msg.id" :id="msg.VIEW_ID" :class="[msg.id%2==0 ? 'right' : 'left']">
9
+			<view class="msg-warp">
10
+				<view class="msg-title">{{msg.title}}</view>
11
+				<view class="msg-content">{{msg.content}}</view>
12
+			</view>
13
+		</view>
14
+	</mescroll-body>
15
+</template>
16
+
17
+<script>
18
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
19
+	import {apiMsgList} from "@/api/mock.js"
20
+	
21
+	export default {
22
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
23
+		data() {
24
+			return {
25
+				downOption:{
26
+					autoShowLoading: true, // 显示下拉刷新的进度条
27
+					textColor: "#FF8095" // 下拉刷新的文本颜色
28
+				},
29
+				upOption: {
30
+					use: false, // 禁止上拉
31
+					toTop: {
32
+						src: '' // 不显示回到顶部按钮
33
+					}
34
+				},
35
+				pageNum: 1, // 页码
36
+				pageSize: 10, // 页长
37
+				isEnd: false, // 是否无消息
38
+				msgList: []
39
+			}
40
+		},
41
+		methods: {
42
+			/*下拉刷新的回调 */
43
+			downCallback() {
44
+				//联网加载数据
45
+				apiMsgList(this.pageNum, this.pageSize).then(data => {
46
+					// 需自行维护页码
47
+					this.pageNum ++;
48
+					// 先隐藏下拉刷新的状态
49
+					this.mescroll.endSuccess();
50
+					// 不满一页,说明已经无更多消息 (建议根据您实际接口返回的总页码数,总消息量,是否有消息的字段来判断)
51
+					if(data.length < this.pageSize){
52
+						this.isEnd = true; // 标记已无更多消息
53
+						this.mescroll.lockDownScroll(true); // 锁定下拉
54
+					}
55
+					// 生成VIEW_ID,大写,避免污染源数据
56
+					data.forEach(val=>{
57
+						val.VIEW_ID = "msg" + val.id // 不以数字开头
58
+					})
59
+					
60
+					// 获取当前最顶部的VIEW_ID (注意是写在data.concat前面)
61
+					let topMsg = this.msgList[0]
62
+					
63
+					//设置列表数据
64
+					this.msgList = data.concat(this.msgList); // 注意不是this.msgList.concat
65
+					
66
+					this.$nextTick(()=>{
67
+						if(this.pageNum <= 2){
68
+							// 第一页直接滚动到底部 ( this.pageNum已在前面加1 )
69
+							this.mescroll.scrollTo(99999, 0)
70
+						}else if(topMsg){
71
+							// 保持顶部消息的位置
72
+							let view = uni.createSelectorQuery().select('#'+topMsg.VIEW_ID);
73
+							view.boundingClientRect(v => {
74
+								console.log("节点离页面顶部的距离=" + v.top);
75
+								this.mescroll.scrollTo(v.top - 100, 0) // 减去上偏移量100
76
+							}).exec();
77
+						}
78
+					})
79
+					
80
+				}).catch(()=>{
81
+					this.pageNum --; // 联网失败,必须回减页码
82
+					this.mescroll.endErr(); // 隐藏下拉刷新的状态
83
+				})
84
+			}
85
+		}
86
+	}
87
+</script>
88
+
89
+<style lang="scss">
90
+	/* 无更多消息 */
91
+	.msg-end{
92
+		padding: 40rpx 0;
93
+		font-size: 24rpx;
94
+		text-align: center;
95
+		color: #FF8095;
96
+	}
97
+	/*消息列表*/
98
+	.msg{
99
+		margin: 30rpx;
100
+		&.right{
101
+			text-align: right;
102
+		}
103
+		&.left{
104
+			text-align: left;
105
+		}
106
+		.msg-warp{
107
+			width: 50%;
108
+			display: inline-block;
109
+			padding: 30rpx;
110
+			border-radius: 30rpx;
111
+			background-color: #FF8095;
112
+			color: #fff;
113
+			.msg-title{
114
+				margin-left: -12rpx;
115
+				font-size: 32rpx;
116
+				text-align: left;
117
+			}
118
+			.msg-content{
119
+				font-size: 26rpx;
120
+				margin-top: 10rpx;
121
+				text-align: left;
122
+			}
123
+		}
124
+	}
125
+</style>

+ 89
- 0
oa-ui-app/uni_modules/mescroll/pages/base/list-news.vue Voir le fichier

@@ -0,0 +1,89 @@
1
+<template>
2
+	<mescroll-body ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" @up="upCallback">
3
+		<view class="notice">本Demo的下拉刷新: 添加新数据到列表顶部</view>
4
+		<view class="news-li" v-for="news in dataList" :key="news.id">
5
+			<view>{{news.title}}</view>
6
+			<view class="new-content">{{news.content}}</view>
7
+		</view>
8
+	</mescroll-body>
9
+</template>
10
+
11
+<script>
12
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
13
+	import {apiNewList} from "@/api/mock.js"
14
+	
15
+	export default {
16
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
17
+		data() {
18
+			return {
19
+				downOption: {
20
+					auto: false //是否在初始化后,自动执行downCallback; 默认true
21
+				},
22
+				dataList: []
23
+			}
24
+		},
25
+		methods: {
26
+			/*下拉刷新的回调 */
27
+			downCallback() {
28
+				//联网加载数据
29
+				apiNewList().then(data => {
30
+					//联网成功的回调,隐藏下拉刷新的状态
31
+					this.mescroll.endSuccess();
32
+					//设置列表数据
33
+					this.dataList.unshift(data[0]);
34
+				}).catch(()=>{
35
+					//联网失败的回调,隐藏下拉刷新的状态
36
+					this.mescroll.endErr();
37
+				})
38
+			},
39
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
40
+			upCallback(page) {
41
+				//联网加载数据
42
+				apiNewList(page.num, page.size).then(curPageData=>{
43
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
44
+					//mescroll会根据传的参数,自动判断列表如果无任何数据,则提示空;列表无下一页数据,则提示无更多数据;
45
+					
46
+					//方法一(推荐): 后台接口有返回列表的总页数 totalPage
47
+					//this.mescroll.endByPage(curPageData.length, totalPage); //必传参数(当前页的数据个数, 总页数)
48
+					
49
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
50
+					//this.mescroll.endBySize(curPageData.length, totalSize); //必传参数(当前页的数据个数, 总数据量)
51
+					
52
+					//方法三(推荐): 您有其他方式知道是否有下一页 hasNext
53
+					//this.mescroll.endSuccess(curPageData.length, hasNext); //必传参数(当前页的数据个数, 是否有下一页true/false)
54
+					
55
+					//方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据.
56
+					this.mescroll.endSuccess(curPageData.length);
57
+					
58
+					//设置列表数据
59
+					this.dataList=this.dataList.concat(curPageData);
60
+				}).catch(()=>{
61
+					//联网失败, 结束加载
62
+					this.mescroll.endErr();
63
+				})
64
+			}
65
+		}
66
+	}
67
+</script>
68
+
69
+<style>
70
+	/*说明*/
71
+	.notice{
72
+		font-size: 30upx;
73
+		padding: 40upx 0;
74
+		border-bottom: 1upx solid #eee;
75
+		text-align: center;
76
+	}
77
+	/*展示上拉加载的数据列表*/
78
+	.news-li{
79
+		font-size: 32upx;
80
+		padding: 32upx;
81
+		border-bottom: 1upx solid #eee;
82
+	}
83
+	.news-li .new-content{
84
+		font-size: 28upx;
85
+		margin-top: 10upx;
86
+		margin-left: 20upx;
87
+		color: #666;
88
+	}
89
+</style>

+ 78
- 0
oa-ui-app/uni_modules/mescroll/pages/base/list-products.vue Voir le fichier

@@ -0,0 +1,78 @@
1
+<template>
2
+	 <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback">
3
+		<view class="notice-warp">
4
+			<view class="notice">商品的名称价格销量随时会变动,也可能会下架删除</view>
5
+			<view class="notice">所以本Demo的下拉刷新会重置列表数据</view>
6
+			<view class="btn-change" @click="isGoodsEdit=true">{{isGoodsEdit ? "已模拟后台修改信息, 请下拉刷新" : "点击模拟后台修改商品信息"}}</view>
7
+		</view>
8
+		<good-list :list="goods"></good-list>
9
+	</mescroll-body>
10
+</template>
11
+
12
+<script>
13
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
14
+	import {apiGoods} from "@/api/mock.js"
15
+	
16
+	export default {
17
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
18
+		data() {
19
+			return {
20
+				goods: [], // 数据列表
21
+				isGoodsEdit: false  // 是否加载编辑后的数据
22
+			}
23
+		},
24
+		methods: {
25
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
26
+			upCallback(page) {
27
+				//联网加载数据
28
+				apiGoods(page.num, page.size, this.isGoodsEdit).then(curPageData=>{
29
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
30
+					//mescroll会根据传的参数,自动判断列表如果无任何数据,则提示空;列表无下一页数据,则提示无更多数据;
31
+
32
+					//方法一(推荐): 后台接口有返回列表的总页数 totalPage
33
+					//this.mescroll.endByPage(curPageData.length, totalPage); //必传参数(当前页的数据个数, 总页数)
34
+
35
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
36
+					//this.mescroll.endBySize(curPageData.length, totalSize); //必传参数(当前页的数据个数, 总数据量)
37
+
38
+					//方法三(推荐): 您有其他方式知道是否有下一页 hasNext
39
+					//this.mescroll.endSuccess(curPageData.length, hasNext); //必传参数(当前页的数据个数, 是否有下一页true/false)
40
+
41
+					//方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据
42
+					this.mescroll.endSuccess(curPageData.length);
43
+
44
+					//设置列表数据
45
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
46
+					this.goods=this.goods.concat(curPageData); //追加新数据
47
+				}).catch(()=>{
48
+					//联网失败, 结束加载
49
+					this.mescroll.endErr();
50
+				})
51
+			}
52
+		}
53
+	}
54
+</script>
55
+
56
+<style>
57
+	/*说明*/
58
+	.notice-warp{
59
+		font-size: 26upx;
60
+		padding: 40upx 0;
61
+		border-bottom: 1upx solid #eee;
62
+		text-align: center;
63
+	}
64
+	.notice-warp .notice{
65
+		color:#555;
66
+	}
67
+	.notice-warp .btn-change{
68
+		display: inline-block;
69
+		margin-top: 28upx;
70
+		padding: 6upx 16upx;
71
+		border: 1upx solid #FF6990;
72
+		border-radius: 40upx;
73
+		color: #FF6990;
74
+	}
75
+	.notice-warp .btn-change:active{
76
+		opacity: .5;
77
+	}
78
+</style>

+ 106
- 0
oa-ui-app/uni_modules/mescroll/pages/base/list-search.vue Voir le fichier

@@ -0,0 +1,106 @@
1
+<template>
2
+	 <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" :up="upOption" @up="upCallback">
3
+		<view class="item">
4
+			<text class="tip">热门搜索:</text>
5
+			<text class="hot-word" @click="doSearch('奶粉')">奶粉</text>
6
+			<text class="hot-word" @click="doSearch('面霜')">面霜</text>
7
+			<text class="hot-word" @click="doSearch('图书')">图书</text>
8
+		</view>
9
+		<view class="item">
10
+			<text class="tip">关键词:</text>
11
+			<input class="word-input" placeholder="请输入搜索关键词" v-model="curWord" @input="inputWord"/>
12
+		</view>
13
+		<good-list :list="goods"></good-list>
14
+	</mescroll-body>
15
+</template>
16
+
17
+<script>
18
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
19
+	import {apiSearch} from "@/api/mock.js"
20
+	
21
+	export default {
22
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
23
+		data() {
24
+			return {
25
+				upOption: {
26
+					// auto: false, //是否在初始化后,自动执行上拉回调callback; 默认true
27
+					// page: {
28
+					// 	num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
29
+					// 	size: 10 // 每页数据的数量
30
+					// }
31
+					noMoreSize: 3, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
32
+					empty: {
33
+						tip: '~ 搜索无结果 ~' // 提示
34
+					}
35
+				},
36
+				goods: [], // 数据列表
37
+				curWord:"" //当前搜索关键词
38
+			}
39
+		},
40
+		methods: {
41
+			// 输入监听
42
+			inputWord(e){
43
+				// this.curWord = e.detail.value // 已使用v-model,无需再次赋值
44
+				// 节流,避免输入过快多次请求
45
+				this.searchTimer && clearTimeout(this.searchTimer)
46
+				this.searchTimer = setTimeout(()=>{
47
+					this.doSearch(this.curWord)
48
+				},300)
49
+			},
50
+			// 搜索
51
+			doSearch(word){
52
+				this.curWord = word
53
+				this.goods = []; // 先清空列表,显示加载进度
54
+				this.mescroll.resetUpScroll();
55
+			},
56
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
57
+			upCallback(page) {
58
+				//联网加载数据
59
+				apiSearch(page.num, page.size, this.curWord).then(curPageData=>{
60
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
61
+					this.mescroll.endSuccess(curPageData.length);
62
+					//如果是第一页需手动制空列表
63
+					if(page.num == 1) this.goods = [];
64
+					//追加新数据
65
+					this.goods=this.goods.concat(curPageData);
66
+				}).catch(()=>{
67
+					//联网失败, 结束加载
68
+					this.mescroll.endErr();
69
+				})
70
+			}
71
+		}
72
+	}
73
+</script>
74
+
75
+<style>
76
+	/*关键词搜索*/
77
+	.item{
78
+		padding: 20rpx;
79
+	}
80
+	.tip{
81
+		font-size: 30rpx;
82
+		vertical-align: middle;
83
+	}
84
+	.hot-word{
85
+		font-size: 24rpx;
86
+		margin-left: 30rpx;
87
+		padding: 6rpx 40rpx;
88
+		border: 2rpx solid #FF6990;
89
+		border-radius: 100rpx;
90
+		vertical-align: middle;
91
+		color: #FF6990;
92
+	}
93
+	.word-input{
94
+		display: inline-block;
95
+		width: 60%;
96
+		height: 50rpx;
97
+		line-height: 50rpx;
98
+		font-size: 24rpx;
99
+		margin-left: 30rpx;
100
+		border: 2rpx solid #18B4FE;
101
+		border-radius: 60rpx;
102
+		text-align: center;
103
+		background-color: #fff;
104
+		vertical-align: middle;
105
+	}
106
+</style>

+ 119
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-body-part.vue Voir le fichier

@@ -0,0 +1,119 @@
1
+<template>
2
+	<!-- mescroll-body本质是原生page的滚动,无法像mescroll-uni那样用flex布局嵌在某个view中使用局部区域滚动, 但是可以通过fixed定位其他元素来实现"局部区域滚动"-->
3
+	<view>
4
+		<!-- 顶部 fixed定位 -->
5
+		<view class="top-warp"> 
6
+			<view>顶部区域</view>
7
+			<view style="font-size: 24rpx;">mescroll-body 通过fixed定位其他元素,实现"局部区域滚动"</view>
8
+		</view>
9
+		
10
+		<!-- 左边 fixed定位 -->
11
+		<scroll-view class="left-warp" :scroll-y="true">
12
+			<view class="tab" :class="{active:i==tabIndex}" v-for="(tab,i) in tabs" :key="i" @click="tabChange(i)">{{tab}}</view>
13
+		</scroll-view>
14
+		
15
+		<!-- mescroll-body跟随page滚动, 不可fixed定位, 可设置 top, bottom, topbar, bottombar, safearea的偏移量-->
16
+		<mescroll-body ref="mescrollRef" top="88" bottom="100" @init="mescrollInit" @down="downCallback" @up="upCallback">
17
+			<good-list :list="goods"></good-list>
18
+		</mescroll-body>
19
+		
20
+		<!-- 底部 fixed定位 -->
21
+		<view class="bottom-warp"> 底部区域 </view>
22
+	</view>
23
+</template>
24
+
25
+
26
+<script>
27
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
28
+	import {apiSearch} from "@/api/mock.js"
29
+	
30
+	export default {
31
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
32
+		data() {
33
+			return {
34
+				goods: [], // 数据列表
35
+				tabs: ['全部', '奶粉', '面膜', '图书', '果汁', '奶瓶', '美素', '花王', '韩蜜', '口红', '毛巾', '玩具', '衣服'],
36
+				tabIndex: 0 // tab下标
37
+			}
38
+		},
39
+		methods: {
40
+			upCallback(page) {
41
+				//联网加载数据
42
+				let keyword = this.tabs[this.tabIndex]
43
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
44
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
45
+					this.mescroll.endSuccess(curPageData.length);
46
+					//设置列表数据
47
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
48
+					this.goods=this.goods.concat(curPageData); //追加新数据
49
+				}).catch(()=>{
50
+					//联网失败, 结束加载
51
+					this.mescroll.endErr();
52
+				})
53
+			},
54
+			// 切换菜单
55
+			tabChange(i){
56
+				if(this.tabIndex != i){
57
+					this.tabIndex = i
58
+					this.goods = []; // 先置空列表,显示加载进度条
59
+					this.mescroll.resetUpScroll(); // 重置列表数据
60
+				}
61
+			}
62
+		}
63
+	}
64
+</script>
65
+
66
+
67
+<style lang="scss">
68
+	/* 顶部 fixed定位*/
69
+	.top-warp{
70
+		z-index: 200;
71
+		position: fixed;
72
+		top: var(--window-top);
73
+		left: 0;
74
+		width: 100%;
75
+		height: 88rpx;
76
+		padding-top: 10rpx;
77
+		font-size: 28rpx;
78
+		text-align: center;
79
+		background-color: #CFE0DA;
80
+	}
81
+	
82
+	/* 左边 fixed定位*/
83
+	.left-warp{
84
+		z-index: 100;
85
+		position: fixed;
86
+		top: var(--window-top);
87
+		left: 0;
88
+		bottom: 100rpx;
89
+		width: 180rpx;
90
+		padding-top: 88rpx;
91
+		background-color: #eee;
92
+		.tab{
93
+			font-size: 28rpx;
94
+			padding: 28rpx;
95
+			&.active{
96
+				background-color: #fff;
97
+			}
98
+		}
99
+	}
100
+	
101
+	// 设置padding
102
+	.mescroll-body,
103
+	/deep/.mescroll-body{
104
+		padding-left: 180rpx;
105
+	}
106
+	
107
+	/* 底部 fixed定位*/
108
+	.bottom-warp{
109
+		z-index: 200;
110
+		position: fixed;
111
+		left: 0;
112
+		bottom: 0;
113
+		width: 100%;
114
+		height: 100rpx;
115
+		line-height: 100rpx;
116
+		text-align: center;
117
+		background-color: #FF6990;
118
+	}
119
+</style>

+ 37
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-comp-item.vue Voir le fichier

@@ -0,0 +1,37 @@
1
+<template>
2
+	<!-- 当mescroll-body写在子组件时,父页面需引入mescroll-comp.js的mixins -->
3
+	<mescroll-body ref="mescrollRef" top="100" @init="mescrollInit" @down="downCallback" @up="upCallback">
4
+		<!-- 数据列表 -->
5
+		<good-list :list="goods"></good-list>
6
+	</mescroll-body>
7
+</template>
8
+
9
+<script>
10
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
11
+	import {apiGoods} from "@/api/mock.js"
12
+	
13
+	export default {
14
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
15
+		data() {
16
+			return {
17
+				goods: []
18
+			}
19
+		},
20
+		methods: {
21
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
22
+			upCallback(page) {
23
+				//联网加载数据
24
+				apiGoods(page.num, page.size).then(curPageData=>{
25
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
26
+					this.mescroll.endSuccess(curPageData.length);
27
+					//设置列表数据
28
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
29
+					this.goods=this.goods.concat(curPageData); //追加新数据
30
+				}).catch(()=>{
31
+					//联网失败, 结束加载
32
+					this.mescroll.endErr();
33
+				})
34
+			}
35
+		}
36
+	}
37
+</script>

+ 57
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-comp.vue Voir le fichier

@@ -0,0 +1,57 @@
1
+<template>
2
+	<!-- 注: 本示例仅演示mescroll-body写在一层子组件的情况, 其实同样适用mescroll-body写在子子..子组件的情况, 只需每一层都要写上第一步和第二步的代码即可 -->
3
+	 <view>
4
+		<view class="notice-warp">
5
+			<view class="notice">mescroll-body是原生页面的滚动,子组件无页面生命周期</view>
6
+			<view class="notice">需通过mescroll-comp.js给子组件补充页面生命周期</view>
7
+		</view>
8
+		
9
+		<!-- 第一步: 给mescroll-body的组件添加: ref="mescrollItem" (固定的,不可改,与mescroll-comp.js对应)-->
10
+		<mescroll-item ref="mescrollItem"></mescroll-item>
11
+	</view>
12
+</template>
13
+
14
+<script>
15
+	import MescrollItem from "./mescroll-comp-item.vue"; // 一个mescroll-body写在子组件的情况
16
+	// import MescrollItem from "./mescroll-more.vue"; // 多个mescroll-body写在子组件的情况
17
+	
18
+	// 第二步: 引入mescroll-comp.js
19
+	import MescrollCompMixin from "@/components/mescroll-uni/mixins/mescroll-comp.js";
20
+	export default {
21
+		mixins: [MescrollCompMixin],
22
+		components: {
23
+			MescrollItem
24
+		}
25
+	}
26
+</script>
27
+
28
+<style>
29
+	/*说明*/
30
+	.notice-warp{
31
+		z-index: 9;
32
+		position: fixed;
33
+		top: var(--window-top);
34
+		left: 0;
35
+		width: 100%;
36
+		height: 100rpx;/*对应mescroll-body的top值*/
37
+		font-size: 26upx;
38
+		padding-top: 10upx;
39
+		border-bottom: 1upx solid #eee;
40
+		text-align: center;
41
+		background-color: #fff;
42
+	}
43
+	.notice-warp .notice{
44
+		color:#555;
45
+	}
46
+	.notice-warp .btn-change{
47
+		display: inline-block;
48
+		margin-top: 28upx;
49
+		padding: 6upx 16upx;
50
+		border: 1upx solid #FF6990;
51
+		border-radius: 40upx;
52
+		color: #FF6990;
53
+	}
54
+	.notice-warp .btn-change:active{
55
+		opacity: .5;
56
+	}
57
+</style>

+ 80
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-more-item.vue Voir le fichier

@@ -0,0 +1,80 @@
1
+<template>
2
+	<!-- 不能用v-if (i: 每个tab页的专属下标;  index: 当前tab的下标; 申明在 MescrollMoreItemMixin )-->
3
+	<view v-show="i === index">
4
+		<!-- top="120"下拉布局往下偏移,防止被悬浮菜单遮住 -->
5
+		<!-- ref动态生成: 字节跳动小程序编辑器不支持一个页面存在相同的ref (如不考虑字节跳动小程序可固定值为 ref="mescrollRef") -->
6
+		<mescroll-body :ref="'mescrollRef'+i" @init="mescrollInit" top="120" :down="downOption" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
7
+			<!-- 数据列表 -->
8
+			<good-list :list="goods"></good-list>
9
+		</mescroll-body>
10
+	</view>
11
+</template>
12
+
13
+<script>
14
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
15
+	import MescrollMoreItemMixin from "@/components/mescroll-uni/mixins/mescroll-more-item.js";
16
+	import {apiSearch} from "@/api/mock.js"
17
+	
18
+	export default {
19
+		mixins: [MescrollMixin,MescrollMoreItemMixin], // 注意此处还需使用MescrollMoreItemMixin (必须写在MescrollMixin后面)
20
+		props:{
21
+			i: Number, // 每个tab页的专属下标 (除了支付宝小程序必须在这里定义, 其他平台都可不用写, 因为已在MescrollMoreItemMixin定义)
22
+			index: { // 当前tab的下标 (除了支付宝小程序必须在这里定义, 其他平台都可不用写, 因为已在MescrollMoreItemMixin定义)
23
+				type: Number,
24
+				default(){
25
+					return 0
26
+				}
27
+			},
28
+			tabs: { // 为了请求数据,演示用,可根据自己的项目判断是否要传
29
+				type: Array,
30
+				default(){
31
+					return []
32
+				}
33
+			}
34
+		},
35
+		data() {
36
+			return {
37
+				downOption:{
38
+					auto:false // 不自动加载 (mixin已处理第一个tab触发downCallback)
39
+				},
40
+				upOption:{
41
+					auto:false, // 不自动加载
42
+					// page: {
43
+					// 	num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
44
+					// 	size: 10 // 每页数据的数量
45
+					// },
46
+					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
47
+					empty:{
48
+						tip: '~ 空空如也 ~', // 提示
49
+						btnText: '去看看'
50
+					}
51
+				},
52
+				goods: [] //列表数据
53
+			}
54
+		},
55
+		methods: {
56
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
57
+			upCallback(page) {
58
+				// this.i: 每个tab页的专属下标
59
+				// this.index: 当前tab的下标
60
+				let word = this.tabs[this.i].name
61
+				apiSearch(page.num, page.size, word).then(curPageData=>{
62
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
63
+					this.mescroll.endSuccess(curPageData.length);
64
+					//设置列表数据
65
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
66
+					this.goods=this.goods.concat(curPageData); //追加新数据
67
+				}).catch(()=>{
68
+					//联网失败, 结束加载
69
+					this.mescroll.endErr();
70
+				})
71
+			},
72
+			//点击空布局按钮的回调
73
+			emptyClick(){
74
+				uni.showToast({
75
+					title:'点击了按钮,具体逻辑自行实现'
76
+				})
77
+			}
78
+		}
79
+	}
80
+</script>

+ 75
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-more.vue Voir le fichier

@@ -0,0 +1,75 @@
1
+<template>
2
+	<view>
3
+		<!-- 菜单 -->
4
+		<view class="top-warp">
5
+			<view class="tip">每个菜单列表仅初始化一次,切换菜单缓存数据</view>
6
+			<!-- 当设置tab-width,指定每个tab宽度时,则不使用flex布局,改用水平滑动 -->
7
+			<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange" :tab-width="130"></me-tabs>
8
+		</view>
9
+		
10
+		<!-- 子组件 (i: 每个tab页的专属下标;  index: 当前tab的下标) -->
11
+		
12
+		<!-- 如果每个子组件布局不一样, 可拆开写 (注意ref只能为 "mescrollItem下标" 的格式, 另外 :i="下标" :index="tabIndex"也是固定写法) : -->
13
+		<!-- <home ref="mescrollItem0" :i="0" :index="tabIndex"></home>
14
+		<shopcart ref="mescrollItem1" :i="1" :index="tabIndex"></shopcart>
15
+		<user ref="mescrollItem2" :i="2" :index="tabIndex"></user> -->
16
+		
17
+		<mescroll-item ref="mescrollItem0" :i="0" :index="tabIndex" :tabs="tabs"></mescroll-item>
18
+		<mescroll-item ref="mescrollItem1" :i="1" :index="tabIndex" :tabs="tabs"></mescroll-item>
19
+		<mescroll-item ref="mescrollItem2" :i="2" :index="tabIndex" :tabs="tabs"></mescroll-item>
20
+		<mescroll-item ref="mescrollItem3" :i="3" :index="tabIndex" :tabs="tabs"></mescroll-item>
21
+		<mescroll-item ref="mescrollItem4" :i="4" :index="tabIndex" :tabs="tabs"></mescroll-item>
22
+		<mescroll-item ref="mescrollItem5" :i="5" :index="tabIndex" :tabs="tabs"></mescroll-item>
23
+		<mescroll-item ref="mescrollItem6" :i="6" :index="tabIndex" :tabs="tabs"></mescroll-item>
24
+		<mescroll-item ref="mescrollItem7" :i="7" :index="tabIndex" :tabs="tabs"></mescroll-item>
25
+		<mescroll-item ref="mescrollItem8" :i="8" :index="tabIndex" :tabs="tabs"></mescroll-item>
26
+		
27
+		
28
+		<!-- 如果每个子组件布局一样, 则可使用v-for (注意v-for的ref="mescrollItem"必须是固定值; 另外支付宝小程序不支持此v-for的写法)-->
29
+		<!-- <mescroll-item ref="mescrollItem" v-for="(tab,i) in tabs" :key="i" :i="i" :index="tabIndex" :tabs="tabs"></mescroll-item> -->
30
+	</view>
31
+</template>
32
+
33
+<script>
34
+	import MescrollItem from "./mescroll-more-item.vue";
35
+	import MescrollMoreMixin from "@/components/mescroll-uni/mixins/mescroll-more.js";
36
+	
37
+	export default {
38
+		mixins: [MescrollMoreMixin], // 多个mescroll-body写在子组件时, 则使用mescroll-more.js补充子组件的页面生命周期
39
+		components: {
40
+			MescrollItem
41
+		},
42
+		data() {
43
+			return {
44
+				tabs: [{name:'全部'}, {name:'奶粉'}, {name:'面膜'}, {name:'图书'}, {name:'果汁'}, {name:'奶瓶'}, {name:'美素'}, {name:'花王'}, {name:'韩蜜'}],
45
+				tabIndex: 0 // 当前tab下标,必须与mescroll-more.js对应,所以tabIndex是固定变量,不可以改为其他的名字
46
+			}
47
+		},
48
+		onShow() {
49
+			// 返回刷新: https://www.mescroll.com/uni.html#note 第二点
50
+			// if(this.canReset){
51
+			// 	let curMescroll = this.getMescroll(this.tabIndex)
52
+			// 	curMescroll && curMescroll.resetUpScroll()
53
+			// }
54
+			// this.canReset = true
55
+		}
56
+	}
57
+</script>
58
+
59
+<style>
60
+	.top-warp{
61
+		z-index: 9990;
62
+		position: fixed;
63
+		top: --window-top; /* css变量 */
64
+		left: 0;
65
+		width: 100%;
66
+		height: 120upx;
67
+		background-color: white;
68
+	}
69
+	.top-warp .tip{
70
+		font-size: 28upx;
71
+		height: 60upx;
72
+		line-height: 60upx;
73
+		text-align: center;
74
+	}
75
+</style>

+ 87
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-native.vue Voir le fichier

@@ -0,0 +1,87 @@
1
+<template>
2
+	<!-- 系统自带的下拉刷新,只能配合mescroll-body使用, 在mescroll-uni中无效 -->
3
+	<mescroll-body ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
4
+		<view class="tip">系统自带的下拉刷新,性能最好,支持条件编译</view>
5
+		<view class="tip">模拟器和真机效果可能不一样,请用真机测试</view>
6
+		<view class="tip" @click="triggerDownScroll">点此主动触发下拉刷新</view>
7
+		<!-- 菜单 -->
8
+		<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
9
+		<!-- 数据列表 -->
10
+		<good-list :list="goods"></good-list>
11
+	</mescroll-body>
12
+</template>
13
+
14
+<script>
15
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
16
+	import {apiSearch} from "@/api/mock.js"
17
+	
18
+	export default {
19
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件,内部已注册onPullDownRefresh)
20
+		data() {
21
+			return {
22
+				downOption:{
23
+					native: true // 必须配置此项,且需在pages.json配置"enablePullDownRefresh" : true
24
+					
25
+					// 支持条件编译,如您可以配置小程序端使用系统自带的,其他平台使用mescroll的下拉样式
26
+					//ifdef MP
27
+					// native: true
28
+					//endif
29
+					//可在mescroll-uni-option.js全局配置native的值
30
+				},
31
+				upOption:{
32
+					noMoreSize: 4, 
33
+					empty:{
34
+						tip: '~ 搜索无数据 ~',
35
+						btnText: '去看看'
36
+					}
37
+				},
38
+				goods: [], //列表数据
39
+				tabs: ['全部', '奶粉', '面膜', '图书'],
40
+				tabIndex: 0 // tab下标
41
+			}
42
+		},
43
+		methods: {
44
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
45
+			upCallback(page) {
46
+				//联网加载数据
47
+				let keyword = this.tabs[this.tabIndex]
48
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
49
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
50
+					this.mescroll.endSuccess(curPageData.length);
51
+					//设置列表数据
52
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
53
+					this.goods=this.goods.concat(curPageData); //追加新数据
54
+				}).catch(()=>{
55
+					//联网失败, 结束加载
56
+					this.mescroll.endErr();
57
+				})
58
+			},
59
+			//点击空布局按钮的回调
60
+			emptyClick(){
61
+				uni.showToast({
62
+					title:'点击了按钮,具体逻辑自行实现'
63
+				})
64
+			},
65
+			
66
+			// 切换菜单
67
+			tabChange() {
68
+				this.goods = []// 先置空列表,显示加载进度
69
+				this.mescroll.resetUpScroll() // 再刷新列表数据
70
+			},
71
+			
72
+			// 主动触发下拉刷新
73
+			triggerDownScroll(){
74
+				this.mescroll.triggerDownScroll()
75
+			}
76
+		}
77
+	}
78
+</script>
79
+
80
+<style>
81
+	.tip{
82
+		font-size: 28upx;
83
+		height: 60upx;
84
+		line-height: 60upx;
85
+		text-align: center;
86
+	}
87
+</style>

+ 89
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-one.vue Voir le fichier

@@ -0,0 +1,89 @@
1
+<template>
2
+	<view>
3
+		<!-- 菜单 -->
4
+		<view class="top-warp">
5
+			<view class="tip">每次切换菜单及时刷新列表,不缓存数据</view>
6
+			<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
7
+		</view>
8
+		
9
+		<!-- top="xxx"下拉布局往下偏移,防止被悬浮菜单遮住 -->
10
+		 <mescroll-body ref="mescrollRef" @init="mescrollInit" top="120" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
11
+			<!-- 数据列表 -->
12
+			<good-list :list="goods"></good-list>
13
+		</mescroll-body>
14
+	</view>
15
+</template>
16
+
17
+<script>
18
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
19
+	import {apiSearch} from "@/api/mock.js"
20
+	
21
+	export default {
22
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
23
+		data() {
24
+			return {
25
+				upOption:{
26
+					// page: {
27
+					// 	num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
28
+					// 	size: 10 // 每页数据的数量
29
+					// },
30
+					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
31
+					empty:{
32
+						tip: '~ 搜索无数据 ~', // 提示
33
+						btnText: '去看看'
34
+					}
35
+				},
36
+				goods: [], //列表数据
37
+				tabs: ['全部', '奶粉', '面膜', '图书'],
38
+				tabIndex: 0 // tab下标
39
+			}
40
+		},
41
+		methods: {
42
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
43
+			upCallback(page) {
44
+				//联网加载数据
45
+				let keyword = this.tabs[this.tabIndex]
46
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
47
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
48
+					this.mescroll.endSuccess(curPageData.length);
49
+					//设置列表数据
50
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
51
+					this.goods=this.goods.concat(curPageData); //追加新数据
52
+				}).catch(()=>{
53
+					//联网失败, 结束加载
54
+					this.mescroll.endErr();
55
+				})
56
+			},
57
+			//点击空布局按钮的回调
58
+			emptyClick(){
59
+				uni.showToast({
60
+					title:'点击了按钮,具体逻辑自行实现'
61
+				})
62
+			},
63
+			
64
+			// 切换菜单
65
+			tabChange() {
66
+				this.goods = []// 先置空列表,显示加载进度
67
+				this.mescroll.resetUpScroll() // 再刷新列表数据
68
+			}
69
+		}
70
+	}
71
+</script>
72
+
73
+<style>
74
+	.top-warp{
75
+		z-index: 9990;
76
+		position: fixed;
77
+		top: --window-top; /* css变量 */
78
+		left: 0;
79
+		width: 100%;
80
+		height: 120upx;
81
+		background-color: white;
82
+	}
83
+	.top-warp .tip{
84
+		font-size: 28upx;
85
+		height: 60upx;
86
+		line-height: 60upx;
87
+		text-align: center;
88
+	}
89
+</style>

+ 191
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-options.vue Voir le fichier

@@ -0,0 +1,191 @@
1
+<template>
2
+	<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" 
3
+	 :down="downOption" 
4
+	 :up="upOption" 
5
+	 :top="0" 
6
+	 :bottom="0" 
7
+	 :topbar="false" 
8
+	 :bottombar="true" 
9
+	 :fixed="true" 
10
+	 height="100%" 
11
+	 :safearea="false" 
12
+	 @emptyclick="emptyClick" 
13
+	 @topclick="topClick" 
14
+	 @scroll="scroll">
15
+	 
16
+		<view class="tip">展示down和up的所有配置项</view>
17
+		<view class="tip" @click="triggerDownScroll">点此主动触发下拉刷新</view>
18
+		<view class="tip" @click="scrollToY(200)">点此测试滚动到指定位置 (如: 200px)</view>
19
+		<!-- 滚动到本页元素,只需普通的id或class选择器即可 -->
20
+		<view class="tip" @click="scrollIntoView('#anchorPoint')" id="anchorPoint">点此测试滚动到指定view (元素在本页)</view>
21
+		<!-- 滚动到子组件,小程序必须用'跨自定义组件的后代选择器' -->
22
+		<view class="tip" @click="scrollIntoView('.good-comp >>> #good2')">点此测试滚动到指定view (元素在子组件)</view>
23
+		
24
+		<!-- tab组件 -->
25
+		<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
26
+		
27
+		<!-- 视频请尽量使用me-video组件 (video在APP中是原生组件, 真机APP端下拉渲染不及时.) -->
28
+		<!-- 使用me-video组件, 未播放时自动展示image封面, 播放时才显示video, 提高性能; 当加上 :mescroll="mescroll"之后, 如果播放中执行下拉,会自动隐藏视频,显示封面,避免视频下拉悬浮错位(仅APP端这样处理) -->
29
+		<me-video v-if="tabIndex==0" :mescroll="mescroll" poster="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-mescroll/2e3cd7a0-f31a-11ea-81ea-f115fe74321c.png" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-mescroll/2ae5d090-f26e-11ea-81ea-f115fe74321c.mp4"></me-video>
30
+		
31
+		<!-- 商品组件 -->
32
+		<good-list class="good-comp" :list="goods"></good-list>
33
+	</mescroll-body>
34
+</template>
35
+
36
+<script>
37
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
38
+	import {apiSearch} from "@/api/mock.js"
39
+	
40
+	export default {
41
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
42
+		data() {
43
+			return {
44
+				downOption: {
45
+					use: true, // 是否启用下拉刷新; 默认true
46
+					auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
47
+					native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
48
+					autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
49
+					isLock: false, // 是否锁定下拉刷新,默认false;
50
+					offset: 80, // 在列表顶部,下拉大于80upx,松手即可触发下拉刷新的回调
51
+					inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
52
+					outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
53
+					bottomOffset: 20, // 当手指touchmove位置在距离body底部20upx范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
54
+					minAngle: 45, // 向下滑动最少偏移的角度,取值区间  [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
55
+					bgColor: "#E75A7C", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
56
+					textColor: "#fff", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
57
+					textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
58
+					textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
59
+					textLoading: '加载中 ...' // 加载中的提示文本
60
+				},
61
+				upOption: {
62
+					use: true, // 是否启用上拉加载; 默认true
63
+					auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
64
+					isLock: false, // 是否锁定上拉加载,默认false;
65
+					isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
66
+					page: {
67
+						num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
68
+						size: 10, // 每页数据的数量
69
+						time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
70
+					},
71
+					noMoreSize: 3, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
72
+					offset: 80, // 距底部多远时,触发upCallback(仅mescroll-uni生效, 对于mescroll-body则需在pages.json设置"onReachBottomDistance")
73
+					bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
74
+					textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
75
+					textLoading: '加载中 ...', // 加载中的提示文本
76
+					textNoMore: '-- END --', // 没有更多数据的提示文本
77
+					toTop: {
78
+						// 回到顶部按钮,需配置src才显示
79
+						src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径
80
+						offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
81
+						duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
82
+						zIndex: 9990, // fixed定位z-index值
83
+						left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
84
+						right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
85
+						bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
86
+						safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取mescroll组件props的safearea值)
87
+						width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
88
+						radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
89
+					},
90
+					empty: {
91
+						use: true, // 是否显示空布局
92
+						icon: "https://www.mescroll.com/img/mescroll-empty.png", // 图标路径
93
+						tip: '~ 暂无相关数据 ~', // 提示
94
+						btnText: '去逛逛 >', // 按钮
95
+						fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
96
+						top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
97
+						zIndex: 99 // fixed定位z-index值
98
+					},
99
+					onScroll: true // 是否监听滚动事件, 默认false, 仅mescroll-uni生效; mescroll-body直接声明onPageScroll (配置为true时,可@scroll="scroll"获取到滚动条位置和方向; 注意监听列表滚动是非常耗性能的,很容易出现卡顿,非特殊情况不要配置此项)
100
+				},
101
+				goods: [], //列表数据
102
+				tabs: ['全部', '奶粉', '图书'],
103
+				tabIndex: 0 // tab下标
104
+			}
105
+		},
106
+		methods: {
107
+			/*下拉刷新的回调 */
108
+			downCallback() {
109
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
110
+				// loadSwiper();
111
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
112
+				this.mescroll.resetUpScroll()
113
+			},
114
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
115
+			upCallback(page) {
116
+				//联网加载数据
117
+				let keyword = this.tabs[this.tabIndex]
118
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
119
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
120
+					//mescroll会根据传的参数,自动判断列表如果无任何数据,则提示空;列表无下一页数据,则提示无更多数据;
121
+
122
+					//方法一(推荐): 后台接口有返回列表的总页数 totalPage
123
+					//this.mescroll.endByPage(curPageData.length, totalPage); //必传参数(当前页的数据个数, 总页数)
124
+
125
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
126
+					//this.mescroll.endBySize(curPageData.length, totalSize); //必传参数(当前页的数据个数, 总数据量)
127
+
128
+					//方法三(推荐): 您有其他方式知道是否有下一页 hasNext
129
+					//this.mescroll.endSuccess(curPageData.length, hasNext); //必传参数(当前页的数据个数, 是否有下一页true/false)
130
+
131
+					//方法四 (不推荐),会存在一个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据当前页的数据个数判断,则需翻到第三页才会知道无更多数据
132
+					this.mescroll.endSuccess(curPageData.length);
133
+
134
+					//设置列表数据
135
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
136
+					this.goods=this.goods.concat(curPageData); //追加新数据
137
+					
138
+				}).catch(()=>{
139
+					//联网失败, 结束加载
140
+					this.mescroll.endErr();
141
+				})
142
+			},
143
+			// 点击空布局按钮的回调
144
+			emptyClick(){
145
+				uni.showToast({
146
+					title:"点击了按钮"
147
+				})
148
+			},
149
+			// 点击回到顶部按钮的回调
150
+			topClick(){
151
+				console.log('点击了回到顶部按钮');
152
+			},
153
+			// mescroll-uni滚动事件 (需在up配置onScroll:true才生效, mescroll-body直接声明onPageScroll)
154
+			scroll(){
155
+				console.log('mescroll元素id: '+this.mescroll.viewId+' , 滚动内容高度:'+this.mescroll.getScrollHeight() + ', mescroll高度:'+this.mescroll.getClientHeight() + ', 滚动条位置:'+this.mescroll.getScrollTop() + ', 距离底部:'+this.mescroll.getScrollBottom() + ', 是否向上滑:'+this.mescroll.isScrollUp)
156
+			},
157
+			// 切换菜单
158
+			tabChange() {
159
+				this.goods = []// 先置空列表,显示加载进度
160
+				this.mescroll.resetUpScroll() // 再刷新列表数据
161
+			},
162
+			// 主动触发下拉刷新
163
+			triggerDownScroll(){
164
+				this.mescroll.triggerDownScroll()
165
+			},
166
+			// 滚动到指定位置,传数字 (单位px)
167
+			scrollToY(y){
168
+				// this.mescroll.scrollTo(y) // 过渡动画时长默认300ms
169
+				this.mescroll.scrollTo(y, 0) // 无过渡动画
170
+			},
171
+			// 滚动到指定view,传view的id
172
+			scrollIntoView(viewId){
173
+				// this.mescroll.scrollTo(viewId) // 过渡动画时长默认300ms
174
+				this.mescroll.scrollTo(viewId, 0) // 无过渡动画
175
+			}
176
+		},
177
+		// mescroll-body的滚动事件是页面的滚动事件
178
+		// onPageScroll(e){
179
+		// 	console.log("mescroll-body的滚动事件e.scrollTop=" + e.scrollTop);
180
+		// }
181
+	}
182
+</script>
183
+
184
+<style>
185
+	.tip{
186
+		font-size: 28upx;
187
+		height: 60upx;
188
+		line-height: 60upx;
189
+		text-align: center;
190
+	}
191
+</style>

+ 134
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-uni-part.vue Voir le fichier

@@ -0,0 +1,134 @@
1
+<template>
2
+	<!-- mescroll-uni本质是scroll-view,支持局部区域滚动,可使用flex布局灵活的嵌在某个view中, 而mescroll-body只能靠fixed定位其他元素变相实现'局部滚动' -->
3
+	<view class="page-warp">
4
+		
5
+		<view class="top-warp">
6
+			<view>顶部区域</view>
7
+			<view style="font-size: 24rpx;">mescroll-uni 支持局部区域滚动,支持使用flex嵌在某个view</view>
8
+		</view>
9
+		
10
+		<view class="center-warp">
11
+			<!-- 左边 -->
12
+			<scroll-view class="left-warp" :scroll-y="true">
13
+				<view class="tab" :class="{active:i==tabIndex}" v-for="(tab,i) in tabs" :key="i" @click="tabChange(i)">{{tab}}</view>
14
+			</scroll-view>
15
+			
16
+			<view class="right-warp">
17
+				<!--右边 :fixed="false", 高度跟随父元素 (不在组件上定义class,避免部分小程序平台编译丢失, 如支付宝,钉钉小程序) -->
18
+				<mescroll-uni :fixed="false" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback">
19
+					<good-list :list="goods"></good-list>
20
+				</mescroll-uni>
21
+			</view>
22
+		</view>
23
+		
24
+		<view class="bottom-warp"> 底部区域 </view>
25
+		
26
+	</view>
27
+</template>
28
+
29
+
30
+<script>
31
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
32
+	import {apiSearch} from "@/api/mock.js"
33
+	
34
+	export default {
35
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
36
+		data() {
37
+			return {
38
+				goods: [], // 数据列表
39
+				tabs: ['全部', '奶粉', '面膜', '图书', '果汁', '奶瓶', '美素', '花王', '韩蜜', '口红', '毛巾', '玩具', '衣服'],
40
+				tabIndex: 0 // tab下标
41
+			}
42
+		},
43
+		methods: {
44
+			upCallback(page) {
45
+				//联网加载数据
46
+				let keyword = this.tabs[this.tabIndex]
47
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
48
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
49
+					this.mescroll.endSuccess(curPageData.length);
50
+					//设置列表数据
51
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
52
+					this.goods=this.goods.concat(curPageData); //追加新数据
53
+				}).catch(()=>{
54
+					//联网失败, 结束加载
55
+					this.mescroll.endErr();
56
+				})
57
+			},
58
+			// 切换菜单
59
+			tabChange(i){
60
+				if(this.tabIndex != i){
61
+					this.tabIndex = i
62
+					this.goods = []; // 先置空列表,显示加载进度条
63
+					this.mescroll.resetUpScroll(); // 重置列表数据
64
+				}
65
+			}
66
+		}
67
+	}
68
+</script>
69
+
70
+
71
+<style lang="scss">
72
+	/*根元素需要有固定的高度*/
73
+	page{
74
+		height: 100%;
75
+		// 支付宝小程序,钉钉小程序需添加绝对定位,否则height:100%失效: https://opendocs.alipay.com/mini/framework/acss#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
76
+		/* #ifdef MP-ALIPAY || MP-DINGTALK*/
77
+		position: absolute;
78
+		top: 0;
79
+		left: 0;
80
+		width: 100%;
81
+		/* #endif */
82
+		
83
+		/*需给父元素设置height:100%*/
84
+		.page-warp{
85
+			height: 100%;
86
+			display: flex;
87
+			flex-direction: column;
88
+			
89
+			/* 顶部区域 */
90
+			.top-warp{
91
+				font-size: 28rpx;
92
+				padding: 20rpx;
93
+				text-align: center;
94
+				background-color: #CFE0DA;
95
+			}
96
+			
97
+			/* 中间 */
98
+			.center-warp{
99
+				flex: 1;
100
+				min-width: 0;
101
+				min-height: 0;/* 需给flex:1的元素加上最小高,否则内容超过会溢出容器 (如:小程序Android真机) */
102
+				border: 4px solid red;
103
+				display: flex;
104
+				
105
+				// 左边
106
+				.left-warp{
107
+					width: 180rpx;
108
+					height: 100%;
109
+					background-color: #eee;
110
+					.tab{
111
+						font-size: 28rpx;
112
+						padding: 28rpx;
113
+						&.active{
114
+							background-color: #fff;
115
+						}
116
+					}
117
+				}
118
+				
119
+				// 右边
120
+				.right-warp{
121
+					flex: 1;
122
+					min-width: 0;
123
+				}
124
+			}
125
+			
126
+			/* 底部区域 */
127
+			.bottom-warp{
128
+				padding: 20rpx;
129
+				text-align: center;
130
+				background-color: #FF6990;
131
+			}
132
+		}
133
+	}
134
+</style>

+ 113
- 0
oa-ui-app/uni_modules/mescroll/pages/base/mescroll-uni.vue Voir le fichier

@@ -0,0 +1,113 @@
1
+<template>
2
+	<view>
3
+		<!-- 菜单 -->
4
+		<view class="top-warp">
5
+			<view class="tip">基于scroll-view,常用在浮窗弹层等局部滚动区域</view>
6
+			<view class="tip" @click="triggerDownScroll">点此主动触发下拉刷新</view>
7
+			<view class="tip" @click="scrollToY(200)">点此测试滚动到指定位置 (如: 200px)</view>
8
+			<!-- 滚动到本页元素,只需普通的id或class选择器即可 -->
9
+			<view class="tip" @click="scrollIntoView('#anchorPoint')">点此测试滚动到指定view (元素在本页)</view>
10
+			<!-- 滚动到子组件,小程序必须用'跨自定义组件的后代选择器' -->
11
+			<view class="tip" @click="scrollIntoView('.good-comp >>> #good2')">点此测试滚动到指定view (元素在子组件)</view>
12
+			<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
13
+		</view>
14
+		
15
+		<!-- top="xxx"下拉布局往下偏移,防止被悬浮菜单遮住 -->
16
+		 <mescroll-uni ref="mescrollRef" @init="mescrollInit" top="365" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
17
+			<!-- 大海报 -->
18
+			<image id="anchorPoint" v-if="tabIndex==0" src="https://www.mescroll.com/img/taobao/taobao3.jpg" mode="widthFix" style="width: 100%"/>
19
+			 
20
+			<!-- 数据列表 -->
21
+			<good-list class="good-comp" :list="goods"></good-list>
22
+		</mescroll-uni>
23
+	</view>
24
+</template>
25
+
26
+<script>
27
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
28
+	import {apiSearch} from "@/api/mock.js"
29
+	
30
+	export default {
31
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
32
+		data() {
33
+			return {
34
+				upOption:{
35
+					// page: {
36
+					// 	size: 10 // 每页数据的数量
37
+					// },
38
+					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
39
+					empty:{
40
+						tip: '~ 搜索无数据 ~', // 提示
41
+						btnText: '去看看'
42
+					}
43
+				},
44
+				goods: [], //列表数据
45
+				tabs: ['全部', '奶粉', '面膜', '图书'],
46
+				tabIndex: 0 // tab下标
47
+			}
48
+		},
49
+		methods: {
50
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
51
+			upCallback(page) {
52
+				//联网加载数据
53
+				let keyword = this.tabs[this.tabIndex]
54
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
55
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
56
+					this.mescroll.endSuccess(curPageData.length);
57
+					//设置列表数据
58
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
59
+					this.goods=this.goods.concat(curPageData); //追加新数据
60
+				}).catch(()=>{
61
+					//联网失败, 结束加载
62
+					this.mescroll.endErr();
63
+				})
64
+			},
65
+			//点击空布局按钮的回调
66
+			emptyClick(){
67
+				uni.showToast({
68
+					title:'点击了按钮,具体逻辑自行实现'
69
+				})
70
+			},
71
+			
72
+			// 切换菜单
73
+			tabChange() {
74
+				this.goods = []// 先置空列表,显示加载进度
75
+				this.mescroll.resetUpScroll() // 再刷新列表数据
76
+			},
77
+			
78
+			// 主动触发下拉刷新
79
+			triggerDownScroll(){
80
+				this.mescroll.scrollTo(0, 0)
81
+				this.mescroll.triggerDownScroll()
82
+			},
83
+			// 滚动到指定位置,传数字 (单位px)
84
+			scrollToY(y){
85
+				// this.mescroll.scrollTo(y) // 过渡动画时长默认300ms
86
+				this.mescroll.scrollTo(y, 0) // 无过渡动画
87
+			},
88
+			// 滚动到指定view,传view的id
89
+			scrollIntoView(viewId){
90
+				// this.mescroll.scrollTo(viewId) // 过渡动画时长默认300ms
91
+				this.mescroll.scrollTo(viewId, 0) // 无过渡动画
92
+			}
93
+		}
94
+	}
95
+</script>
96
+
97
+<style>
98
+	.top-warp{
99
+		z-index: 9990;
100
+		position: fixed;
101
+		top: --window-top; /* css变量 */
102
+		left: 0;
103
+		width: 100%;
104
+		height: 365upx;
105
+		background-color: white;
106
+	}
107
+	.top-warp .tip{
108
+		font-size: 28upx;
109
+		height: 60upx;
110
+		line-height: 60upx;
111
+		text-align: center;
112
+	}
113
+</style>

+ 141
- 0
oa-ui-app/uni_modules/mescroll/pages/base/sticky-data.vue Voir le fichier

@@ -0,0 +1,141 @@
1
+<!-- 菜单悬浮的原理: 通过给菜单添加position:sticky实现, 用法超简单, 仅APP端的低端机不兼容 https://caniuse.com/#feat=css-sticky -->
2
+<template>
3
+	<view>
4
+		<!-- 对于mescroll-body: 需设置:sticky="true", 此应避免在mescroll-body标签前面加其他非定位的元素, 否则下拉区域会被挤出, 无法会隐藏.-->
5
+		<!-- 对于mescroll-uni: 则无需设置:sticky="true", 无其他限制和要求 -->
6
+		<mescroll-body :sticky="true" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback">
7
+			<swiper style="min-height: 300rpx" autoplay="true" interval="3000" duration="300" circular="true">
8
+				<swiper-item>
9
+					<image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper1.jpg" mode="widthFix"/>
10
+				</swiper-item>
11
+				<swiper-item>
12
+					<image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper2.jpg" mode="widthFix"/>
13
+				</swiper-item>
14
+			</swiper>
15
+			
16
+			<view class="demo-tip">
17
+				<view>列表只初始化一次,切换菜单缓存数据</view>
18
+				<view>吸顶通过给菜单加position:sticky实现, 用法简单</view>
19
+				<view>小程序,微信h5端: 低端机sticky也可生效, 可放心使用</view>
20
+				<view>APP端: 仅部分低端机无效,若要兼容则参考sticky-scroll</view>
21
+			</view>
22
+			
23
+			<!-- sticky吸顶悬浮的菜单, 父元素必须是 mescroll -->
24
+			<view class="sticky-tabs">
25
+				<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
26
+			</view>
27
+			
28
+			<!-- 数据列表 -->
29
+			<good-list :list="goods"></good-list>
30
+		</mescroll-body>
31
+		
32
+		<!-- 此处可以写其他fixed定位元素 -->
33
+		<!-- <view></view> -->
34
+	</view>
35
+</template>
36
+
37
+<script>
38
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
39
+	import {apiSearch} from "@/api/mock.js"
40
+	
41
+	export default {
42
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
43
+		data() {
44
+			return {
45
+				tabs:[
46
+					{name:'全部', goods: null, num:1, y:0, curPageLen:0, hasNext:true},
47
+					{name:'母婴', goods: null, num:1, y:0, curPageLen:0, hasNext:true},
48
+					{name:'图书', goods: null, num:1, y:0, curPageLen:0, hasNext:true}
49
+					],
50
+				tabIndex: 0 // 当前菜单下标
51
+			}
52
+		},
53
+		computed: {
54
+			// 列表数据
55
+			goods() {
56
+				return this.tabs[this.tabIndex].goods
57
+			}
58
+		},
59
+		methods: {
60
+			/*下拉刷新的回调 */
61
+			downCallback() {
62
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
63
+				// loadSwiper();
64
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
65
+				this.mescroll.resetUpScroll()
66
+			},
67
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
68
+			upCallback(page) {
69
+				let keyword = this.tabs[this.tabIndex].name;
70
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
71
+					// 当前tab数据
72
+					let curTab = this.tabs[this.tabIndex]
73
+					
74
+					// 设置列表数据
75
+					if(page.num == 1) curTab.goods = []; //如果是第一页需手动制空列表
76
+					curTab.goods = curTab.goods.concat(curPageData); //追加新数据
77
+					curTab.num = page.num; // 页码
78
+					curTab.curPageLen = curPageData.length; // 当前页长
79
+					curTab.hasNext = this.mescroll.optUp.hasNext; // 是否还有下一页
80
+					
81
+					// 需先隐藏加载状态
82
+					this.mescroll.endSuccess(curPageData.length);
83
+				}).catch(()=>{
84
+					//联网失败, 结束加载
85
+					this.mescroll.endErr();
86
+				})
87
+			},
88
+			// 切换菜单
89
+			tabChange (index) {
90
+				// 记录切换前滚动条的位置
91
+				if(!this.preIndex) this.preIndex = 0
92
+				let preTab = this.tabs[this.preIndex]
93
+				preTab.y = this.mescroll.getScrollTop()
94
+				this.preIndex = index;
95
+				// 当前菜单的数据
96
+				let curTab = this.tabs[index]
97
+				if (!curTab.goods) {
98
+					// 没有初始化,则初始化
99
+					this.mescroll.resetUpScroll()
100
+				} else{
101
+					// 初始化过,则恢复之前的列表数据
102
+					this.mescroll.setPageNum(curTab.num + 1); // 恢复当前页码
103
+					this.mescroll.endSuccess(curTab.curPageLen, curTab.hasNext); // 恢复是否有下一页或显示空布局
104
+					this.$nextTick(()=>{
105
+						this.mescroll.scrollTo(curTab.y, 0) // 恢复滚动条的位置
106
+					})
107
+				}
108
+			}
109
+		}
110
+	}
111
+</script>
112
+
113
+<style lang="scss">
114
+	/*
115
+	sticky生效条件:
116
+	1、父元素不能overflow:hidden或者overflow:auto属性。(mescroll-body设置:sticky="true"即可, mescroll-uni本身没有设置overflow)
117
+	2、必须指定top、bottom、left、right4个值之一,否则只会处于相对定位
118
+	3、父元素的高度不能低于sticky元素的高度
119
+	4、sticky元素仅在其父元素内生效,所以父元素必须是 mescroll
120
+	*/
121
+	.sticky-tabs{
122
+		z-index: 990;
123
+		position: sticky;
124
+		top: var(--window-top);
125
+		background-color: #fff;
126
+	}
127
+	
128
+	// 使用mescroll-uni,则top为0
129
+	.mescroll-uni,
130
+	/deep/.mescroll-uni{
131
+		.sticky-tabs{
132
+			top: 0;
133
+		}
134
+	}
135
+	
136
+	.demo-tip{
137
+		padding: 18rpx;
138
+		font-size: 24rpx;
139
+		text-align: center;
140
+	}
141
+</style>

+ 177
- 0
oa-ui-app/uni_modules/mescroll/pages/base/sticky-scroll-data.vue Voir le fichier

@@ -0,0 +1,177 @@
1
+<!-- 菜单悬浮的原理: 监听滚动条的位置大于某个值时,控制顶部菜单的显示和隐藏, 用法比sticky复杂, 但APP端可兼容低端机 -->
2
+<template>
3
+	<view>
4
+		<!-- 菜单 (悬浮,预先隐藏)-->
5
+		<me-tabs v-if="isShowSticky" v-model="tabIndex" :fixed="true" :tabs="tabs" @change="tabChange"></me-tabs>
6
+		
7
+		 <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :up="upOption" @scroll="scroll" @topclick="topClick">
8
+			<!--轮播-->
9
+			<swiper style="min-height: 300rpx" autoplay="true" interval="3000" duration="300" circular="true">
10
+		        <swiper-item>
11
+		            <image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper1.jpg" mode="widthFix"/>
12
+		        </swiper-item>
13
+				<swiper-item>
14
+		            <image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper2.jpg" mode="widthFix"/>
15
+		        </swiper-item>
16
+		    </swiper>
17
+			
18
+			<view class="demo-tip">
19
+				<view>列表只初始化一次,切换菜单缓存数据</view>
20
+				<view>吸顶通过监听滚动条实现, 比sticky复杂, 但APP端可兼容低端机</view>
21
+			</view>
22
+			
23
+			<!-- 菜单 (在mescroll-uni中不能使用fixed,否则iOS滚动时会抖动, 所以需在mescroll-uni之外存在一个一样的菜单) -->
24
+			<view id="tabInList">
25
+				<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
26
+			</view>
27
+			
28
+			<!-- 数据列表 -->
29
+			<good-list :list="goods"></good-list>
30
+		</mescroll-body>
31
+	</view>
32
+</template>
33
+
34
+<script>
35
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
36
+	import {apiSearch} from "@/api/mock.js"
37
+	
38
+	export default {
39
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
40
+		data() {
41
+			return {
42
+				upOption: {
43
+					// 如果用mescroll-uni 则需要onScroll: true, 且需要 @scroll="scroll"; 而mescroll-body最简单只需在onPageScroll处理即可
44
+					// onScroll: true // 是否监听滚动事件, 默认false (配置为true时,可@scroll="scroll"获取到滚动条位置和方向)
45
+				},
46
+				tabs:[
47
+					{name:'全部', goods: null, num:1, y:0, curPageLen:0, hasNext:true},
48
+					{name:'母婴', goods: null, num:1, y:0, curPageLen:0, hasNext:true},
49
+					{name:'图书', goods: null, num:1, y:0, curPageLen:0, hasNext:true}
50
+					],
51
+				tabIndex: 0, // 当前菜单下标
52
+				preIndex: 0, // 前一个菜单下标
53
+				navTop: null, // nav距离到顶部的距离 (如计算不准确,可直接写死某个值)
54
+				isShowSticky: false // 是否悬浮
55
+			}
56
+		},
57
+		computed: {
58
+			// 列表数据
59
+			goods() {
60
+				return this.tabs[this.tabIndex].goods
61
+			}
62
+		},
63
+		methods: {
64
+			/*下拉刷新的回调 */
65
+			downCallback() {
66
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
67
+				// loadSwiper();
68
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
69
+				this.mescroll.resetUpScroll()
70
+			},
71
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
72
+			upCallback(page) {
73
+				//联网加载数据
74
+				if(this.isChangeTab){
75
+					this.mescroll.hideUpScroll(); // 切换菜单,不显示mescroll进度, 显示系统进度条
76
+					uni.showLoading();
77
+				}
78
+				let keyword = this.tabs[this.tabIndex].name;
79
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
80
+					//联网成功的回调
81
+					
82
+					// 当前tab数据
83
+					let curTab = this.tabs[this.tabIndex]
84
+					
85
+					//设置列表数据
86
+					if(page.num == 1) curTab.goods = []; //如果是第一页需手动制空列表
87
+					curTab.goods=curTab.goods.concat(curPageData); //追加新数据
88
+					
89
+					// 数据渲染完毕再隐藏加载状态 this.$nextTick在iOS真机不触发,需改成setTimeout
90
+					// this.$nextTick(()=>{
91
+					setTimeout(()=>{
92
+						// 需先隐藏加载状态
93
+						this.mescroll.endSuccess(curPageData.length);
94
+						// 再记录当前页的数据
95
+						curTab.num = page.num; // 页码
96
+						curTab.curPageLen = curPageData.length; // 当前页长
97
+						curTab.hasNext = this.mescroll.optUp.hasNext; // 是否还有下一页
98
+						
99
+						// 设置nav到顶部的距离 (需根据自身的情况获取navTop的值, 这里放到列表数据渲染完毕之后)
100
+						// 也可以放到onReady里面,或者菜单顶部的数据(轮播等)加载完毕之后..
101
+						if(!this.navTop) this.setNavTop()
102
+						// 保持tab悬浮,列表数据显示第一条
103
+						if(this.isChangeTab){
104
+							this.isChangeTab = false;
105
+							uni.hideLoading();
106
+							if(this.isShowSticky) this.mescroll.scrollTo(this.navTop, 0)
107
+						}
108
+					},20)
109
+				}).catch(()=>{
110
+					//联网失败, 结束加载
111
+					this.mescroll.endErr();
112
+				})
113
+			},
114
+			// 设置nav到顶部的距离 (滚动条为0, 菜单顶部的数据加载完毕获取到的navTop数值是最精确的)
115
+			setNavTop(){
116
+				let view = uni.createSelectorQuery().select('#tabInList');
117
+				view.boundingClientRect(data => {
118
+					console.log('tabInList基本信息 = ' + JSON.stringify(data));
119
+					this.navTop = data.top // 到屏幕顶部的距离
120
+				}).exec();
121
+			},
122
+			// mescroll-uni的滚动事件 (需在up配置onScroll:true才生效)
123
+			// 而mescroll-body最简单只需在onPageScroll处理即可
124
+			scroll(){
125
+				console.log('滚动条位置 = ' + this.mescroll.getScrollTop() + ', navTop = ' + this.navTop);
126
+				// 菜单悬浮的原理: 监听滚动条的位置大于某个值时,控制顶部菜单的显示和隐藏
127
+				if (this.mescroll.getScrollTop() >= this.navTop) {
128
+					this.isShowSticky = true // 显示悬浮菜单
129
+				} else {
130
+					this.isShowSticky = false // 隐藏悬浮菜单
131
+				}
132
+			},
133
+			// 点击回到顶部按钮时,先隐藏悬浮菜单,避免闪动
134
+			topClick(){
135
+				this.isShowSticky = false
136
+			},
137
+			// 切换菜单
138
+			tabChange (index) {
139
+				// 记录前一个菜单的数据
140
+				let preTab = this.tabs[this.preIndex]
141
+				preTab.y = this.mescroll.getScrollTop(); // 滚动条位置
142
+				this.preIndex = index;
143
+				// 当前菜单的数据
144
+				let curTab = this.tabs[index]
145
+				if (!curTab.goods) {
146
+					// 没有初始化,则初始化
147
+					this.isChangeTab = true;
148
+					this.mescroll.resetUpScroll()
149
+				} else{
150
+					// 初始化过,则恢复之前的列表数据
151
+					this.mescroll.setPageNum(curTab.num + 1); // 恢复当前页码
152
+					this.mescroll.endSuccess(curTab.curPageLen, curTab.hasNext); // 恢复是否有下一页或显示空布局
153
+					this.$nextTick(()=>{
154
+						this.mescroll.scrollTo(curTab.y, 0) // 恢复滚动条的位置
155
+					})
156
+				}
157
+			}
158
+		},
159
+		// 使用mescroll-body最简单只需在onPageScroll处理即可
160
+		onPageScroll(e){
161
+			console.log('滚动条位置 = ' + e.scrollTop + ', navTop = ' + this.navTop);
162
+			if (e.scrollTop >= this.navTop) {
163
+				this.isShowSticky = true // 显示悬浮菜单
164
+			} else {
165
+				this.isShowSticky = false // 隐藏悬浮菜单
166
+			}
167
+		}
168
+	}
169
+</script>
170
+
171
+<style>
172
+	.demo-tip{
173
+		padding: 18rpx;
174
+		font-size: 24rpx;
175
+		text-align: center;
176
+	}
177
+</style>

+ 142
- 0
oa-ui-app/uni_modules/mescroll/pages/base/sticky-scroll.vue Voir le fichier

@@ -0,0 +1,142 @@
1
+<!-- 菜单悬浮的原理: 监听滚动条的位置大于某个值时,控制顶部菜单的显示和隐藏, 用法比sticky复杂, 但APP端可兼容低端机 -->
2
+<template>
3
+	<view>
4
+		<!-- 菜单 (悬浮,预先隐藏)-->
5
+		<me-tabs v-if="isShowSticky" v-model="tabIndex" :fixed="true" :tabs="tabs" @change="tabChange"></me-tabs>
6
+		
7
+		 <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :up="upOption" @scroll="scroll" @topclick="topClick">
8
+			<!--轮播-->
9
+			<swiper style="min-height: 300rpx" autoplay="true" interval="3000" duration="300" circular="true">
10
+		        <swiper-item>
11
+		            <image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper1.jpg" mode="widthFix"/>
12
+		        </swiper-item>
13
+				<swiper-item>
14
+		            <image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper2.jpg" mode="widthFix"/>
15
+		        </swiper-item>
16
+		    </swiper>
17
+			
18
+			<view class="demo-tip">
19
+				<view>每次切换菜单都刷新列表数据</view>
20
+				<view>吸顶通过监听滚动条实现, 比sticky复杂, 但APP端可兼容低端机</view>
21
+			</view>
22
+			
23
+			<!-- 菜单 (在mescroll-uni中不能使用fixed,否则iOS滚动时会抖动, 所以需在mescroll-uni之外存在一个一样的菜单) -->
24
+			<view id="tabInList">
25
+				<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
26
+			</view>
27
+			
28
+			<!-- 数据列表 -->
29
+			<good-list :list="goods"></good-list>
30
+		</mescroll-body>
31
+	</view>
32
+</template>
33
+
34
+<script>
35
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
36
+	import {apiSearch} from "@/api/mock.js"
37
+	
38
+	export default {
39
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
40
+		data() {
41
+			return {
42
+				goods: [], // 数据列表
43
+				upOption: {
44
+					// 如果用mescroll-uni 则需要onScroll: true, 且需要 @scroll="scroll"; 而mescroll-body最简单只需在onPageScroll处理即可
45
+					// onScroll: true // 是否监听滚动事件, 默认false (配置为true时,可@scroll="scroll"获取到滚动条位置和方向)
46
+				},
47
+				tabs: ['全部', '母婴', '图书'],
48
+				tabIndex: 0, // tab下标
49
+				navTop: null, // nav距离到顶部的距离 (如计算不准确,可直接写死某个值)
50
+				isShowSticky: false // 是否悬浮
51
+			}
52
+		},
53
+		methods: {
54
+			/*下拉刷新的回调 */
55
+			downCallback() {
56
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
57
+				// loadSwiper();
58
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
59
+				this.mescroll.resetUpScroll()
60
+			},
61
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
62
+			upCallback(page) {
63
+				//联网加载数据
64
+				if(this.isChangeTab){
65
+					this.mescroll.hideUpScroll(); // 切换菜单,不显示mescroll进度, 显示系统进度条
66
+					uni.showLoading();
67
+				}
68
+				let keyword = this.tabs[this.tabIndex]
69
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
70
+					//联网成功的回调
71
+
72
+					//设置列表数据
73
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
74
+					this.goods=this.goods.concat(curPageData); //追加新数据
75
+					
76
+					// 数据渲染完毕再隐藏加载状态 this.$nextTick在iOS真机不触发,需改成setTimeout
77
+					// this.$nextTick(()=>{
78
+					setTimeout(()=>{
79
+						this.mescroll.endSuccess(curPageData.length);
80
+						// 设置nav到顶部的距离 (需根据自身的情况获取navTop的值, 这里放到列表数据渲染完毕之后)
81
+						// 也可以放到onReady里面,或者菜单顶部的数据(轮播等)加载完毕之后..
82
+						if(!this.navTop) this.setNavTop()
83
+						// 保持tab悬浮,列表数据显示第一条
84
+						if(this.isChangeTab){
85
+							this.isChangeTab = false;
86
+							uni.hideLoading();
87
+							if(this.isShowSticky) this.mescroll.scrollTo(this.navTop, 0)
88
+						}
89
+					},20)
90
+				}).catch(()=>{
91
+					//联网失败, 结束加载
92
+					this.mescroll.endErr();
93
+				})
94
+			},
95
+			// 设置nav到顶部的距离 (滚动条为0, 菜单顶部的数据加载完毕获取到的navTop数值是最精确的)
96
+			setNavTop(){
97
+				let view = uni.createSelectorQuery().select('#tabInList');
98
+				view.boundingClientRect(data => {
99
+					console.log('tabInList基本信息 = ' + JSON.stringify(data));
100
+					this.navTop = data.top // 到屏幕顶部的距离
101
+				}).exec();
102
+			},
103
+			// mescroll-uni的滚动事件 (需在up配置onScroll:true才生效)
104
+			// 而mescroll-body最简单只需在onPageScroll处理即可
105
+			scroll(){
106
+				console.log('滚动条位置 = ' + this.mescroll.getScrollTop() + ', navTop = ' + this.navTop);
107
+				// 菜单悬浮的原理: 监听滚动条的位置大于某个值时,控制顶部菜单的显示和隐藏
108
+				if (this.mescroll.getScrollTop() >= this.navTop) {
109
+					this.isShowSticky = true // 显示悬浮菜单
110
+				} else {
111
+					this.isShowSticky = false // 隐藏悬浮菜单
112
+				}
113
+			},
114
+			// 点击回到顶部按钮时,先隐藏悬浮菜单,避免闪动
115
+			topClick(){
116
+				this.isShowSticky = false
117
+			},
118
+			// 切换菜单
119
+			tabChange () {
120
+				this.isChangeTab = true;
121
+				this.mescroll.resetUpScroll()
122
+			}
123
+		},
124
+		// 使用mescroll-body最简单只需在onPageScroll处理即可
125
+		onPageScroll(e){
126
+			console.log('滚动条位置 = ' + e.scrollTop + ', navTop = ' + this.navTop);
127
+			if (e.scrollTop >= this.navTop) {
128
+				this.isShowSticky = true // 显示悬浮菜单
129
+			} else {
130
+				this.isShowSticky = false // 隐藏悬浮菜单
131
+			}
132
+		}
133
+	}
134
+</script>
135
+
136
+<style>
137
+	.demo-tip{
138
+		padding: 18rpx;
139
+		font-size: 24rpx;
140
+		text-align: center;
141
+	}
142
+</style>

+ 93
- 0
oa-ui-app/uni_modules/mescroll/pages/base/sticky-uni.vue Voir le fichier

@@ -0,0 +1,93 @@
1
+<template>
2
+	<view>
3
+		<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback">
4
+			<swiper style="min-height: 300rpx" autoplay="true" interval="3000" duration="300" circular="true">
5
+				<swiper-item>
6
+					<image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper1.jpg" mode="widthFix"/>
7
+				</swiper-item>
8
+				<swiper-item>
9
+					<image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper2.jpg" mode="widthFix"/>
10
+				</swiper-item>
11
+			</swiper>
12
+			
13
+			<view class="demo-tip">
14
+				<view>仅测试mescroll-uni使用sticky的情况</view>
15
+				<view>与mescroll-body使用的区别:</view>
16
+				<view>1. mescroll-uni 无需配置 :sticky="true"</view>
17
+				<view>2. sticky的top 无需考虑var(--window-top)</view>
18
+			</view>
19
+			
20
+			<!-- sticky吸顶悬浮的菜单, 父元素必须是 mescroll -->
21
+			<view class="sticky-tabs">
22
+				<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
23
+			</view>
24
+			
25
+			<!-- 数据列表 -->
26
+			<good-list :list="goods"></good-list>
27
+		</mescroll-uni>
28
+	</view>
29
+</template>
30
+
31
+<script>
32
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
33
+	import {apiSearch} from "@/api/mock.js"
34
+	
35
+	export default {
36
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
37
+		data() {
38
+			return {
39
+				goods: [], // 数据列表
40
+				tabs: ['全部', '母婴', '图书'],
41
+				tabIndex: 0 // tab下标
42
+			}
43
+		},
44
+		methods: {
45
+			/*下拉刷新的回调 */
46
+			downCallback() {
47
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
48
+				// loadSwiper();
49
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
50
+				this.mescroll.resetUpScroll()
51
+			},
52
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
53
+			upCallback(page) {
54
+				let keyword = this.tabs[this.tabIndex]
55
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
56
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
57
+					this.goods=this.goods.concat(curPageData); //追加新数据
58
+					this.mescroll.endSuccess(curPageData.length); // 隐藏加载状态栏
59
+				}).catch(()=>{
60
+					//联网失败, 结束加载
61
+					this.mescroll.endErr();
62
+				})
63
+			},
64
+			// 切换菜单
65
+			tabChange () {
66
+				this.goods = []; // 置空列表,显示加载进度条
67
+				this.mescroll.resetUpScroll()
68
+			}
69
+		}
70
+	}
71
+</script>
72
+
73
+<style lang="scss">
74
+	/*
75
+	sticky生效条件:
76
+	1、父元素不能overflow:hidden或者overflow:auto属性。(mescroll-body设置:sticky="true"即可, mescroll-uni本身没有设置overflow)
77
+	2、必须指定top、bottom、left、right4个值之一,否则只会处于相对定位
78
+	3、父元素的高度不能低于sticky元素的高度
79
+	4、sticky元素仅在其父元素内生效,所以父元素必须是 mescroll
80
+	*/
81
+	.sticky-tabs{
82
+		z-index: 990;
83
+		position: sticky;
84
+		top: 0;
85
+		background-color: #fff;
86
+	}
87
+	
88
+	.demo-tip{
89
+		padding: 18rpx;
90
+		font-size: 24rpx;
91
+		text-align: center;
92
+	}
93
+</style>

+ 107
- 0
oa-ui-app/uni_modules/mescroll/pages/base/sticky.vue Voir le fichier

@@ -0,0 +1,107 @@
1
+<!-- 菜单悬浮的原理: 通过给菜单添加position:sticky实现, 用法超简单, 仅APP端的低端机不兼容 https://caniuse.com/#feat=css-sticky -->
2
+<template>
3
+	<view>
4
+		<!-- 对于mescroll-body: 需设置:sticky="true", 此应避免在mescroll-body标签前面加其他非定位的元素, 否则下拉区域会被挤出, 无法会隐藏.-->
5
+		<!-- 对于mescroll-uni: 则无需设置:sticky="true", 无其他限制和要求 -->
6
+		<mescroll-body :sticky="true" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback">
7
+			<swiper style="min-height: 300rpx" autoplay="true" interval="3000" duration="300" circular="true">
8
+				<swiper-item>
9
+					<image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper1.jpg" mode="widthFix"/>
10
+				</swiper-item>
11
+				<swiper-item>
12
+					<image style="width: 100%;height: auto;" src="https://www.mescroll.com/img/swiper2.jpg" mode="widthFix"/>
13
+				</swiper-item>
14
+			</swiper>
15
+			
16
+			<view class="demo-tip">
17
+				<view>每次切换菜单,都刷新列表数据</view>
18
+				<view>吸顶通过给菜单加position:sticky实现, 用法简单</view>
19
+				<view>小程序和微信h5端: 低端机sticky也可生效, 可放心使用</view>
20
+				<view>APP端: 仅部分低端机无效,若要兼容则参考sticky-scroll</view>
21
+			</view>
22
+			
23
+			<!-- sticky吸顶悬浮的菜单, 父元素必须是 mescroll -->
24
+			<view class="sticky-tabs">
25
+				<me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs>
26
+			</view>
27
+			
28
+			<!-- 数据列表 -->
29
+			<good-list :list="goods"></good-list>
30
+		</mescroll-body>
31
+		
32
+		<!-- 此处可以写其他fixed定位元素 -->
33
+		<!-- <view></view> -->
34
+	</view>
35
+</template>
36
+
37
+<script>
38
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
39
+	import {apiSearch} from "@/api/mock.js"
40
+	
41
+	export default {
42
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
43
+		data() {
44
+			return {
45
+				goods: [], // 数据列表
46
+				tabs: ['全部', '母婴', '图书'],
47
+				tabIndex: 0 // tab下标
48
+			}
49
+		},
50
+		methods: {
51
+			/*下拉刷新的回调 */
52
+			downCallback() {
53
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
54
+				// loadSwiper();
55
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
56
+				this.mescroll.resetUpScroll()
57
+			},
58
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
59
+			upCallback(page) {
60
+				let keyword = this.tabs[this.tabIndex]
61
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
62
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
63
+					this.goods=this.goods.concat(curPageData); //追加新数据
64
+					this.mescroll.endSuccess(curPageData.length); // 隐藏加载状态栏
65
+				}).catch(()=>{
66
+					//联网失败, 结束加载
67
+					this.mescroll.endErr();
68
+				})
69
+			},
70
+			// 切换菜单
71
+			tabChange () {
72
+				this.goods = []; // 置空列表,显示加载进度条
73
+				this.mescroll.resetUpScroll()
74
+			}
75
+		}
76
+	}
77
+</script>
78
+
79
+<style lang="scss">
80
+	/*
81
+	sticky生效条件:
82
+	1、父元素不能overflow:hidden或者overflow:auto属性。(mescroll-body设置:sticky="true"即可, mescroll-uni本身没有设置overflow)
83
+	2、必须指定top、bottom、left、right4个值之一,否则只会处于相对定位
84
+	3、父元素的高度不能低于sticky元素的高度
85
+	4、sticky元素仅在其父元素内生效,所以父元素必须是 mescroll
86
+	*/
87
+	.sticky-tabs{
88
+		z-index: 990;
89
+		position: sticky;
90
+		top: var(--window-top);
91
+		background-color: #fff;
92
+	}
93
+	
94
+	// 使用mescroll-uni,则top为0
95
+	.mescroll-uni,
96
+	/deep/.mescroll-uni{
97
+		.sticky-tabs{
98
+			top: 0;
99
+		}
100
+	}
101
+	
102
+	.demo-tip{
103
+		padding: 18rpx;
104
+		font-size: 24rpx;
105
+		text-align: center;
106
+	}
107
+</style>

+ 132
- 0
oa-ui-app/uni_modules/mescroll/pages/index/index.vue Voir le fichier

@@ -0,0 +1,132 @@
1
+<template>
2
+	<view>
3
+		
4
+		<view class="group-title">base demos 基础案例</view>
5
+		
6
+		<navigator url="/pages/base/list-news">
7
+			<view class="demo-li">list-news 新闻列表 <text class="demo-tip">下拉刷新添加数据到列表顶部</text></view>
8
+		</navigator>
9
+		
10
+		<navigator url="/pages/base/list-products">
11
+			<view class="demo-li">list-products 商品列表 <text class="demo-tip">下拉刷新重置列表数据</text></view>
12
+		</navigator>
13
+		
14
+		<navigator url="/pages/base/mescroll-options">
15
+			<view class="demo-li">mescroll-options 所有配置项 <text class="demo-tip">快速熟悉mescroll</text></view>
16
+		</navigator>
17
+		
18
+		<navigator url="/pages/base/mescroll-comp">
19
+			<view class="demo-li">mescroll-body写在子组件中 <text class="demo-tip">需引mescroll-comp.js</text></view>
20
+		</navigator>
21
+		
22
+		<navigator url="/pages/base/mescroll-one">
23
+			<view class="demo-li">mescroll-one 单mescroll<text class="demo-tip">切换菜单,及时刷新数据</text></view>
24
+		</navigator>
25
+		
26
+		<navigator url="/pages/base/mescroll-more">
27
+			<view class="demo-li">mescroll-more 多mescroll<text class="demo-tip">列表仅初始化一次,缓存数据</text></view>
28
+		</navigator>
29
+		
30
+		<navigator url="/pages/base/list-search">
31
+			<view class="demo-li">list-search 商品搜索<text class="demo-tip">this.mescroll.resetUpScroll()的使用</text></view>
32
+		</navigator>
33
+		
34
+		<navigator url="/pages/base/list-msg">
35
+			<view class="demo-li">list-msg 聊天记录 <text class="demo-tip">保持当前内容的位置</text></view>
36
+		</navigator>
37
+		
38
+		<navigator url="/pages/base/mescroll-native">
39
+			<view class="demo-li">mescroll-native<text class="demo-tip">系统自带的下拉刷新,性能最好</text></view>
40
+		</navigator>
41
+		
42
+		<navigator url="/pages/base/mescroll-uni">
43
+			<view class="demo-li">mescroll-uni<text class="demo-tip">基于scroll-view,常用在浮窗弹层等局部区域</text></view>
44
+		</navigator>
45
+		
46
+		<navigator url="/pages/base/mescroll-uni-part">
47
+			<view class="demo-li">mescroll-uni-part<text class="demo-tip">mescroll-uni用flex实现局部区域滚动</text></view>
48
+		</navigator>
49
+		
50
+		<navigator url="/pages/base/mescroll-body-part">
51
+			<view class="demo-li">mescroll-body-part<text class="demo-tip">mescroll-body实现"局部区域"滚动</text></view>
52
+		</navigator>
53
+		
54
+		<view class="group-title">intermediate demos 中级案例</view>
55
+		
56
+		<navigator url="/pages/base/sticky">
57
+			<view class="demo-li">sticky吸顶悬浮<text class="demo-tip">切换菜单刷新列表, 原生css实现</text></view>
58
+		</navigator>
59
+		
60
+		<navigator url="/pages/base/sticky-data">
61
+			<view class="demo-li">sticky-data吸顶悬浮<text class="demo-tip">切换菜单缓存数据, 原生css实现</text></view>
62
+		</navigator>
63
+		
64
+		<navigator url="/pages/base/sticky-scroll">
65
+			<view class="demo-li">sticky-scroll吸顶悬浮<text class="demo-tip">切换tab刷新列表,监听滚动实现</text></view>
66
+		</navigator>
67
+		
68
+		<navigator url="/pages/base/sticky-scroll-data">
69
+			<view class="demo-li">sticky-scroll-data吸顶悬浮<text class="demo-tip">切换tab缓存数据,监听滚动实现</text></view>
70
+		</navigator>
71
+		
72
+		<navigator url="/pages/base/sticky-uni">
73
+			<view class="demo-li">sticky-uni吸顶悬浮<text class="demo-tip">测试mescroll-uni使用sticky</text></view>
74
+		</navigator>
75
+		
76
+		<navigator url="/pages/intermediate/mescroll-swiper">
77
+			<view class="demo-li">mescroll-swiper<text class="demo-tip">轮播菜单导航</text></view>
78
+		</navigator>
79
+		
80
+		<navigator url="/pages/intermediate/beibei">
81
+			<view class="demo-li">仿【贝贝】下拉刷新上拉加载<text class="demo-tip">自定义mescroll组件</text></view>
82
+		</navigator>
83
+		
84
+		<navigator url="/pages/intermediate/xinlang">
85
+			<view class="demo-li">仿【新浪微博】下拉刷新上拉加载<text class="demo-tip">自定义mescroll组件</text></view>
86
+		</navigator>
87
+		
88
+		
89
+		<view class="group-title">senior demos 高级案例</view>
90
+		<view class="demo-li disable">仿【美囤妈妈】下拉刷新上拉加载<text class="demo-tip">请到官网获取</text></view>
91
+		<view class="demo-li disable">仿【美团】下拉刷新上拉加载<text class="demo-tip">请到官网获取</text></view>
92
+		<view class="demo-li disable">仿【京东】下拉刷新上拉加载<text class="demo-tip">请到官网获取</text></view>
93
+		<view class="demo-li disable">仿【淘宝】下拉刷新上拉加载<text class="demo-tip">请到官网获取</text></view>
94
+	</view>
95
+</template>
96
+
97
+<script>
98
+	export default {
99
+		onLoad() {
100
+			uni.setNavigationBarTitle({
101
+				title: 'mescroll ('+ uni.getSystemInfoSync().platform + ')'
102
+			})
103
+		}
104
+	}
105
+</script>
106
+
107
+<style>
108
+	.group-title {
109
+		font-size: 30upx;
110
+		padding: 24upx;
111
+		border-bottom: 1upx solid #eee;
112
+		color: red;
113
+	}
114
+
115
+	.demo-li {
116
+		font-size: 28upx;
117
+		padding: 24upx;
118
+		border-bottom: 1upx solid #eee;
119
+		color: #18B4FE;
120
+	}
121
+	
122
+	.demo-li.disable{
123
+		color: gray;
124
+	}
125
+	
126
+	.demo-li .demo-tip {
127
+		float: right;
128
+		margin-top: 4upx;
129
+		font-size: 24upx;
130
+		color: gray;
131
+	}
132
+</style>

+ 65
- 0
oa-ui-app/uni_modules/mescroll/pages/intermediate/beibei.vue Voir le fichier

@@ -0,0 +1,65 @@
1
+<template>
2
+	<view>
3
+		<!-- 模拟的标题 -->
4
+		<image class="header" src="https://www.mescroll.com/img/beibei/header.jpg" mode="aspectFit"/>
5
+		
6
+		<mescroll-body-diy ref="mescrollRef" @init="mescrollInit" top="180" bottom="100" @down="downCallback" @up="upCallback">
7
+			<!-- 模拟的内容 -->
8
+			<image src="https://www.mescroll.com/img/beibei/beibei1.jpg" mode="widthFix"/>
9
+			<image src="https://www.mescroll.com/img/beibei/beibei2.jpg" mode="widthFix"/>
10
+			<!-- 分页的数据列表 -->
11
+			<good-list :list="goods"></good-list>
12
+		</mescroll-body-diy>
13
+		
14
+		<!-- 模拟的底部 -->
15
+		<image class="footer" src="https://www.mescroll.com/img/beibei/footer.jpg" mode="aspectFit"/>
16
+	</view>
17
+</template>
18
+
19
+<script>
20
+	import MescrollBodyDiy from "@/components/mescroll-diy/beibei/mescroll-body.vue";
21
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
22
+	import {apiGoods} from "@/api/mock.js"
23
+	
24
+	export default {
25
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
26
+		components: {
27
+			MescrollBodyDiy // 避免与main.js注册的全局组件名称相同,否则注册组件失效(iOS真机 APP HBuilderX2.7.9)
28
+		},
29
+		data() {
30
+			return {
31
+				goods: [] // 数据列表
32
+			}
33
+		},
34
+		methods: {
35
+			/*下拉刷新的回调 */
36
+			downCallback() {
37
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
38
+				// loadSwiper();
39
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
40
+				this.mescroll.resetUpScroll()
41
+			},
42
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
43
+			upCallback(page) {
44
+				//联网加载数据
45
+				apiGoods(page.num, page.size).then(curPageData=>{
46
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
47
+					this.mescroll.endSuccess(curPageData.length);
48
+
49
+					//设置列表数据
50
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
51
+					this.goods=this.goods.concat(curPageData); //追加新数据
52
+				}).catch(()=>{
53
+					//联网失败, 结束加载
54
+					this.mescroll.endErr();
55
+				})
56
+			}
57
+		}
58
+	}
59
+</script>
60
+
61
+<style>
62
+	image{width: 100%;vertical-align: bottom;height:auto}
63
+	.header{z-index: 9900;position: fixed;top: --window-top;left: 0;height: 180upx;background: white;}
64
+	.footer{z-index: 9900;position: fixed;bottom: 0;left: 0;height: 100upx;background: white;}
65
+</style>

+ 86
- 0
oa-ui-app/uni_modules/mescroll/pages/intermediate/mescroll-swiper-item.vue Voir le fichier

@@ -0,0 +1,86 @@
1
+<template>
2
+	<!-- 
3
+	swiper中的transfrom会使fixed失效,此时用height="100%"固定高度; 
4
+	swiper中无法触发mescroll-mixins.js的onPageScroll和onReachBottom方法,只能用mescroll-uni,不能用mescroll-body
5
+	-->
6
+	<!-- ref动态生成: 字节跳动小程序编辑器不支持一个页面存在相同的ref (如不考虑字节跳动小程序可固定值为 ref="mescrollRef") -->
7
+	 <mescroll-uni :ref="'mescrollRef'+i" @init="mescrollInit" height="100%" top="60" :down="downOption" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
8
+		<!-- 数据列表 -->
9
+		<good-list :list="goods"></good-list>
10
+	</mescroll-uni>
11
+</template>
12
+
13
+<script>
14
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
15
+	import MescrollMoreItemMixin from "@/components/mescroll-uni/mixins/mescroll-more-item.js";
16
+	import {apiSearch} from "@/api/mock.js"
17
+	
18
+	export default {
19
+		mixins: [MescrollMixin,MescrollMoreItemMixin], // 注意此处还需使用MescrollMoreItemMixin (必须写在MescrollMixin后面)
20
+		data() {
21
+			return {
22
+				downOption:{
23
+					auto:false // 不自动加载 (mixin已处理第一个tab触发downCallback)
24
+				},
25
+				upOption:{
26
+					auto:false, // 不自动加载
27
+					// page: {
28
+					// 	num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
29
+					// 	size: 10 // 每页数据的数量
30
+					// },
31
+					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
32
+					empty:{
33
+						tip: '~ 空空如也 ~', // 提示
34
+						btnText: '去看看'
35
+					}
36
+				},
37
+				goods: [] //列表数据
38
+			}
39
+		},
40
+		props:{
41
+			i: Number, // 每个tab页的专属下标 (除了支付宝小程序必须在这里定义, 其他平台都可不用写, 因为已在MescrollMoreItemMixin定义)
42
+			index: { // 当前tab的下标 (除了支付宝小程序必须在这里定义, 其他平台都可不用写, 因为已在MescrollMoreItemMixin定义)
43
+				type: Number,
44
+				default(){
45
+					return 0
46
+				}
47
+			},
48
+			tabs: { // 为了请求数据,演示用,可根据自己的项目判断是否要传
49
+				type: Array,
50
+				default(){
51
+					return []
52
+				}
53
+			}
54
+		},
55
+		methods: {
56
+			/*下拉刷新的回调 */
57
+			downCallback() {
58
+				// 这里加载你想下拉刷新的数据, 比如刷新轮播数据
59
+				// loadSwiper();
60
+				// 下拉刷新的回调,默认重置上拉加载列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
61
+				this.mescroll.resetUpScroll()
62
+			},
63
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
64
+			upCallback(page) {
65
+				//联网加载数据
66
+				let keyword = this.tabs[this.i].name
67
+				apiSearch(page.num, page.size, keyword).then(curPageData=>{
68
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
69
+					this.mescroll.endSuccess(curPageData.length);
70
+					//设置列表数据
71
+					if(page.num == 1) this.goods = []; //如果是第一页需手动制空列表
72
+					this.goods=this.goods.concat(curPageData); //追加新数据
73
+				}).catch(()=>{
74
+					//联网失败, 结束加载
75
+					this.mescroll.endErr();
76
+				})
77
+			},
78
+			//点击空布局按钮的回调
79
+			emptyClick(){
80
+				uni.showToast({
81
+					title:'点击了按钮,具体逻辑自行实现'
82
+				})
83
+			}
84
+		}
85
+	}
86
+</script>

+ 58
- 0
oa-ui-app/uni_modules/mescroll/pages/intermediate/mescroll-swiper.vue Voir le fichier

@@ -0,0 +1,58 @@
1
+<template>
2
+	<view>
3
+		<!-- 当设置tab-width,指定每个tab宽度时,则不使用flex布局,改用水平滑动 -->
4
+		<me-tabs v-model="tabIndex" :tabs="tabs" :fixed="true" :tab-width="130"></me-tabs>
5
+		<swiper :style="{height: height}" :current="tabIndex" @change="swiperChange">
6
+			<swiper-item v-for="(tab,i) in tabs" :key="i">
7
+				<mescroll-item ref="mescrollItem" :i="i" :index="tabIndex" :tabs="tabs"></mescroll-item>
8
+			</swiper-item>
9
+		</swiper>
10
+	</view>
11
+</template>
12
+
13
+<script>
14
+	import MescrollItem from "./mescroll-swiper-item.vue";
15
+	
16
+	export default {
17
+		components: {
18
+			MescrollItem
19
+		},
20
+		data() {
21
+			return {
22
+				height: "400px", // 需要固定swiper的高度
23
+				tabs: [{name:'全部'}, {name:'奶粉'}, {name:'面膜'}, {name:'图书'}, {name:'果汁'}, {name:'奶瓶'}, {name:'美素'}, {name:'花王'}, {name:'韩蜜'}],
24
+				tabIndex: 0 // 当前tab的下标
25
+			}
26
+		},
27
+		methods: {
28
+			// 轮播菜单
29
+			swiperChange(e){
30
+				this.tabIndex = e.detail.current
31
+			},
32
+			// 获取指定下标的mescroll对象
33
+			// getMescroll(i){
34
+			// 	let mescrollItems = this.$refs.mescrollItem;
35
+			// 	if(mescrollItems){
36
+			// 		let item = mescrollItems[i]
37
+			// 		if(item) return item.mescroll
38
+			// 	}
39
+			// 	return null
40
+			// }
41
+		},
42
+		onLoad() {
43
+			// 需要固定swiper的高度
44
+			this.height = uni.getSystemInfoSync().windowHeight + 'px'
45
+		},
46
+		onShow() {
47
+			// 返回刷新: https://www.mescroll.com/uni.html#note 第二点
48
+			// if(this.canReset){
49
+			// 	let curMescroll = this.getMescroll(this.tabIndex)
50
+			// 	curMescroll && curMescroll.resetUpScroll()
51
+			// }
52
+			// this.canReset = true
53
+		}
54
+	}
55
+</script>
56
+
57
+<style>
58
+</style>

+ 124
- 0
oa-ui-app/uni_modules/mescroll/pages/intermediate/xinlang.vue Voir le fichier

@@ -0,0 +1,124 @@
1
+<template>
2
+	<view>
3
+		<!-- 模拟的标题 -->
4
+		<image class="header" src="https://www.mescroll.com/img/xinlang/header.jpg" mode="aspectFit"/>
5
+		<view :style="{'top':top}" class="download-tip">1条新微博</view>
6
+		
7
+		<mescroll-body-diy ref="mescrollRef" @init="mescrollInit" top="100" bottom="100" :down="downOption" @down="downCallback" @up="upCallback">
8
+			<!-- 新增的微博 -->
9
+			<view class="news-li" v-for="news in addList" :key="news.id">
10
+				<view>{{news.title}}</view>
11
+				<view class="new-content">{{news.content}}</view>
12
+			</view>
13
+			<!-- 模拟的内容 -->
14
+			<image src="https://www.mescroll.com/img/xinlang/xinlang1.jpg" mode="widthFix"/>
15
+			<!-- 分页的数据 -->
16
+			<view class="news-li" v-for="news in dataList" :key="news.id">
17
+				<view>{{news.title}}</view>
18
+				<view class="new-content">{{news.content}}</view>
19
+			</view>
20
+		</mescroll-body-diy>
21
+		
22
+		<!-- 模拟的底部 -->
23
+		<image class="footer" src="https://www.mescroll.com/img/xinlang/footer.jpg" mode="aspectFit"/>
24
+	</view>
25
+</template>
26
+
27
+<script>
28
+	import MescrollBodyDiy from "@/components/mescroll-diy/xinlang/mescroll-body.vue";
29
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
30
+	import {apiWeiboList} from "@/api/mock.js"
31
+	
32
+	export default {
33
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
34
+		components: {
35
+			MescrollBodyDiy, // 避免与main.js注册的全局组件名称相同,否则注册组件失效(iOS真机 APP HBuilderX2.7.9)
36
+		},
37
+		data() {
38
+			return {
39
+				downOption:{
40
+					auto:false,//是否在初始化完毕之后自动执行下拉回调callback; 默认true
41
+				},
42
+				addList:[],//新增微博
43
+				dataList: [], // 数据列表
44
+				top: 0 //提示,到顶部的距离
45
+			}
46
+		},
47
+		methods: {
48
+			/*下拉刷新的回调 */
49
+			downCallback(){
50
+				//加载轮播数据..
51
+				//...
52
+				//加载列表数据
53
+				apiWeiboList().then(curPageData=>{
54
+					//联网成功的回调,隐藏下拉刷新的状态
55
+					this.mescroll.endSuccess();
56
+					//添加新数据到顶部
57
+					this.addList.unshift(curPageData[0]);
58
+					//显示提示
59
+					// #ifdef H5
60
+					this.top=uni.upx2px(100+88)+"px"; // H5的高度需加上 88的标题栏
61
+					// #endif
62
+					
63
+					// #ifndef H5
64
+					this.top=uni.upx2px(100)+"px"; // 非H5不必加
65
+					// #endif
66
+					setTimeout(()=> {
67
+						this.top=0;
68
+					},2000);
69
+				}).catch(()=>{
70
+					//联网失败, 结束加载
71
+					this.mescroll.endErr();
72
+				})
73
+			},
74
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
75
+			upCallback(page) {
76
+				//联网加载数据
77
+				apiWeiboList(page.num, page.size).then(curPageData=>{
78
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
79
+					this.mescroll.endSuccess(curPageData.length);
80
+
81
+					//追加新数据
82
+					this.dataList=this.dataList.concat(curPageData);
83
+				}).catch(()=>{
84
+					//联网失败, 结束加载
85
+					this.mescroll.endErr();
86
+				})
87
+			}
88
+		}
89
+	}
90
+</script>
91
+
92
+<style>
93
+	image{width: 100%;vertical-align: bottom;height:auto}
94
+	.header{z-index: 9900;position: fixed;top: --window-top;left: 0;height: 100upx;background: #fff;}
95
+	.footer{z-index: 9900;position: fixed;bottom: 0;left: 0;height: 100upx;background: white;}
96
+	
97
+	.download-tip{
98
+		z-index: 900;
99
+		position: fixed;
100
+		top: calc(var(--window-top) + 20upx);
101
+		left: 0;
102
+		width: 100%;
103
+		height: 60upx;
104
+		line-height: 60upx;
105
+		font-size: 24upx;
106
+		text-align: center;
107
+		background-color: rgba(255,130,1,.7);
108
+		color: white;
109
+		-webkit-transition: top 300ms;
110
+		transition: top 300ms;
111
+	}
112
+	
113
+	/*展示上拉加载的数据列表*/
114
+	.news-li{
115
+		padding: 32upx;
116
+		border-bottom: 1upx solid #eee;
117
+	}
118
+	.news-li .new-content{
119
+		font-size: 28upx;
120
+		margin-top: 10upx;
121
+		margin-left: 20upx;
122
+		color: #666;
123
+	}
124
+</style>

BIN
oa-ui-app/uni_modules/mescroll/static/img/mescroll-empty.png Voir le fichier


BIN
oa-ui-app/uni_modules/mescroll/static/img/mescroll-totop.png Voir le fichier


+ 4
- 5
oa-ui/src/views/flowable/form/budget/components/choosePeople.vue Voir le fichier

@@ -38,8 +38,8 @@
38 38
       </el-table-column> -->
39 39
     </el-table>
40 40
     <div style="text-align: right;">
41
-      <el-pagination @current-change="getList" :current-page.sync="queryParams.pageNum" :page-size="queryParams.pageSize"
42
-        layout="total, prev, pager, next" :total="total">
41
+      <el-pagination @current-change="getList" :current-page.sync="queryParams.pageNum"
42
+        :page-size="queryParams.pageSize" layout="total, prev, pager, next" :total="total">
43 43
       </el-pagination>
44 44
     </div>
45 45
     <div v-if="multiple">
@@ -92,10 +92,9 @@ export default {
92 92
     /** 查询用户列表 */
93 93
     getList() {
94 94
       listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
95
-        this.userList = response.rows;
95
+        this.userList = response.rows.filter(item => item.status != '1' && item.status != '2')
96 96
         this.total = response.total;
97
-      }
98
-      );
97
+      });
99 98
     },
100 99
     /** 查询部门 */
101 100
     getDeptTree() {

Loading…
Annuler
Enregistrer