wangsisi 1 день тому
батько
коміт
a3c8bdb99b
100 змінених файлів з 10563 додано та 10 видалено
  1. 1 0
      App.vue
  2. 2 2
      api/config.js
  3. 3 0
      main.js
  4. 4 2
      manifest.json
  5. 51 0
      package-lock.json
  6. 6 0
      package.json
  7. 4 1
      pages.json
  8. 5 5
      pages/tabBar/mine/mine.vue
  9. 3 0
      uni.scss
  10. 21 0
      uni_modules/uview-plus/LICENSE
  11. 74 0
      uni_modules/uview-plus/README.md
  12. 963 0
      uni_modules/uview-plus/changelog.md
  13. 85 0
      uni_modules/uview-plus/components/u--form/u--form.vue
  14. 50 0
      uni_modules/uview-plus/components/u--image/u--image.vue
  15. 74 0
      uni_modules/uview-plus/components/u--input/u--input.vue
  16. 45 0
      uni_modules/uview-plus/components/u--text/u--text.vue
  17. 47 0
      uni_modules/uview-plus/components/u--textarea/u--textarea.vue
  18. 109 0
      uni_modules/uview-plus/components/u-action-sheet-data/u-action-sheet-data.vue
  19. 26 0
      uni_modules/uview-plus/components/u-action-sheet/actionSheet.js
  20. 62 0
      uni_modules/uview-plus/components/u-action-sheet/props.js
  21. 282 0
      uni_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue
  22. 28 0
      uni_modules/uview-plus/components/u-album/album.js
  23. 86 0
      uni_modules/uview-plus/components/u-album/props.js
  24. 299 0
      uni_modules/uview-plus/components/u-album/u-album.vue
  25. 22 0
      uni_modules/uview-plus/components/u-alert/alert.js
  26. 46 0
      uni_modules/uview-plus/components/u-alert/props.js
  27. 250 0
      uni_modules/uview-plus/components/u-alert/u-alert.vue
  28. 23 0
      uni_modules/uview-plus/components/u-avatar-group/avatarGroup.js
  29. 54 0
      uni_modules/uview-plus/components/u-avatar-group/props.js
  30. 109 0
      uni_modules/uview-plus/components/u-avatar-group/u-avatar-group.vue
  31. 28 0
      uni_modules/uview-plus/components/u-avatar/avatar.js
  32. 81 0
      uni_modules/uview-plus/components/u-avatar/props.js
  33. 61 0
      uni_modules/uview-plus/components/u-avatar/u-avatar.vue
  34. 27 0
      uni_modules/uview-plus/components/u-back-top/backtop.js
  35. 56 0
      uni_modules/uview-plus/components/u-back-top/props.js
  36. 132 0
      uni_modules/uview-plus/components/u-back-top/u-back-top.vue
  37. 27 0
      uni_modules/uview-plus/components/u-badge/badge.js
  38. 79 0
      uni_modules/uview-plus/components/u-badge/props.js
  39. 176 0
      uni_modules/uview-plus/components/u-badge/u-badge.vue
  40. 27 0
      uni_modules/uview-plus/components/u-box/props.js
  41. 91 0
      uni_modules/uview-plus/components/u-box/u-box.vue
  42. 43 0
      uni_modules/uview-plus/components/u-button/button.js
  43. 46 0
      uni_modules/uview-plus/components/u-button/nvue.scss
  44. 159 0
      uni_modules/uview-plus/components/u-button/props.js
  45. 503 0
      uni_modules/uview-plus/components/u-button/u-button.vue
  46. 81 0
      uni_modules/uview-plus/components/u-button/vue.scss
  47. 45 0
      uni_modules/uview-plus/components/u-calendar/calendar.js
  48. 109 0
      uni_modules/uview-plus/components/u-calendar/header.vue
  49. 608 0
      uni_modules/uview-plus/components/u-calendar/month.vue
  50. 160 0
      uni_modules/uview-plus/components/u-calendar/props.js
  51. 411 0
      uni_modules/uview-plus/components/u-calendar/u-calendar.vue
  52. 86 0
      uni_modules/uview-plus/components/u-calendar/util.js
  53. 15 0
      uni_modules/uview-plus/components/u-car-keyboard/carKeyboard.js
  54. 17 0
      uni_modules/uview-plus/components/u-car-keyboard/props.js
  55. 314 0
      uni_modules/uview-plus/components/u-car-keyboard/u-car-keyboard.vue
  56. 40 0
      uni_modules/uview-plus/components/u-card/card.js
  57. 134 0
      uni_modules/uview-plus/components/u-card/props.js
  58. 184 0
      uni_modules/uview-plus/components/u-card/u-card.vue
  59. 340 0
      uni_modules/uview-plus/components/u-cate-tab/u-cate-tab.vue
  60. 17 0
      uni_modules/uview-plus/components/u-cell-group/cellGroup.js
  61. 16 0
      uni_modules/uview-plus/components/u-cell-group/props.js
  62. 66 0
      uni_modules/uview-plus/components/u-cell-group/u-cell-group.vue
  63. 35 0
      uni_modules/uview-plus/components/u-cell/cell.js
  64. 112 0
      uni_modules/uview-plus/components/u-cell/props.js
  65. 267 0
      uni_modules/uview-plus/components/u-cell/u-cell.vue
  66. 29 0
      uni_modules/uview-plus/components/u-checkbox-group/checkboxGroup.js
  67. 93 0
      uni_modules/uview-plus/components/u-checkbox-group/props.js
  68. 133 0
      uni_modules/uview-plus/components/u-checkbox-group/u-checkbox-group.vue
  69. 27 0
      uni_modules/uview-plus/components/u-checkbox/checkbox.js
  70. 76 0
      uni_modules/uview-plus/components/u-checkbox/props.js
  71. 388 0
      uni_modules/uview-plus/components/u-checkbox/u-checkbox.vue
  72. 15 0
      uni_modules/uview-plus/components/u-circle-progress/circleProgress.js
  73. 10 0
      uni_modules/uview-plus/components/u-circle-progress/props.js
  74. 200 0
      uni_modules/uview-plus/components/u-circle-progress/u-circle-progress.vue
  75. 161 0
      uni_modules/uview-plus/components/u-city-locate/u-city-locate.vue
  76. 29 0
      uni_modules/uview-plus/components/u-code-input/codeInput.js
  77. 90 0
      uni_modules/uview-plus/components/u-code-input/props.js
  78. 299 0
      uni_modules/uview-plus/components/u-code-input/u-code-input.vue
  79. 21 0
      uni_modules/uview-plus/components/u-code/code.js
  80. 36 0
      uni_modules/uview-plus/components/u-code/props.js
  81. 131 0
      uni_modules/uview-plus/components/u-code/u-code.vue
  82. 19 0
      uni_modules/uview-plus/components/u-col/col.js
  83. 31 0
      uni_modules/uview-plus/components/u-col/props.js
  84. 169 0
      uni_modules/uview-plus/components/u-col/u-col.vue
  85. 31 0
      uni_modules/uview-plus/components/u-collapse-item/collapseItem.js
  86. 97 0
      uni_modules/uview-plus/components/u-collapse-item/props.js
  87. 243 0
      uni_modules/uview-plus/components/u-collapse-item/u-collapse-item.vue
  88. 17 0
      uni_modules/uview-plus/components/u-collapse/collapse.js
  89. 21 0
      uni_modules/uview-plus/components/u-collapse/props.js
  90. 90 0
      uni_modules/uview-plus/components/u-collapse/u-collapse.vue
  91. 25 0
      uni_modules/uview-plus/components/u-column-notice/columnNotice.js
  92. 61 0
      uni_modules/uview-plus/components/u-column-notice/props.js
  93. 165 0
      uni_modules/uview-plus/components/u-column-notice/u-column-notice.vue
  94. 70 0
      uni_modules/uview-plus/components/u-copy/u-copy.vue
  95. 18 0
      uni_modules/uview-plus/components/u-count-down/countDown.js
  96. 26 0
      uni_modules/uview-plus/components/u-count-down/props.js
  97. 167 0
      uni_modules/uview-plus/components/u-count-down/u-count-down.vue
  98. 62 0
      uni_modules/uview-plus/components/u-count-down/utils.js
  99. 25 0
      uni_modules/uview-plus/components/u-count-to/countTo.js
  100. 61 0
      uni_modules/uview-plus/components/u-count-to/props.js

+ 1 - 0
App.vue

@@ -14,5 +14,6 @@
 
 <style lang="scss">
 	/*每个页面公共css */
+	@import "@/uni_modules/uview-plus/index.scss";
 	@import "@/static/style/common.scss"
 </style>

+ 2 - 2
api/config.js

@@ -1,5 +1,5 @@
 const config = {
-	BASIC_IMG: "https://birdseye-img.sysuimars.com/"
+	BASIC_IMG: "https://birdseye-img.sysuimars.com/youwei-uniapp/"
 }
 
-default export config
+export default config

+ 3 - 0
main.js

@@ -11,10 +11,13 @@ const app = new Vue({
 app.$mount()
 // #endif
 
+import uviewPlus from '@/uni_modules/uview-plus'
+
 // #ifdef VUE3
 import { createSSRApp } from 'vue'
 export function createApp() {
   const app = createSSRApp(App)
+  app.use(uviewPlus)
   return {
     app
   }

+ 4 - 2
manifest.json

@@ -54,7 +54,8 @@
         "setting" : {
             "urlCheck" : false
         },
-        "usingComponents" : true
+        "usingComponents" : true,
+		"mergeVirtualHostAttributes" : true
     },
     "mp-alipay" : {
         "usingComponents" : true
@@ -63,7 +64,8 @@
         "usingComponents" : true
     },
     "mp-toutiao" : {
-        "usingComponents" : true
+        "usingComponents" : true,
+		"mergeVirtualHostAttributes" : true
     },
     "uniStatistics" : {
         "enable" : false

+ 51 - 0
package-lock.json

@@ -0,0 +1,51 @@
+{
+  "name": "feiniao-youwei-uniapp",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "clipboard": "^2.0.11",
+        "dayjs": "^1.11.13"
+      }
+    },
+    "node_modules/clipboard": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.13",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
+    },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+    },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    }
+  }
+}

+ 6 - 0
package.json

@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "clipboard": "^2.0.11",
+    "dayjs": "^1.11.13"
+  }
+}

+ 4 - 1
pages.json

@@ -60,7 +60,10 @@
 	"easycom": {
 		"autoscan": true,
 		"custom": {
-		  "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
+			"^u--(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
+			"^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
+			"^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
+			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
 		}
 	}
 }

+ 5 - 5
pages/tabBar/mine/mine.vue

@@ -14,12 +14,12 @@
 			<view class="grid-item ">
 				<view class="grid-name">守护兑换</view>
 				<text class="grid-text">积累能量获大礼</text>
-				<image class="image" src="https://birdseye-img.sysuimars.com/youwei-uniapp/img/gift.png"></image>
+				<image class="image" :src="`${config.BASIC_IMG}img/gift.png`"></image>
 			</view>
 			<view class="grid-item coupon">
 				<view class="grid-name">优惠券</view>
 				<text class="grid-text">满100减50</text>
-				<image class="icon" src="https://birdseye-img.sysuimars.com/youwei-uniapp/img/coupon.png"></image>
+				<image class="icon" :src="`${config.BASIC_IMG}img/coupon.png`"></image>
 			</view>
 		</view>
 		<view class="list-wrap">
@@ -30,7 +30,7 @@
 				</view>
 				<view class="card-cont">
 					<view class="list-item">
-						
+						123
 					</view>
 				</view>
 			</view>
@@ -40,7 +40,7 @@
 			</view>
 			<view class="list-item">
 				<text class="name">联系客服</text>
-				<text>></text>
+				<up-icon name="arrow-right"></up-icon>
 			</view>
 			<view class="list-item">
 				<text class="name">意见反馈</text>
@@ -51,7 +51,7 @@
 </template>
 
 <script setup>
-
+import config from "@/api/config.js"
 </script>
 
 <style lang="scss" scoped>

+ 3 - 0
uni.scss

@@ -12,6 +12,9 @@
  * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
  */
 
+@import "@/uni_modules/uview-plus/theme.scss";  // 使用@别名
+
+
 /* 颜色变量 */
 
 /* 行为相关颜色 */

+ 21 - 0
uni_modules/uview-plus/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 https://uiadmin.net/uview-plus
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 74 - 0
uni_modules/uview-plus/README.md

@@ -0,0 +1,74 @@
+<p align="center">
+    <img alt="logo" src="https://uiadmin.net/uview-plus/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
+</p>
+<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uview-plus 3.0</h3>
+<h3 align="center">多平台快速开发的UI框架</h3>
+
+[![stars](https://img.shields.io/github/stars/ijry/uview-plus?style=flat-square&logo=GitHub)](https://github.com/ijry/uview-plus)
+[![forks](https://img.shields.io/github/forks/ijry/uview-plus?style=flat-square&logo=GitHub)](https://github.com/ijry/uview-plus)
+[![issues](https://img.shields.io/github/issues/ijry/uview-plus?style=flat-square&logo=GitHub)](https://github.com/ijry/uview-plus/issues)
+[![release](https://img.shields.io/github/v/release/ijry/uview-plus?style=flat-square)](https://gitee.com/jry/uview-plus/releases)
+[![license](https://img.shields.io/github/license/ijry/uview-plus?style=flat-square)](https://en.wikipedia.org/wiki/MIT_License)
+
+## 说明
+
+uview-plus,是uni-app全面兼容vue3/nvue/鸿蒙/uni-app-x(即将发布)的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。uview-plus是基于uView2.x移植的支持vue3的版本,感谢uView。
+
+## 可视化设计
+
+uview-plus现已推出免费可视化设计,可以方便的进行页面可视化设计,导出源码即可使用。极大提高前端页面开发效率;如产品经理设计师直接使用更可作为高保真高可用原型制作工具,让设计稿即代码,无需传统的设计稿开发还原步骤。
+
+<img src="https://s3.bmp.ovh/imgs/2024/11/24/fd58d00071e6e5df.png" width="900" height="auto" >
+<img src="https://s3.bmp.ovh/imgs/2024/11/24/8e85a519fe627fb1.png" width="900" height="auto" >
+
+
+## 文档
+[官方文档:https://uview-plus.jiangruyi.com](https://uview-plus.jiangruyi.com)
+[备用文档:https://uiadmin.net/uview-plus](https://uiadmin.net/uview-plus)
+
+
+## 预览
+
+您可以通过**微信**扫码,查看最佳的演示效果。
+<br>
+<br>
+<img src="https://uview-plus.jiangruyi.com/common/h5_qrcode.png" width="220" height="220" >
+
+## 链接
+
+- [官方文档](https://uview-plus.jiangruyi.com)
+- [更新日志](https://uview-plus.jiangruyi.com/components/changelog.html)
+- [升级指南](https://uview-plus.jiangruyi.com/components/changeGuide.html)
+- [关于我们](https://uview-plus.jiangruyi.com/cooperation/about.html)
+
+## 交流反馈
+
+欢迎加入我们的QQ群交流反馈:[点此跳转](https://uview-plus.jiangruyi.com/components/addQQGroup.html)
+
+## 关于PR
+
+> 我们非常乐意接受各位的优质PR,但在此之前我希望您了解uview-plus是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括nvue页面、vue页面。
+> 所以希望在您修复bug并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢!
+
+## 安装
+
+#### **uni-app插件市场链接** —— [https://ext.dcloud.net.cn/plugin?name=uview-plus](https://ext.dcloud.net.cn/plugin?name=uview-plus)
+
+请通过[官网安装文档](https://uview-plus.jiangruyi.com/components/install.html)了解更详细的内容
+
+## 快速上手
+
+请通过[快速上手](https://uview-plus.jiangruyi.com/components/quickstart.html)了解更详细的内容
+
+## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<u-button text="按钮"></u-button>
+</template>
+```
+
+## 版权信息
+uview-plus遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uview-plus应用到您的产品中。
+

+ 963 - 0
uni_modules/uview-plus/changelog.md

@@ -0,0 +1,963 @@
+## 3.4.64(2025-07-25)
+feat: 新增dragsort拖动排序组件
+
+## 3.4.63(2025-07-24)
+feat: count-down支持slot传递时间参数
+
+## 3.4.62(2025-07-24)
+feat: divider支持默认插槽
+
+## 3.4.61(2025-07-24)
+feat: divider支持默认插槽
+
+## 3.4.60(2025-07-23)
+fix: 修复use阶段全局加载图标引起App端不明$page报错
+
+## 3.4.59(2025-07-23)
+fix: 修复album组件和CateTab组件watch和emits重复
+
+fix: 修复样式优先级导致form表单labelPosition属性top失效
+
+## 3.4.58(2025-07-22)
+fix: 修复样式优先级影响grid宫格组件布局
+
+## 3.4.57(2025-07-22)
+feat: 新增title标题组件
+
+## 3.4.56(2025-07-21)
+fix: 去除多余hotCity参数
+
+## 3.4.55(2025-07-21)
+feat: 新增city-locate城市定位选择组件
+
+feat: 优化index-list组件索引
+
+## 3.4.54(2025-07-18)
+feat: dropdown组件的highlight方法支持同时高亮多个菜单项
+
+feat: 支持一次性全局加载icon字体
+
+## 3.4.53(2025-07-18)
+feat: dropdown组件的highlight方法支持同时高亮多个菜单项 感谢@keeplearning66
+
+## 3.4.52(2025-07-16)
+fix: 修复底部安全区域组件兼容性
+
+## 3.4.51(2025-07-14)
+fix: 修复u-slider在click后没有触发change事件
+
+## 3.4.50(2025-07-13)
+feat: subsection分段器添加禁用参数
+
+## 3.4.49(2025-07-11)
+feat: picker支持bgColor、round、duration和overlayOpacity属性
+
+## 3.4.48(2025-07-10)
+fix: 官方文档Card示例组件多行显示省略号样式异常
+
+feat: album组件支持自定义preview事件
+
+## 3.4.47(2025-07-09)
+fix: 修复datetime-picker打开时,数值可能出现对不上的问题
+
+feat: Subsection 分段器添加支持从 list中读取激活文字颜色和未激活文字颜色
+
+fix: 修复modal定义confirmButton
+
+fix: safe-bottom底部安全距离在小程序优先用JS计算
+
+feat: 新增tree树形组件
+
+## 3.4.46(2025-07-08)
+feat: 上传组件预览视频支持videoPreviewObjectFit参数
+
+feat: td增加多个样式props
+
+fix: 修复noticeBar字号增大时文字遮挡
+
+feat: 项目工程增加pinia
+
+## 3.4.45(2025-07-01)
+fix: 修复picker-data组件缺少name
+
+fix: 优化picker高度单位
+
+## 3.4.44(2025-06-30)
+fix: 修复indexList中stikcy属性写死的问题(always true)
+
+feat: 搜索框添加新的右侧插槽
+
+fix: 修复indexList中丢失的select event
+
+fix: 解决因为层级问题导致点击picker选择器无法正常弹出
+
+## 3.4.43(2025-06-16)
+feat: table2支持header插槽
+
+## 3.4.42(2025-06-12)
+fix: 修复qrcode中默认id问题及canvas2时App无法绘制二维码 感谢@jiaruiyan
+
+## 3.4.41(2025-06-11)
+feat: qrcode支持 新参数useRootHeightAndWidth 是否使用根节点的宽高 感谢@YJR
+
+feat: toast支持设置zIndex层级
+
+
+
+## 3.4.40(2025-06-06)
+fix: 升级二维码 canvas -> canvas2 感谢@yjr
+
+## 3.4.39(2025-05-31)
+fix: 修改步骤条微信小程序下的布局 感谢@jiaruiyan
+
+fix: u-tabs在屏幕尺寸发生变化时滑块位置没有发生变化 感谢@aqzhft
+
+fix: 鸿蒙平台不支持plus.runtime.openWeb 感谢@aqzhft
+
+## 3.4.38(2025-05-30)
+fix: 修复picker-data快捷组件缺少index
+
+fix: 修复picker组件双向绑定初始化及取消后复原再次打开后的当前项目
+
+## 3.4.37(2025-05-29)
+feat: modal支持设置动画时间
+
+fix: DatetimePicker v-model 绑定异步设置无效 (#803)
+
+## 3.4.36(2025-05-28)
+fix: lazy-load图片为空时显示错误
+
+## 3.4.35(2025-05-28)
+feat: 进度条支持从右往左加载
+
+## 3.4.34(2025-05-28)
+feat: table2支持自定义标题和单元格样式
+
+## 3.4.33(2025-05-27)
+fix: 修复小程序cate-tab第一次切换时没反应 感谢@jiaruiyan
+
+fix: 修复datetimepicker传入空字符串时导致组件崩溃 感谢@jiaruiyan
+
+fix: 修复album带单位的字符串参与计算导致的计算数据错误 感谢@jiaruiyan
+
+## 3.4.32(2025-05-26)
+feat: 增加状态栏独立颜色配置支持支付宝小程序状态栏对背景色识别的不友好的情况
+
+fix: 抖音二维码兼容修复
+
+feat: cate-tab组件增加rightTop插槽 #715
+
+fix: 修改 test.promise(res) 预期结果不一致
+
+## 3.4.31(2025-05-17)
+fix: 修复parse富文本组件导致鸿蒙运行白屏
+
+fix: 去除演示项目中uni.$u用法便于兼容鸿蒙
+
+feat: modal新增popupBottom插槽适用类似关闭按钮与内容区域分离的场景
+
+## 3.4.30(2025-05-16)
+feat: 新增pagination分页器组件
+
+feat: popup新增bottom插槽适用类似关闭按钮与内容区域分离的场景
+
+## 3.4.29(2025-05-15)
+fix: 修复table2横向滚动样式
+
+fix: 修复table2组件宽度兼容
+
+fix: 修复image显示png图片时默认背景色问题
+
+feat: cate-tab新增height参数便于设置组件高度
+
+feat: 在index.js种导出digit.js便于使用
+
+fix: 修复tag组件缺失iconColor属性
+
+fix: 优化index-list的setValueForTouch方法逻辑 #708
+
+feat: number-box支持change事件返回变动是点击了增加还是减少按钮
+
+fix: 修复table2在小程序下部分情形不显示表格
+
+## 3.4.28(2025-05-12)
+feat: 新增table表格组件
+
+feat: 新增element-plus风格的table2组件
+
+## 3.4.27(2025-05-06)
+fix: 修复card组件props
+
+## 3.4.26(2025-05-06)
+fix: 修复test工具引入
+
+feat: card组件支持全局设置props默认值
+
+fix: 修复image在加载错误情况下高度和宽度不正确问题
+
+fix: 修复picker-data快捷组件默认picker选中
+
+fix: 修复日历month子组件缺失emits定义
+
+## 3.4.25(2025-04-27)
+fix: up-form编译在微信小程序里样式缺失 #640
+
+fix: number-box输入为空时自动设为最小值
+
+feat: picker与datetimepicke组件hasInput模式支持inputProps属性
+
+## 3.4.24(2025-04-25)
+fix: 修复upload上传逻辑(感谢@semdy)
+
+## 3.4.23(2025-04-24)
+chore: 补全chooseFile TS类型(感谢@semdy)
+
+feat: u-search组件的图标支持显示在右边(感谢@semdy)
+
+chore: 修正chooseFile返回的数据TS类型(感谢@semdy)
+
+fix: PR导致缺失name影响uplad自动上传扩展名
+
+
+## 3.4.22(2025-04-22)
+fix: 修复自动上传偶发的success被覆盖为uploading
+
+fix: float-button缺少key #677
+
+fix: upload组件完善优化(感谢@semdy)
+
+fix: toolbar组件confirmColor属性默认改为空,以便默认使用主题色、标题字体加粗(感谢@semdy)
+
+## 3.4.21(2025-04-21)
+feat: subsection分段器支持双向绑定current
+
+feat: select组件支持maxHeight属性
+
+feat: datetime-picker支持inputBorder属性
+
+## 3.4.20(2025-04-17)
+fix: 修复navbar-mini提示border不存在
+
+feat: status-bar支持对外暴露状态栏高度值
+
+feat: upload支持自定义自动上传后处理逻辑便于对接不同规范后端
+
+feat: 优化tag组件插槽
+
+
+## 3.4.19(2025-04-14)
+fix: 修复model组件增加contentStyle带来的语法问题
+
+## 3.4.18(2025-04-14)
+fix: upload组件支持所有文件类型的onClickPreview事件
+
+## 3.4.17(2025-04-11)
+feat: select组件text插槽增加scope传递currentLabel
+
+## 3.4.16(2025-04-10)
+fix: 修复安卓新加载字体方式导致Cannot read property '$page' of undefined
+
+## 3.4.15(2025-04-10)
+improvment: 优化移步加载数据时swiper组件displayMultipleItems报错
+
+feat: modal增加contentStyle属性
+
+fix: 修复下拉菜单收起动画缺失
+
+fix: 修复sticky的offset属性值为响应式数据时失效 #237
+
+
+## 3.4.14(2025-04-09)
+feat: 支持自托管内置图标及扩展自定义图标
+
+## 3.4.13(2025-04-08)
+fix: tabs点击当前tab触发change事件
+
+## 3.4.12(2025-04-02)
+fix: dropdown关闭后遮挡页面内容 #653
+
+fix u-sticky.vue Uncaught TypeError: e.querySelector is not a function at uni-app-view.umd.js
+
+## 3.4.11(2025-03-31)
+fix: 优化upload组件预览视频的弹窗占位
+
+## 3.4.10(2025-03-28)
+feat: select组件新增多个props属性及优化
+
+fix: 修复cate-tab报错index is not defined #661
+
+
+## 3.4.9(2025-03-27)
+fix: 修复upload组件split报错
+
+fix: 修复float-button缺少flex样式
+
+## 3.4.8(2025-03-27)
+fix: 修复upload组件split报错
+
+fix: 移除mapState
+
+## 3.4.7(2025-03-26)
+fix: 修复action-sheet-data和picker-data数据回显
+
+fix:  优化upload组件视频封面兼容
+
+## 3.4.6(2025-03-25)
+feat: checkbox触发change时携带name参数
+
+feat: upload组件支持服务器本机和阿里云OSS自动上传功能及上传进度条
+
+feat: upload组件支持视频预览及oss上传时获取视频封面图
+
+feat: 新增up-action-sheet-data快捷组件
+
+feat: 新增up-picker-data快捷组件
+
+## 3.4.5(2025-03-24)
+feat: tag组件新增textSize/height/padding/borderRadius属性
+
+feat: 新增genLightColor自动计算浅色方法及tag组件支持autoBgColor自动计算背景色
+
+## 3.4.4(2025-03-13)
+feat: modal增加异步操作进行中点击取消弹出提示特性防止操作被中断
+
+fix: 修复toast组件show方法类型声明
+
+## 3.4.3(2025-03-12)
+fix: 修复textarea自动增高时在输入时高度异常
+
+## 3.4.2(2025-03-11)
+feat: step组件增加title插槽及增加辅助class便于自定义样式
+
+## 3.4.1(2025-03-11)
+feat: 新机制确保setConfig与http在nvue等环境下生效
+
+## 3.3.74(2025-03-06)
+fix: CateTab语法问题
+
+## 3.3.73(2025-03-06)
+feat: CateTab新增v-model:current属性
+
+## 3.3.72(2025-02-28)
+feat: tabs组件支持icon图标及插槽
+
+## 3.3.71(2025-02-27)
+feat: 折叠面板collapse增加titileStyle/iconStyle/rightIconStyle属性
+
+feat: 折叠面板组件新增cellCustomStyle/cellCustomClass属性
+
+fix: select组件盒模型
+
+## 3.3.70(2025-02-24)
+fix: 修改u-checkbox-group组件changes事件发生位置
+
+## 3.3.69(2025-02-19)
+picker允许传递禁用颜色props
+
+slider组件isRange状态下增加min max插槽分开显示内容
+
+feat: 新增经典下拉框组件up-select
+
+## 3.3.68(2025-02-12)
+fix: 修复weekText类型
+
+feat: 日历增加单选与多选指定禁止选中的日期功能
+
+fix: NumberBox删除数字时取值有误 #613
+
+## 3.3.67(2025-02-11)
+feat: navbar支持返回全局拦截器配置
+
+feat: 表单-校验-支持无提示-得到校验结果
+
+feat: picker传递hasInput属性时候,可以禁用输入框点击
+
+## 3.3.66(2025-02-09)
+feat: steps-item增加content插槽
+
+## 3.3.65(2025-02-05)
+feat: number-box组件新增按钮圆角/按钮宽度/数据框背景色/迷你模式
+## 3.3.64(2025-01-18)
+feat: 日历组件支持自定义星期文案
+
+## 3.3.63(2025-01-13)
+fix: cate-tab支持支付宝小程序
+
+fix: textarea 修复 placeholder-style
+
+fix: 修复在图片加载及加载失败时容器宽度
+
+fix: waterfall组件报错Maximum recursive updates
+
+## 3.3.62(2025-01-10)
+feat: sleder滑动选择器双滑块增加外层触发值的变动功能
+
+fix: picker支持hasInput优化
+
+## 3.3.61(2024-12-31)
+fix: 修复微信getSystemInfoSync接口废弃警告
+
+fix: 'u-status-bar' symbol missing
+
+## 3.3.60(2024-12-30)
+feat: 日期组件支持禁用
+
+fix: ts定义修复 #600
+
+feat: Tabs组件选中时增加一个active的class #595
+
+## 3.3.59(2024-12-30)
+fix: Property "isH5" was accessed during render
+
+## 3.3.58(2024-12-26)
+fix: slider组件change事件传参
+
+## 3.3.57(2024-12-23)
+fix: slider组件change事件传参
+
+feat: 更新u-picker组件增加当前选中class类名
+
+## 3.3.56(2024-12-18)
+feat: 在u-alert组件中添加关闭事件
+
+## 3.3.55(2024-12-17)
+add: swiper增加双向绑定
+
+## 3.3.54(2024-12-11)
+add: qrcode支持props控制是否开启点击预览
+
+add: 新增cate-tab垂直分类组件
+
+## 3.3.53(2024-12-10)
+fix: 修复popup居中模式点击内容区域触发关闭
+
+## 3.3.52(2024-12-09)
+add: notice-bar支持justifyContent属性
+
+## 3.3.51(2024-12-09)
+add: radio增加label插槽
+
+## 3.3.50(2024-12-05)
+fix: 优化popup等对禁止背景滚动机制
+
+add: slider在弹窗使用示例
+
+fix: card组件类名问题
+
+## 3.3.49(2024-12-02)
+fix: 去除album多余的$u引用
+
+fix: 优化图片组件兼容性
+
+add: picker组件增加zIndex属性
+
+add: text增加是否占满剩余空间属性
+
+add: input颜色示例
+
+## 3.3.48(2024-11-29)
+add: 文本行数限制样式提高到10行
+
+del: 去除不跨端的inputmode
+## 3.3.47(2024-11-28)
+fix: 时间选择器在hasInput模式下部分机型键盘弹出
+
+## 3.3.46(2024-11-26)
+fix: 修复text传递事件参数
+
+## 3.3.45(2024-11-24)
+add: navbar组件支持配置标题颜色
+
+fix: 边框按钮警告类型下颜色变量使用错误
+
+## 3.3.43(2024-11-18)
+fix: 支持瀑布流组件v-model置为[]
+
+add: 新增字符串路径访问工具方法getValueByPath
+
+add: 新增float-button悬浮按钮组件
+
+## 3.3.42(2024-11-15)
+add: button组件支持stop参数阻止冒泡
+
+## 3.3.41(2024-11-13)
+fix: u-radio-group invalid import
+
+improvement: 优化图片组件宽高及修复事件event传递
+
+## 3.3.40(2024-11-11)
+add: 组件radioGroup增加gap属性用于设置item间隔 
+
+fix: 修复H5全局导入
+
+## 3.3.39(2024-11-04)
+fix: 修复相册组件
+
+## 3.3.38(2024-11-04)
+fix: 修复视频预览报错 #510
+
+add: album组件增加stop参数支持阻止事件冒泡
+
+## 3.3.37(2024-10-21)
+fix: 修复因为修改组件名称前缀,导致h5打包后$parent方法内找不到父组件的问题
+
+fix: 修复datetime-picker选择2000年以前日期出错
+
+## 3.3.36(2024-10-09)
+fix: toast 自动关闭
+
+feat: 增加微信小程序用户昵称审核完毕回调及修改 ts 定义文件
+
+## 3.3.35(2024-10-08)
+feat: modal和picker支持v-model:show双向绑定
+
+feat: 支持checkbox使用slot自定义label后自带点击事件 #522
+
+feat: swipe-action支持自动关闭特性及初始化打开状态
+
+## 3.3.34(2024-09-23)
+feat: 支持toast设置duration值为-1时不自动关闭
+
+## 3.3.33(2024-09-18)
+fix: 修复test.date('008')等验证结果不准确
+
+## 3.3.32(2024-09-09)
+fix: u-keyboard名称冲突warning
+
+## 3.3.31(2024-08-31)
+feat: qrcode初步支持nvue
+
+## 3.3.30(2024-08-30)
+fix: slider兼容step为字符串类型
+
+## 3.3.29(2024-08-30)
+fix: 修复tabs组件current参数为字符串处理逻辑
+
+## 3.3.28(2024-08-26)
+fix: list组件滑动偏移量不一样取绝对值导致iOS下拉偏移量计算错误
+
+## 3.3.27(2024-08-22)
+fix: 修复up-datetime-picker组件toolbarRightSlot定义缺失
+
+fix: 修复FormItem的rules更新错误的问题
+
+## 3.3.26(2024-08-22)
+fix: 批量注册全局组件优化
+
+## 3.3.25(2024-08-21)
+fix: 修复slider在app-vue下样式问题
+
+## 3.3.24(2024-08-19)
+fix: 修复时间选择器hasInput模式小程序不生效
+
+feat: 支持H5导入所有组件
+
+## 3.3.23(2024-08-17)
+feat: swipe-action增加closeAll方法
+
+fix: 兼容tabs在某些场景下index小于0时自动设置为0
+
+add: 通用mixin新增navTo页面跳转方法
+
+## 3.3.21(2024-08-15)
+improvement: 优化二维码组件loading及支持预览与长按事件 #351
+
+fix: 修复swipe-action自动关闭其它功能及组件卸载自动关闭
+
+## 3.3.20(2024-08-15)
+refactor: props默认值文件移至组件文件夹内便于查找
+## 3.3.19(2024-08-14)
+fix: 修复2被rpx兼容处理只在数字值生效
+
+add: 增加swiper自定义插槽示例
+
+## 3.3.18(2024-08-13)
+feat: 新增支持datetime-picker工具栏插槽及picker插槽支持修复
+## 3.3.17(2024-08-12)
+feat: swiper组件增加默认slot便于自定义
+
+feat: grid新增间隔参数
+
+feat: picker新增toolbar-right和toolbar-bottom插槽
+
+## 3.3.16(2024-08-12)
+fix: 解决swiper中title换行后多余的内容未被遮挡问题
+
+fix: 修复迷你导航适配异形屏
+
+## 3.3.15(2024-08-09)
+fix: 修复默认单位设置为rpx时一些组件高度间距异常
+
+fix: 修复日历在rpx单位下布局异常
+
+feat: code-input支持App端展示输入光标
+
+## 3.3.14(2024-08-09)
+add: 增加box组件
+
+add: 增加card卡片组件
+
+
+## 3.3.13(2024-08-08)
+feat: input支持调用原生组件的focus和blur方法
+
+improvement: grid-item条件编译优化
+
+add: 新增迷你导航组件
+
+## 3.3.12(2024-08-06)
+improvement: $u挂载时机调整便于打包分离chunk
+
+fix: steps新增itemStyle属性名称冲突
+
+## 3.3.11(2024-08-05)
+feat: 新增支持upload组件的deletable/maxCount/accept变更监听 #333
+
+feat: 新增支持tabs在swiper中使用
+
+feat: 新增FormItem支持独立设置验证规则rules
+
+fix: 修复index-list未设置$slots.header时索引高亮失效
+
+## 3.3.10(2024-08-02)
+fix: 修复index-list偶发的滑动最后一个索引报错top不存在
+
+fix: 修复gird在QQ、抖音小程序下布局
+
+feat: 优化step支持自定义样式prop
+
+feat: action-sheet组件支持v-model:show双向绑定
+
+fix: 小程序下steps和grid都统一采用grid布局
+
+fix: 修复支付宝小程序下input类型为数字时双向绑定失效
+
+feat : form 表单 validate 校验不通过后 error增加字段prop信息  #304
+
+fix: form组件异步校异常验问题 #393
+
+## 3.3.9(2024-08-01)
+fix: 优化获取nvue元素
+
+feat: modal新增contentTextAlign设置文案对齐方式
+
+fix: 修复NVUE下tabbar文字不显示  #458
+
+feat: loading-page增加zIndex属性
+
+fix: 相册在宽度较小时换行问题
+
+feat: album相册增加自适应自动换行模式
+
+feat: album相册增加图片尺寸单位prop
+
+fix: 修复calendar日历月份居中
+
+## 3.3.8(2024-07-31)
+feat: slider支持进度条任意位置触发按钮拖动
+
+fix: 修复app-vue下modal标题不居中
+
+fix: #459 TS setConfig 声明异常
+
+feat: tabs组件增加longPress长按事件
+
+feat: 新增showRight属性控制collapse右侧图标显隐
+
+fix: 优化nvue下css警告
+
+## 3.3.7(2024-07-29)
+feat: 支持IndexList组件支持在弹窗等场景下使用及联动优化
+
+feat: popup组件支持v-model:show双向绑定
+
+feat: 优化tabs的current双向绑定
+
+fix: checkbox独立使用时checked赋初始值可以,但是手动切换时值没有做双向绑定! #455
+
+feat: slider组件支持区间双滑块
+
+fix: toast 支持自定义图标?可传入了决对路径的 icon也没有用 #409
+
+feat: form-item校验失败时 增加class方便自定义显示错误的展示方式 #394
+
+fix: up-cell的required配置不生效 #395
+
+fix: 横向滚动组件,微信小程序编译后会有警告 #415
+
+fix: u-picker内部对默认值defaultIndex的监听 #425
+
+feat: toast 组件支持遮掩层穿透  #417
+
+fix: 兼容vue的slot编译bug #423
+
+fix: upload 微信小程序 点击预览视频报错 #424
+
+fix: u-number-box 组件修改【integer, decimalLength, min, max 】props时没有触发绑定值更新 #429
+
+feat: Tabs组件能否支持自定义插槽 #439
+
+feat: ActionSheet 可以配置最大高度吗, 我当做select使用了。 #445
+
+fix: cursor-pointer优化
+
+feat: 新版slider组件兼容NVUE改造
+
+feat: 新增slider组件手动实现以支持样式自定义
+
+perf:补充TS声明提示信息
+
+修复:ActionSheet 操作菜单cancelText属性为空DOM节点还存在并且可以点击问题
+
+fix: 去除预留的beforeDestroy兼容容易在某些sdk下不识别条件编译
+
+## 3.3.6(2024-07-23)
+feat: u-album组件添加radius,shape参数,定义参考当前u-image参数
+
+fix: 修复了calendar组件title和日期title未垂直居中的问题
+
+fix: update:modelValue缺失emit定义
+
+## 3.3.5(2024-07-10)
+picker组件支持hasInput模式
+
+## 3.3.4(2024-07-07)
+fix: input组件双向绑定问题 #419
+
+lazy-load完善emit
+
+优化通用小程序分享
+
+## 3.3.2(2024-06-27)
+fix: 在Nvue环境中编译,出现大量警告 #406
+## 3.3.1(2024-06-27)
+u-button组件报错,找不到button mixins #407
+## 3.3.0(2024-06-27)
+feat: checkbox支持label设置slot
+
+feat: modal增加customClass
+
+feat: navbar、popup、tabs、text支持customClass
+
+fix: cell组建缺少flex布局
+
+fix: 修复微信小程序真机调试时快速输入出现文本回退问题
+
+feat: tag增加默认slot
+
+公共mixin改造为按需导入语法
+
+refactor: 组件props混入mixin改造为按需导入语法
+
+fix: u-tabbar 安卓手机点击按钮变蓝问题 #396
+
+feat: upload组建增加extension属性
+
+fix: upload组件参数mode添加left
+
+fix: 修复阴影在非nvue时白色背景色不显示
+
+## 3.2.24(2024-06-11)
+fix: 修复时间选择器confirm事件触发时机导致2次才会触发v-model更新
+## 3.2.23(2024-05-30)
+fix: #378 H5 u-input 在表单中初始值为空也会触发一次 formValidate(this,"change")事件导致进入页面直接校验了一次
+
+fix: #373 搜索组件up-search的@clear事件无效
+
+fix: #372 ActionSheet 组件的取消按钮触发区域太小
+
+## 3.2.22(2024-05-13)
+上传组件支持微信小程序预览视频
+
+修复折叠面板右侧箭头不显示
+
+修复uxp2px
+
+## 3.2.21(2024-05-10)
+fix: loading-icon修复flex布局
+## 3.2.20(2024-05-10)
+修复瀑布流大小写#355
+## 3.2.19(2024-05-10)
+去除意外的文件引入
+## 3.2.18(2024-05-09)
+fix: 349 popup 组件设置 zIndex 属性后,组件渲染异常#
+feat: 搜索框增加adjustPosition属性
+fix: #331增加u-action-sheet__cancel
+优化mixin兼容性
+feat: #326 up-list增加下拉刷新功能
+fix: #319 优化up-tabs参数与定义匹配
+fix: index-list组件微信小程序端使用自定义导航栏异常
+fix: #285 pickerimmediateChange 写死为true
+fix: #111 u-scroll-list组件,隐藏指示器后报错, 提示找不到ref
+list增加微信小程序防抖配置
+
+## 3.2.17(2024-05-08)
+fix: 支付宝小程序二维码渲染
+## 3.2.16(2024-05-06)
+修复tabs中,当前激活样式的undefined bug
+
+fix: #341u-code 倒计时没结束前退出,再次进入结束后退出界面,再次进入重新开始倒计时bug
+
+受到uni-app内置text样式影响修复
+
+## 3.2.15(2024-04-28)
+优化时间选择器hasInput模式初始化值
+## 3.2.14(2024-04-24)
+去除pleaseSetTranspileDependencies
+
+http采用useStore
+
+## 3.2.13(2024-04-22)
+修复modal标题样式
+
+优化日期选择器hasInput模式宽度
+
+## 3.2.12(2024-04-22)
+修复color应用
+## 3.2.11(2024-04-18)
+修复import化带来的问题
+## 3.2.10(2024-04-17)
+完善input清空事件App端失效的兼容性
+
+修复日历组件二次打开后当前月份显示不正确
+
+## 3.2.9(2024-04-16)
+组件内uni.$u用法改为import引入
+
+规范化及兼容性增强
+
+## 3.2.8(2024-04-15)
+修复up-tag语法错
+## 3.2.7(2024-04-15)
+修复下拉菜单背景色在支付宝小程序无效
+
+setConfig改为浅拷贝解决无法用import导入代替uni.$u.props设置
+
+## 3.2.6(2024-04-14)
+修复某些情况下滑动单元格默认右侧按钮是展开的问题
+## 3.2.5(2024-04-13)
+调整分段器尺寸及修复窗口大小改变时重新计算尺寸
+
+多个组件支持cursor-pointer增强PC端体验
+
+## 3.2.4(2024-04-12)
+初步支持typescript
+## 3.2.3(2024-04-12)
+fix: 修复square属性在小程序下无效问题
+
+fix:修复lastIndex异常导致的column异常问题
+
+fix: alipayapp picker style
+
+feat(button): 添加用户同意隐私协议事件回调
+
+fix: input switch password
+
+fix: 修复u-code组件keepRuning失效问题
+
+feat: form-item添加labelPosition属性
+
+新增dropdown组件
+
+分段器支持内部current值
+
+优化cell和action-sheet视觉大小
+
+修复tabs文字换行
+
+## 3.2.2(2024-04-11)
+修复换行符问题
+## 3.2.1(2024-04-11)
+修复演示H5二维码
+
+fix: #270 ReadMore 展开阅读更多内容变化兼容
+
+fix: #238Calendar组件maxDate修改为不能小于minDate
+
+checkbox支持独立使用
+
+修复popup中在微信小程序中真机调试滚动失效
+
+## 3.2.0(2024-04-10)
+修复轮播图在nvue显示
+修复疑似u-slider名称被占用导致slider在App下不显示
+解决微信小程序提示 Some selectors are not allowed in component wxss
+示例中u-前缀统一为up-
+增加瀑布流与图片懒加载组件
+fix: #308修复tag组件缺失iconColor参数
+fix: #297使用grid布局解决目前编译为抖音小程序无法开启virtualHost
+## 3.1.52(2024-04-07)
+工具类方法调用import化改造
+新增up-copy复制组件
+## 3.1.51(2024-04-07)
+优化时间选择器自带输入框格式化显示
+防止按钮文字换行
+修复订单列表模板滑动
+增加u-qrcode二维码组件
+## 3.1.49(2024-03-27)
+日期时间组件支持自带输入框
+fix: popup弹窗滚动穿透问题
+fix: 修复小程序numberbox bug
+## 3.1.48(2024-03-18)
+fix:[plugin:uni:pre-css] Unbalanced delimiter found in string
+## 3.1.47(2024-03-18)
+fix: setConfig设置组件默认参数无效问题
+fix: 修复自定义图标无效问题
+feat: 增加u-form-item单独设置规则变量
+fix:#293小程序是自定义导航栏的时候即传了customNavHeight的时候会出现跳转偏移的情况
+
+## 3.1.46(2024-01-29)
+beforeUnmount
+## 3.1.45(2024-01-24)
+fix: #262ext组件为超链接的情况下size属性不生效
+fix: #263最新版本3.1.42中微信小程序u-swipe-action-item报错
+fix: #224最新版本3.1.42中微信小程序u-swipe-action-item报错
+fix: #263支持支付宝小程序
+fix: #261u-input在直接修改v-model的绑定值时,每隔一次会无法出发change事件
+优化折叠面板兼容微信小程序
+## 3.1.42(2024-01-15)
+修复u-number-box默认值0时在小程序不显示值
+优化u-code的timer判断
+优化支付宝小程序下textarea字数统计兼容
+优化u-calendar
+## 3.1.41(2023-11-18)
+#215优化u-cell图标容器间距问题
+## 3.1.40(2023-11-16)
+修复u-slider双向绑定
+## 3.1.39(2023-11-10)
+修复头条小程序不支持env(safe-area-inset-bottom)
+优化#201u-grid 指定列数导致闪烁
+#193IndexList 索引列表 高度错误
+其他优化
+## 3.1.38(2023-10-08)
+修复u-slider
+## 3.1.37(2023-09-13)
+完善emits定义及修复code-input双向数据绑定
+## 3.1.36(2023-08-08)
+修复富文本事件名称大小写
+## 3.1.35(2023-08-02)
+修复编译到支付宝小程序u-form报错
+## 3.1.34(2023-07-27)
+修复App打包uni.$u.mpMixin方式sdk暂时不支持导致报错
+## 3.1.33(2023-07-13)
+修复弹窗进入动画、模板页面样式等
+## 3.1.31(2023-07-11)
+修复dayjs引用
+## 3.0.8(2022-07-12)
+修复u-tag默认宽度撑满容器
+## 3.0.7(2022-07-12)
+修复u-navbar自定义插槽演示示例
+## 3.0.6(2022-07-11)
+修复u-image缺少emits申明
+## 3.0.5(2022-07-11)
+修复u-upload缺少emits申明
+## 3.0.4(2022-07-10)
+修复u-textarea/u-input/u-datetime-picker/u-number-box/u-radio-group/u-switch/u-rate在vue3下数据绑定
+## 3.0.3(2022-07-09)
+启用自建演示二维码
+## 3.0.2(2022-07-09)
+修复dayjs/clipboard等导致打包报错
+## 3.0.1(2022-07-09)
+增加插件市场地址
+## 3.0.0(2022-07-09)
+# uview-plus(vue3)初步发布

+ 85 - 0
uni_modules/uview-plus/components/u--form/u--form.vue

@@ -0,0 +1,85 @@
+<template>
+	<uvForm
+		ref="uForm"
+		:model="model"
+		:rules="rules"
+		:errorType="errorType"
+		:borderBottom="borderBottom"
+		:labelPosition="labelPosition"
+		:labelWidth="labelWidth"
+		:labelAlign="labelAlign"
+		:labelStyle="labelStyle"
+		:customStyle="customStyle"
+	>
+		<slot />
+	</uvForm>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
+	 * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
+	 */
+	import uvForm from '../u-form/u-form.vue';
+	import { props } from '../u-form/props.js';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	export default {
+		// #ifdef MP-WEIXIN
+		name: 'u-form',
+		// #endif
+		// #ifndef MP-WEIXIN
+		name: 'u--form',
+		// #endif
+		mixins: [mpMixin, props, mixin],
+		components: {
+			uvForm
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
+			setRules(rules) {
+				this.$refs.uForm.setRules(rules)
+			},
+			/**
+			 * 校验全部数据
+			 * @param {Object} options
+			 * @param {Boolean} options.showErrorMsg -是否显示校验信息,
+			 */
+			validate(options) {
+				/**
+				 * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
+				 * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
+				 * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
+				 */
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validate(options)
+			},
+			validateField(value, callback) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validateField(value, callback)
+			},
+			resetFields() {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.resetFields()
+			},
+			clearValidate(props) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.clearValidate(props)
+			},
+			setMpData() {
+				this.$refs.uForm.children = this.children
+			}
+		},
+	}
+</script>

+ 50 - 0
uni_modules/uview-plus/components/u--image/u--image.vue

@@ -0,0 +1,50 @@
+<template>
+	<uvImage 
+		:src="src"
+		:mode="mode"
+		:width="width"
+		:height="height"
+		:shape="shape"
+		:radius="radius"
+		:lazyLoad="lazyLoad"
+		:showMenuByLongpress="showMenuByLongpress"
+		:loadingIcon="loadingIcon"
+		:errorIcon="errorIcon"
+		:showLoading="showLoading"
+		:showError="showError"
+		:fade="fade"
+		:webp="webp"
+		:duration="duration"
+		:bgColor="bgColor"
+		:customStyle="customStyle"
+		@click="$emit('click')"
+		@error="$emit('error')"
+		@load="$emit('load')"
+	>
+		<template v-slot:loading>
+			<slot name="loading"></slot>
+		</template>
+		<template v-slot:error>
+			<slot name="error"></slot>
+		</template>
+	</uvImage>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
+	 * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
+	 */
+	import uvImage from '../u-image/u-image.vue';
+	import { props } from '../u-image/props.js';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	export default {
+		name: 'u--image',
+		mixins: [mpMixin, props, mixin],
+		components: {
+			uvImage
+		},
+		emits: ['click', 'error', 'load']
+	}
+</script>

+ 74 - 0
uni_modules/uview-plus/components/u--input/u--input.vue

@@ -0,0 +1,74 @@
+<template>
+	<uvInput 
+		<!-- #ifdef VUE2 -->
+		:value="value"
+		@input="e => $emit('input', e)"
+		<!-- #endif -->
+		<!-- #ifdef VUE3 -->
+		:modelValue="modelValue"
+		@update:modelValue="e => $emit('update:modelValue', e)"
+		<!-- #endif -->
+		:type="type"
+		:fixed="fixed"
+		:disabled="disabled"
+		:disabledColor="disabledColor"
+		:clearable="clearable"
+		:password="password"
+		:maxlength="maxlength"
+		:placeholder="placeholder"
+		:placeholderClass="placeholderClass"
+		:placeholderStyle="placeholderStyle"
+		:showWordLimit="showWordLimit"
+		:confirmType="confirmType"
+		:confirmHold="confirmHold"
+		:holdKeyboard="holdKeyboard"
+		:focus="focus"
+		:autoBlur="autoBlur"
+		:disableDefaultPadding="disableDefaultPadding"
+		:cursor="cursor"
+		:cursorSpacing="cursorSpacing"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:inputAlign="inputAlign"
+		:fontSize="fontSize"
+		:color="color"
+		:prefixIcon="prefixIcon"
+		:suffixIcon="suffixIcon"
+		:suffixIconStyle="suffixIconStyle"
+		:prefixIconStyle="prefixIconStyle"
+		:border="border"
+		:readonly="readonly"
+		:shape="shape"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		:ignoreCompositionEvent="ignoreCompositionEvent"
+	>
+		<!-- #ifdef MP -->
+		<slot name="prefix"></slot>
+		<slot name="suffix"></slot>
+		<!-- #endif -->
+		<!-- #ifndef MP -->
+		<slot name="prefix" slot="prefix"></slot>
+		<slot name="suffix" slot="suffix"></slot>
+		<!-- #endif -->
+	</uvInput>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
+	 * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
+	 */
+	import uvInput from '../u-input/u-input.vue';
+	import { props } from '../u-input/props.js';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	export default {
+		name: 'u--input',
+		mixins: [mpMixin, props, mixin],
+		components: {
+			uvInput
+		},
+	}
+</script>

+ 45 - 0
uni_modules/uview-plus/components/u--text/u--text.vue

@@ -0,0 +1,45 @@
+<template>
+    <uvText
+        :type="type"
+        :show="show"
+        :text="text"
+        :prefixIcon="prefixIcon"
+        :suffixIcon="suffixIcon"
+        :mode="mode"
+        :href="href"
+        :format="format"
+        :call="call"
+        :openType="openType"
+        :bold="bold"
+        :block="block"
+        :lines="lines"
+        :color="color"
+		:decoration="decoration"
+        :size="size"
+        :iconStyle="iconStyle"
+        :margin="margin"
+        :lineHeight="lineHeight"
+        :align="align"
+        :wordWrap="wordWrap"
+        :customStyle="customStyle"
+    ></uvText>
+</template>
+
+<script>
+/**
+ * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
+ * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
+ * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
+ */
+import uvText from "../u-text/u-text.vue";
+import { props } from "../u-text/props.js";
+import { mpMixin } from '../../libs/mixin/mpMixin.js'
+import { mixin } from '../../libs/mixin/mixin.js'
+export default {
+    name: "u--text",
+    mixins: [mpMixin, mixin, props,],
+    components: {
+        uvText,
+    },
+};
+</script>

+ 47 - 0
uni_modules/uview-plus/components/u--textarea/u--textarea.vue

@@ -0,0 +1,47 @@
+<template>
+	<uvTextarea
+		:value="value"
+		:modelValue="modelValue"
+		:placeholder="placeholder"
+		:height="height"
+		:confirmType="confirmType"
+		:disabled="disabled"
+		:count="count"
+		:focus="focus"
+		:autoHeight="autoHeight"
+		:fixed="fixed"
+		:cursorSpacing="cursorSpacing"
+		:cursor="cursor"
+		:showConfirmBar="showConfirmBar"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:disableDefaultPadding="disableDefaultPadding"
+		:holdKeyboard="holdKeyboard"
+		:maxlength="maxlength"
+		:border="border"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		:ignoreCompositionEvent="ignoreCompositionEvent"
+		@input="e => $emit('input', e)"
+		@update:modelValue="e => $emit('update:modelValue', e)"
+	></uvTextarea>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
+	 * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
+	 */
+	import uvTextarea from '../u-textarea/u-textarea.vue';
+	import { props } from '../u-textarea/props.js';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	export default {
+		name: 'u--textarea',
+		mixins: [mpMixin, props, mixin],
+		components: {
+			uvTextarea
+		},
+	}
+</script>

+ 109 - 0
uni_modules/uview-plus/components/u-action-sheet-data/u-action-sheet-data.vue

@@ -0,0 +1,109 @@
+<template>
+	<view class="u-action-sheet-data">
+		<view class="u-action-sheet-data__trigger">
+			<slot name="trigger"></slot>
+			<up-input
+				v-if="!$slots['trigger']"
+				:modelValue="current"
+				disabled
+				disabledColor="#ffffff"
+				:placeholder="title"
+				border="none"
+			></up-input>
+			<view @click="show = true"
+				class="u-action-sheet-data__trigger__cover"></view>
+		</view>
+		<up-action-sheet
+			:show="show"
+			:actions="options"
+			:title="title"
+			safeAreaInsetBottom
+			:description="description"
+			@close="show = false"
+			@select="select"
+		>
+		</up-action-sheet>
+	</view>
+</template>
+
+<script>
+export default {
+    props: {
+		modelValue: {
+			type: [String, Number],
+			default: ''
+		},
+		title: {
+			type: String,
+			default: ''
+		},
+		description: {
+			type: String,
+			default: ''
+		},
+		options: {
+			type: Array,
+			default: () => {
+				return []
+			}
+		},
+		valueKey: {
+			type: String,
+			default: 'value'
+		},
+		labelKey: {
+			type: String,
+			default: 'name'
+		}
+    },
+    data() {
+        return {
+			show: false,
+			current: '',
+        }
+    },
+    created() {
+		if (this.modelValue) {
+			this.options.forEach((ele) => {
+				if (ele[this.valueKey] == this.modelValue) {
+					this.current = ele[this.labelKey]
+				}
+			})
+		}
+    },
+    emits: ['update:modelValue'],
+	watch: {
+		modelValue() {
+			this.options.forEach((ele) => {
+				if (ele[this.valueKey] == this.modelValue) {
+					this.current = ele[this.labelKey]
+				}
+			})
+		}
+	},
+    methods: {
+        hideKeyboard() {
+            uni.hideKeyboard()
+        },
+        select(e) {
+            this.$emit('update:modelValue', e[this.valueKey])
+			this.current = e[this.labelKey]
+        },
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+	.u-action-sheet-data {
+		&__trigger {
+			position: relative;
+			&__cover {
+				position: absolute;
+				top: 0;
+				left: 0;
+				right: 0;
+				bottom: 0;
+			}
+		}
+	}
+</style>

+ 26 - 0
uni_modules/uview-plus/components/u-action-sheet/actionSheet.js

@@ -0,0 +1,26 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:44:35
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/actionSheet.js
+ */
+export default {
+    // action-sheet组件
+    actionSheet: {
+        show: false,
+        title: '',
+        description: '',
+        actions: [],
+        index: '',
+        cancelText: '',
+        closeOnClickAction: true,
+        safeAreaInsetBottom: true,
+        openType: '',
+        closeOnClickOverlay: true,
+        round: 0,
+        wrapMaxHeight: '600px'
+    }
+}

+ 62 - 0
uni_modules/uview-plus/components/u-action-sheet/props.js

@@ -0,0 +1,62 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+    props: {
+        // 操作菜单是否展示 (默认false)
+        show: {
+            type: Boolean,
+            default: () => defProps.actionSheet.show
+        },
+        // 标题
+        title: {
+            type: String,
+            default: () => defProps.actionSheet.title
+        },
+        // 选项上方的描述信息
+        description: {
+            type: String,
+            default: () => defProps.actionSheet.description
+        },
+        // 数据
+        actions: {
+            type: Array,
+            default: () => defProps.actionSheet.actions
+        },
+        // 取消按钮的文字,不为空时显示按钮
+        cancelText: {
+            type: String,
+            default: () => defProps.actionSheet.cancelText
+        },
+        // 点击某个菜单项时是否关闭弹窗
+        closeOnClickAction: {
+            type: Boolean,
+            default: () => defProps.actionSheet.closeOnClickAction
+        },
+        // 处理底部安全区(默认true)
+        safeAreaInsetBottom: {
+            type: Boolean,
+            default: () => defProps.actionSheet.safeAreaInsetBottom
+        },
+        // 小程序的打开方式
+        openType: {
+            type: String,
+            default: () => defProps.actionSheet.openType
+        },
+        // 点击遮罩是否允许关闭 (默认true)
+        closeOnClickOverlay: {
+            type: Boolean,
+            default: () => defProps.actionSheet.closeOnClickOverlay
+        },
+        // 圆角值
+        round: {
+            type: [Boolean, String, Number],
+            default: () => defProps.actionSheet.round
+        },
+        // 选项区域最大高度
+        wrapMaxHeight: {
+            type: [String],
+            default: () => defProps.actionSheet.wrapMaxHeight
+        },
+    }
+})

+ 282 - 0
uni_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue

@@ -0,0 +1,282 @@
+
+<template>
+	<u-popup
+	    :show="show"
+	    mode="bottom"
+	    @close="closeHandler"
+	    :safeAreaInsetBottom="safeAreaInsetBottom"
+	    :round="round"
+	>
+		<view class="u-action-sheet">
+			<view
+			    class="u-action-sheet__header"
+			    v-if="title"
+			>
+				<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
+				<view
+				    class="u-action-sheet__header__icon-wrap"
+				    @tap.stop="cancel"
+				>
+					<u-icon
+					    name="close"
+					    size="17"
+					    color="#c8c9cc"
+					    bold
+					></u-icon>
+				</view>
+			</view>
+			<text
+			    class="u-action-sheet__description"
+				:style="[{
+					marginTop: `${title && description ? 0 : '18px'}`
+				}]"
+			    v-if="description"
+			>{{description}}</text>
+			<slot>
+				<u-line v-if="description"></u-line>
+				<scroll-view scroll-y class="u-action-sheet__item-wrap" :style="{maxHeight: wrapMaxHeight}">
+					<view :key="index" v-for="(item, index) in actions">
+						<!-- #ifdef MP -->
+						<button
+						    class="u-reset-button"
+						    :openType="item.openType"
+						    @getuserinfo="onGetUserInfo"
+						    @contact="onContact"
+						    @getphonenumber="onGetPhoneNumber"
+						    @error="onError"
+						    @launchapp="onLaunchApp"
+						    @opensetting="onOpenSetting"
+						    :lang="lang"
+						    :session-from="sessionFrom"
+						    :send-message-title="sendMessageTitle"
+						    :send-message-path="sendMessagePath"
+						    :send-message-img="sendMessageImg"
+						    :show-message-card="showMessageCard"
+						    :app-parameter="appParameter"
+						    @tap="selectHandler(index)"
+						    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+						>
+							<!-- #endif -->
+							<view
+							    class="u-action-sheet__item-wrap__item"
+							    @tap.stop="selectHandler(index)"
+							    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+							    :hover-stay-time="150"
+							>
+								<template v-if="!item.loading">
+									<text
+									    class="u-action-sheet__item-wrap__item__name"
+									    :style="[itemStyle(index)]"
+									>{{ item.name }}</text>
+									<text
+									    v-if="item.subname"
+									    class="u-action-sheet__item-wrap__item__subname"
+									>{{ item.subname }}</text>
+								</template>
+								<u-loading-icon
+								    v-else
+								    custom-class="van-action-sheet__loading"
+								    size="18"
+								    mode="circle"
+								/>
+							</view>
+							<!-- #ifdef MP -->
+						</button>
+						<!-- #endif -->
+						<u-line v-if="index !== actions.length - 1"></u-line>
+					</view>
+				</scroll-view>
+			</slot>
+			<u-gap
+			    bgColor="#eaeaec"
+			    height="6"
+			    v-if="cancelText"
+			></u-gap>
+			<view class="u-action-sheet__item-wrap__item u-action-sheet__cancel"
+				hover-class="u-action-sheet--hover" @tap="cancel" v-if="cancelText">
+				<text
+				    @touchmove.stop.prevent
+				    :hover-stay-time="150"
+				    class="u-action-sheet__cancel-text"
+				>{{cancelText}}</text>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	import { openType } from '../../libs/mixin/openType'
+	import { buttonMixin } from '../../libs/mixin/button'
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit } from '../../libs/function/index';
+	/**
+	 * ActionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://ijry.github.io/uview-plus/components/actionSheet.html
+	 * 
+	 * @property {Boolean}			show				操作菜单是否展示 (默认 false )
+	 * @property {String}			title				操作菜单标题
+	 * @property {String}			description			选项上方的描述信息
+	 * @property {Array<Object>}	actions				按钮的文字数组,见官方文档示例
+	 * @property {String}			cancelText			取消按钮的提示文字,不为空时显示按钮
+	 * @property {Boolean}			closeOnClickAction	点击某个菜单项时是否关闭弹窗 (默认 true )
+	 * @property {Boolean}			safeAreaInsetBottom	处理底部安全区 (默认 true )
+	 * @property {String}			openType			小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
+	 * @property {Boolean}			closeOnClickOverlay	点击遮罩是否允许关闭  (默认 true )
+	 * @property {Number|String}	round				圆角值,默认无圆角  (默认 0 )
+	 * @property {String}			lang				指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
+	 * @property {String}			sessionFrom			会话来源,openType="contact"时有效
+	 * @property {String}			sendMessageTitle	会话内消息卡片标题,openType="contact"时有效
+	 * @property {String}			sendMessagePath		会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+	 * @property {String}			sendMessageImg		会话内消息卡片图片,openType="contact"时有效
+	 * @property {Boolean}			showMessageCard		是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
+	 * @property {String}			appParameter		打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
+	 * 
+	 * @event {Function} select			点击ActionSheet列表项时触发 
+	 * @event {Function} close			点击取消按钮时触发
+	 * @event {Function} getuserinfo	用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
+	 * @event {Function} contact		客服消息回调,openType="contact"时有效
+	 * @event {Function} getphonenumber	获取用户手机号回调,openType="getPhoneNumber"时有效
+	 * @event {Function} error			当使用开放能力时,发生错误的回调,openType="error"时有效
+	 * @event {Function} launchapp		打开 APP 成功的回调,openType="launchApp"时有效
+	 * @event {Function} opensetting	在打开授权设置页后回调,openType="openSetting"时有效
+	 * @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
+	 */
+	export default {
+		name: "u-action-sheet",
+		// 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到
+		mixins: [openType, buttonMixin, mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			// 操作项目的样式
+			itemStyle() {
+				return (index) => {
+					let style = {};
+					if (this.actions[index].color) style.color = this.actions[index].color
+					if (this.actions[index].fontSize) style.fontSize = addUnit(this.actions[index].fontSize)
+					// 选项被禁用的样式
+					if (this.actions[index].disabled) style.color = '#c0c4cc'
+					return style;
+				}
+			},
+		},
+		emits: ["close", "select", "update:show"],
+		methods: {
+			closeHandler() {
+				// 允许点击遮罩关闭时,才发出close事件
+				if(this.closeOnClickOverlay) {
+					this.$emit('update:show', false)
+					this.$emit('close')
+				}
+			},
+			// 点击取消按钮
+			cancel() {
+				this.$emit('update:show', false)
+				this.$emit('close')
+			},
+			selectHandler(index) {
+				const item = this.actions[index]
+				if (item && !item.disabled && !item.loading) {
+					this.$emit('select', item)
+					if (this.closeOnClickAction) {
+						this.$emit('update:show', false)
+						this.$emit('close')
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$u-action-sheet-reset-button-width:100% !default;
+	$u-action-sheet-title-font-size: 16px !default;
+	$u-action-sheet-title-padding: 12px 30px !default;
+	$u-action-sheet-title-color: $u-main-color !default;
+	$u-action-sheet-header-icon-wrap-right:15px !default;
+	$u-action-sheet-header-icon-wrap-top:15px !default;
+	$u-action-sheet-description-font-size:13px !default;
+	$u-action-sheet-description-color:14px !default;
+	$u-action-sheet-description-margin: 18px 15px !default;
+	$u-action-sheet-item-wrap-item-padding:17px !default;
+	$u-action-sheet-item-wrap-name-font-size:16px !default;
+	$u-action-sheet-item-wrap-subname-font-size:13px !default;
+	$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
+	$u-action-sheet-item-wrap-subname-margin-top:10px !default;
+	$u-action-sheet-cancel-text-font-size:16px !default;
+	$u-action-sheet-cancel-text-color:$u-content-color !default;
+	$u-action-sheet-cancel-text-font-size:15px !default;
+	$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
+
+	.u-reset-button {
+		width: $u-action-sheet-reset-button-width;
+	}
+
+	.u-action-sheet {
+		text-align: center;
+		&__header {
+			position: relative;
+			padding: $u-action-sheet-title-padding;
+			&__title {
+				font-size: $u-action-sheet-title-font-size;
+				color: $u-action-sheet-title-color;
+				font-weight: bold;
+				text-align: center;
+			}
+
+			&__icon-wrap {
+				position: absolute;
+				right: $u-action-sheet-header-icon-wrap-right;
+				top: $u-action-sheet-header-icon-wrap-top;
+			}
+		}
+
+		&__description {
+			font-size: $u-action-sheet-description-font-size;
+			color: $u-tips-color;
+			margin: $u-action-sheet-description-margin;
+			text-align: center;
+		}
+
+		&__item-wrap {
+
+			&__item {
+				padding: $u-action-sheet-item-wrap-item-padding;
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				flex-direction: column;
+
+				&__name {
+					font-size: $u-action-sheet-item-wrap-name-font-size;
+					color: $u-main-color;
+					text-align: center;
+				}
+
+				&__subname {
+					font-size: $u-action-sheet-item-wrap-subname-font-size;
+					color: $u-action-sheet-item-wrap-subname-color;
+					margin-top: $u-action-sheet-item-wrap-subname-margin-top;
+					text-align: center;
+				}
+			}
+		}
+
+		&__cancel-text {
+			font-size: $u-action-sheet-cancel-text-font-size;
+			color: $u-action-sheet-cancel-text-color;
+			text-align: center;
+			// padding: $u-action-sheet-cancel-text-font-size;
+		}
+
+		&--hover {
+			background-color: $u-action-sheet-cancel-text-hover-background-color;
+		}
+	}
+</style>

+ 28 - 0
uni_modules/uview-plus/components/u-album/album.js

@@ -0,0 +1,28 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:47:24
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/album.js
+ */
+export default {
+    // album 组件
+    album: {
+        urls: [],
+        keyName: '',
+        singleSize: 180,
+        multipleSize: 70,
+        space: 6,
+        singleMode: 'scaleToFill',
+        multipleMode: 'aspectFill',
+        maxCount: 9,
+        previewFullImage: true,
+        rowCount: 3,
+        showMore: true,
+        autoWrap: false,
+        unit: 'px',
+        stop: true,
+    }
+}

+ 86 - 0
uni_modules/uview-plus/components/u-album/props.js

@@ -0,0 +1,86 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 图片地址,Array<String>|Array<Object>形式
+        urls: {
+            type: Array,
+            default: () => defProps.album.urls
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: () => defProps.album.keyName
+        },
+        // 单图时,图片长边的长度
+        singleSize: {
+            type: [String, Number],
+            default: () => defProps.album.singleSize
+        },
+        // 多图时,图片边长
+        multipleSize: {
+            type: [String, Number],
+            default: () => defProps.album.multipleSize
+        },
+        // 多图时,图片水平和垂直之间的间隔
+        space: {
+            type: [String, Number],
+            default: () => defProps.album.space
+        },
+        // 单图时,图片缩放裁剪的模式
+        singleMode: {
+            type: String,
+            default: () => defProps.album.singleMode
+        },
+        // 多图时,图片缩放裁剪的模式
+        multipleMode: {
+            type: String,
+            default: () => defProps.album.multipleMode
+        },
+        // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+        maxCount: {
+            type: [String, Number],
+            default: () => defProps.album.maxCount
+        },
+        // 是否可以预览图片
+        previewFullImage: {
+            type: Boolean,
+            default: () => defProps.album.previewFullImage
+        },
+        // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+        rowCount: {
+            type: [String, Number],
+            default: () => defProps.album.rowCount
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: () => defProps.album.showMore
+        },
+        // 图片形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: () => defProps.image.shape
+        },
+        // 圆角,单位任意
+        radius: {
+            type: [String, Number],
+            default: () => defProps.image.radius
+        },
+        // 自适应换行
+        autoWrap: {
+            type: Boolean,
+            default: () => defProps.album.autoWrap
+        },
+        // 单位
+        unit: {
+            type: [String],
+            default: () => defProps.album.unit
+        },
+        // 阻止点击冒泡
+        stop: {
+            type: Boolean,
+            default: () => defProps.album.stop
+        }
+    }
+})

+ 299 - 0
uni_modules/uview-plus/components/u-album/u-album.vue

@@ -0,0 +1,299 @@
+<template>
+    <view class="u-album">
+        <view
+            class="u-album__row"
+            ref="u-album__row"
+            v-for="(arr, index) in showUrls"
+            :forComputedUse="albumWidth"
+            :key="index"
+            :style="{flexWrap: autoWrap ? 'wrap' : 'nowrap'}"
+        >
+            <view
+                class="u-album__row__wrapper"
+                v-for="(item, index1) in arr"
+                :key="index1"
+                :style="[imageStyle(index + 1, index1 + 1)]"
+                @tap="onPreviewTap($event, getSrc(item))"
+            >
+                <image
+                    :src="getSrc(item)"
+                    :mode="
+                        urls.length === 1
+                            ? imageHeight > 0
+                                ? singleMode
+                                : 'widthFix'
+                            : multipleMode
+                    "
+                    :style="[
+                        {
+                            width: imageWidth,
+                            height: imageHeight,
+                            borderRadius: shape == 'circle' ? '10000px' : addUnit(radius)
+                        }
+                    ]"
+                ></image>
+                <view
+                    v-if="
+                        showMore &&
+                        urls.length > rowCount * showUrls.length &&
+                        index === showUrls.length - 1 &&
+                        index1 === showUrls[showUrls.length - 1].length - 1
+                    "
+                    class="u-album__row__wrapper__text"
+                    :style="{
+					    borderRadius: shape == 'circle' ? '50%' : addUnit(radius),
+				    }"
+                >
+                    <up-text
+                        :text="`+${urls.length - maxCount}`"
+                        color="#fff"
+                        :size="multipleSize * 0.3"
+                        align="center"
+                        customStyle="justify-content: center"
+                    ></up-text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import { props } from './props';
+import { mpMixin } from '../../libs/mixin/mpMixin';
+import { mixin } from '../../libs/mixin/mixin';
+import { addUnit, sleep } from '../../libs/function/index';
+import test from '../../libs/function/test';
+// #ifdef APP-NVUE
+// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+/**
+ * Album 相册
+ * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
+ * @tutorial https://ijry.github.io/uview-plus/components/album.html
+ *
+ * @property {Array}           urls             图片地址列表 Array<String>|Array<Object>形式
+ * @property {String}          keyName          指定从数组的对象元素中读取哪个属性作为图片地址
+ * @property {String | Number} singleSize       单图时,图片长边的长度  (默认 180 )
+ * @property {String | Number} multipleSize     多图时,图片边长 (默认 70 )
+ * @property {String | Number} space            多图时,图片水平和垂直之间的间隔 (默认 6 )
+ * @property {String}          singleMode       单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
+ * @property {String}          multipleMode     多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
+ * @property {String | Number} maxCount         取消按钮的提示文字 (默认 9 )
+ * @property {Boolean}         previewFullImage 是否可以预览图片 (默认 true )
+ * @property {String | Number} rowCount         每行展示图片数量,如设置,singleSize和multipleSize将会无效	(默认 3 )
+ * @property {Boolean}         showMore         超出maxCount时是否显示查看更多的提示 (默认 true )
+ * @property {String}          shape            图片形状,circle-圆形,square-方形 (默认 'square' )
+ * @property {String | Number} radius           圆角值,单位任意,如果为数值,则为px单位 (默认 0 )
+ * @property {Boolean}         autoWrap         自适应换行模式,不受rowCount限制,图片会自动换行 (默认 false )
+ * @property {String}          unit             图片单位 (默认 px )
+ * @event    {Function}        albumWidth       某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送  (回调参数 width )
+ * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
+ */
+export default {
+    name: 'u-album',
+    mixins: [mpMixin, mixin, props],
+    data() {
+        return {
+            // 单图的宽度
+            singleWidth: 0,
+            // 单图的高度
+            singleHeight: 0,
+            // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
+            singlePercent: 0.6
+        }
+    },
+    watch: {
+        urls: {
+            immediate: true,
+            handler(newVal) {
+                if (newVal.length === 1) {
+                    this.getImageRect()
+                }
+            }
+        }
+    },
+    computed: {
+        imageStyle() {
+            return (index1, index2) => {
+                const { space, rowCount, multipleSize, urls } = this,
+                    rowLen = this.showUrls.length,
+                    allLen = this.urls.length
+                const style = {
+                    marginRight: addUnit(space),
+                    marginBottom: addUnit(space)
+                }
+                // 如果为最后一行,则每个图片都无需下边框
+                if (index1 === rowLen && !this.autoWrap) style.marginBottom = 0
+                // 每行的最右边一张和总长度的最后一张无需右边框
+                if (!this.autoWrap) {
+                    if (
+                        index2 === rowCount ||
+                        (index1 === rowLen &&
+                            index2 === this.showUrls[index1 - 1].length)
+                    )
+                        style.marginRight = 0
+                }
+                return style
+            }
+        },
+        // 将数组划分为二维数组
+        showUrls() {
+            if (this.autoWrap) {
+                return [ this.urls.slice(0, this.maxCount) ];
+            } else {
+                const arr = []
+                this.urls.map((item, index) => {
+                    // 限制最大展示数量
+                    if (index + 1 <= this.maxCount) {
+                        // 计算该元素为第几个素组内
+                        const itemIndex = Math.floor(index / this.rowCount)
+                        // 判断对应的索引是否存在
+                        if (!arr[itemIndex]) {
+                            arr[itemIndex] = []
+                        }
+                        arr[itemIndex].push(item)
+                    }
+                })
+                return arr
+            }
+        },
+        imageWidth() {
+            return addUnit(
+                this.urls.length === 1 ? this.singleWidth : this.multipleSize, this.unit
+            )
+        },
+        imageHeight() {
+            return addUnit(
+                this.urls.length === 1 ? this.singleHeight : this.multipleSize, this.unit
+            )
+        },
+        // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
+        // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
+        albumWidth() {
+            let width = 0
+            if (this.urls.length === 1) {
+                width = this.singleWidth
+            } else {
+                width =
+                    this.showUrls[0].length * this.multipleSize +
+                    this.space * (this.showUrls[0].length - 1)
+            }
+            this.$emit('albumWidth', width)
+            return width
+        }
+    },
+    emits: ['preview', 'albumWidth'],
+    methods: {
+        addUnit,
+        // 预览图片
+        onPreviewTap(e, url) {
+            const urls = this.urls.map((item) => {
+                return this.getSrc(item)
+            })
+            if (this.previewFullImage) {
+                uni.previewImage({
+                    current: url,
+                    urls
+                })
+                // 是否阻止事件传播
+                this.stop && this.preventEvent(e)
+            } else {
+                this.$emit('preview', {
+                    urls,
+                    currentIndex: urls.indexOf(url)
+                })
+            }
+        },
+        // 获取图片的路径
+        getSrc(item) {
+            return test.object(item)
+                ? (this.keyName && item[this.keyName]) || item.src
+                : item
+        },
+        // 单图时,获取图片的尺寸
+        // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
+        // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+        getImageRect() {
+            const src = this.getSrc(this.urls[0])
+            uni.getImageInfo({
+                src,
+                success: (res) => {
+                    let singleSize = this.singleSize;
+                    // 单位
+                    let unit = '';
+                    if (Number.isNaN(Number(this.singleSize))) {
+                        // 大小中有字符 则记录字符
+                        unit = this.singleSize.replace(/\d+/g, ''); // 单位
+                        singleSize = Number(this.singleSize.replace(/\D+/g, ''), 10); // 具体值
+                    }
+
+                    // 判断图片横向还是竖向展示方式
+                    const isHorizotal = res.width >= res.height
+                    this.singleWidth = isHorizotal
+                        ? singleSize
+                        : (res.width / res.height) * singleSize
+                    this.singleHeight = !isHorizotal
+                        ? singleSize
+                        : (res.height / res.width) * this.singleWidth
+
+                    // 如果有单位统一设置单位
+                    if(unit != null && unit !== ''){
+                        this.singleWidth = this.singleWidth + unit
+                        this.singleHeight = this.singleHeight + unit
+                    }
+                },
+                fail: () => {
+                    this.getComponentWidth()
+                }
+            })
+        },
+        // 获取组件的宽度
+        async getComponentWidth() {
+            // 延时一定时间,以获取dom尺寸
+            await sleep(30)
+            // #ifndef APP-NVUE
+            this.$uGetRect('.u-album__row').then((size) => {
+                this.singleWidth = size.width * this.singlePercent
+            })
+            // #endif
+
+            // #ifdef APP-NVUE
+            // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
+            const ref = this.$refs['u-album__row'][0]
+            ref &&
+                dom.getComponentRect(ref, (res) => {
+                    this.singleWidth = res.size.width * this.singlePercent
+                })
+            // #endif
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.u-album {
+    @include flex(column);
+
+    &__row {
+        @include flex(row);
+
+        &__wrapper {
+            position: relative;
+
+            &__text {
+                position: absolute;
+                top: 0;
+                left: 0;
+                right: 0;
+                bottom: 0;
+                background-color: rgba(0, 0, 0, 0.3);
+                @include flex(row);
+                justify-content: center;
+                align-items: center;
+            }
+        }
+    }
+}
+</style>

+ 22 - 0
uni_modules/uview-plus/components/u-alert/alert.js

@@ -0,0 +1,22 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:48:53
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/alert.js
+ */
+export default {
+    // alert警告组件
+    alert: {
+        title: '',
+        type: 'warning',
+        description: '',
+        closable: false,
+        showIcon: false,
+        effect: 'light',
+        center: false,
+        fontSize: 14
+    }
+}

+ 46 - 0
uni_modules/uview-plus/components/u-alert/props.js

@@ -0,0 +1,46 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 显示文字
+        title: {
+            type: String,
+            default: () => defProps.alert.title
+        },
+        // 主题,success/warning/info/error
+        type: {
+            type: String,
+            default: () => defProps.alert.type
+        },
+        // 辅助性文字
+        description: {
+            type: String,
+            default: () => defProps.alert.description
+        },
+        // 是否可关闭
+        closable: {
+            type: Boolean,
+            default: () => defProps.alert.closable
+        },
+        // 是否显示图标
+        showIcon: {
+            type: Boolean,
+            default: () => defProps.alert.showIcon
+        },
+        // 浅或深色调,light-浅色,dark-深色
+        effect: {
+            type: String,
+            default: () => defProps.alert.effect
+        },
+        // 文字是否居中
+        center: {
+            type: Boolean,
+            default: () => defProps.alert.center
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: () => defProps.alert.fontSize
+        }
+    }
+})

+ 250 - 0
uni_modules/uview-plus/components/u-alert/u-alert.vue

@@ -0,0 +1,250 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :show="show"
+	>
+		<view
+		    class="u-alert"
+		    :class="[`u-alert--${type}--${effect}`]"
+		    @tap.stop="clickHandler"
+		    :style="[addStyle(customStyle)]"
+		>
+			<view
+			    class="u-alert__icon"
+			    v-if="showIcon"
+			>
+				<u-icon
+				    :name="iconName"
+				    size="18"
+				    :color="iconColor"
+				></u-icon>
+			</view>
+			<view
+			    class="u-alert__content"
+			    :style="[{
+					paddingRight: closable ? '20px' : 0
+				}]"
+			>
+				<text
+				    class="u-alert__content__title"
+				    v-if="title"
+					:style="[{
+						fontSize: addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ title }}</text>
+				<text
+				    class="u-alert__content__desc"
+					v-if="description"
+					:style="[{
+						fontSize: addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ description }}</text>
+			</view>
+			<view
+			    class="u-alert__close"
+			    v-if="closable"
+			    @tap.stop="closeHandler"
+			>
+				<u-icon
+				    name="close"
+				    :color="iconColor"
+				    size="15"
+				></u-icon>
+			</view>
+		</view>
+	</u-transition>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit, addStyle } from '../../libs/function/index';
+	/**
+	 * Alert  警告提示
+	 * @description 警告提示,展现需要关注的信息。
+	 * @tutorial https://ijry.github.io/uview-plus/components/alertTips.html
+	 * 
+	 * @property {String}			title       显示的文字 
+	 * @property {String}			type        使用预设的颜色  (默认 'warning' )
+	 * @property {String}			description 辅助性文字,颜色比title浅一点,字号也小一点,可选  
+	 * @property {Boolean}			closable    关闭按钮(默认为叉号icon图标)  (默认 false )
+	 * @property {Boolean}			showIcon    是否显示左边的辅助图标   ( 默认 false )
+	 * @property {String}			effect      多图时,图片缩放裁剪的模式  (默认 'light' )
+	 * @property {Boolean}			center		文字是否居中  (默认 false )
+	 * @property {String | Number}	fontSize    字体大小  (默认 14 )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @event    {Function}        click       点击组件时触发
+	 * @event    {Function}        close       点击关闭按钮时触发
+	 * @example  <u-alert :title="title"  type = "warning" :closable="closable" :description = "description"></u-alert>
+	 */
+	export default {
+		name: 'u-alert',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				show: true
+			}
+		},
+		computed: {
+			iconColor() {
+				return this.effect === 'light' ? this.type : '#fff'
+			},
+			// 不同主题对应不同的图标
+			iconName() {
+				switch (this.type) {
+					case 'success':
+						return 'checkmark-circle-fill';
+						break;
+					case 'error':
+						return 'close-circle-fill';
+						break;
+					case 'warning':
+						return 'error-circle-fill';
+						break;
+					case 'info':
+						return 'info-circle-fill';
+						break;
+					case 'primary':
+						return 'more-circle-fill';
+						break;
+					default: 
+						return 'error-circle-fill';
+				}
+			}
+		},
+		emits: ["click","close"],
+		methods: {
+			addUnit,
+			addStyle,
+			// 点击内容
+			clickHandler() {
+				this.$emit('click')
+			},
+			// 点击关闭按钮
+			closeHandler() {
+				this.show = false   
+				this.$emit('close')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-alert {
+		position: relative;
+		background-color: $u-primary;
+		padding: 8px 10px;
+		@include flex(row);
+		align-items: center;
+		border-top-left-radius: 4px;
+		border-top-right-radius: 4px;
+		border-bottom-left-radius: 4px;
+		border-bottom-right-radius: 4px;
+
+		&--primary--dark {
+			background-color: $u-primary;
+		}
+
+		&--primary--light {
+			background-color: #ecf5ff;
+		}
+
+		&--error--dark {
+			background-color: $u-error;
+		}
+
+		&--error--light {
+			background-color: #FEF0F0;
+		}
+
+		&--success--dark {
+			background-color: $u-success;
+		}
+
+		&--success--light {
+			background-color: #f5fff0;
+		}
+
+		&--warning--dark {
+			background-color: $u-warning;
+		}
+
+		&--warning--light {
+			background-color: #FDF6EC;
+		}
+
+		&--info--dark {
+			background-color: $u-info;
+		}
+
+		&--info--light {
+			background-color: #f4f4f5;
+		}
+
+		&__icon {
+			margin-right: 5px;
+		}
+
+		&__content {
+			@include flex(column);
+			flex: 1;
+
+			&__title {
+				color: $u-main-color;
+				font-size: 14px;
+				font-weight: bold;
+				color: #fff;
+				margin-bottom: 2px;
+			}
+
+			&__desc {
+				color: $u-main-color;
+				font-size: 14px;
+				flex-wrap: wrap;
+				color: #fff;
+			}
+		}
+
+		&__title--dark,
+		&__desc--dark {
+			color: #FFFFFF;
+		}
+
+		&__text--primary--light,
+		&__text--primary--light {
+			color: $u-primary;
+		}
+
+		&__text--success--light,
+		&__text--success--light {
+			color: $u-success;
+		}
+
+		&__text--warning--light,
+		&__text--warning--light {
+			color: $u-warning;
+		}
+
+		&__text--error--light,
+		&__text--error--light {
+			color: $u-error;
+		}
+
+		&__text--info--light,
+		&__text--info--light {
+			color: $u-info;
+		}
+
+		&__close {
+			position: absolute;
+			top: 11px;
+			right: 10px;
+		}
+	}
+</style>

+ 23 - 0
uni_modules/uview-plus/components/u-avatar-group/avatarGroup.js

@@ -0,0 +1,23 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:49:55
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/avatarGroup.js
+ */
+export default {
+    // avatarGroup 组件
+    avatarGroup: {
+        urls: [],
+        maxCount: 5,
+        shape: 'circle',
+        mode: 'scaleToFill',
+        showMore: true,
+        size: 40,
+        keyName: '',
+        gap: 0.5,
+		extraValue: 0
+    }
+}

+ 54 - 0
uni_modules/uview-plus/components/u-avatar-group/props.js

@@ -0,0 +1,54 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 头像图片组
+        urls: {
+            type: Array,
+            default: () => defProps.avatarGroup.urls
+        },
+        // 最多展示的头像数量
+        maxCount: {
+            type: [String, Number],
+            default: () => defProps.avatarGroup.maxCount
+        },
+        // 头像形状
+        shape: {
+            type: String,
+            default: () => defProps.avatarGroup.shape
+        },
+        // 图片裁剪模式
+        mode: {
+            type: String,
+            default: () => defProps.avatarGroup.mode
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: () => defProps.avatarGroup.showMore
+        },
+        // 头像大小
+        size: {
+            type: [String, Number],
+            default: () => defProps.avatarGroup.size
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: () => defProps.avatarGroup.keyName
+        },
+		// 头像之间的遮挡比例
+        gap: {
+            type: [String, Number],
+            validator(value) {
+                return value >= 0 && value <= 1
+            },
+            default: () => defProps.avatarGroup.gap
+        },
+		// 需额外显示的值
+		extraValue: {
+			type: [Number, String],
+			default: () => defProps.avatarGroup.extraValue
+		}
+    }
+})

+ 109 - 0
uni_modules/uview-plus/components/u-avatar-group/u-avatar-group.vue

@@ -0,0 +1,109 @@
+<template>
+	<view class="u-avatar-group">
+		<view
+		    class="u-avatar-group__item"
+		    v-for="(item, index) in showUrl"
+		    :key="index"
+		    :style="{
+				marginLeft: index === 0 ? 0 : addUnit(-size * gap)
+			}"
+		>
+			<u-avatar
+			    :size="size"
+			    :shape="shape"
+			    :mode="mode"
+			    :src="testObject(item) ? keyName && item[keyName] || item.url : item"
+			></u-avatar>
+			<view
+			    class="u-avatar-group__item__show-more"
+			    v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
+				@tap="clickHandler"
+			>
+				<up-text
+				    color="#ffffff"
+				    :size="size * 0.4"
+				    :text="`+${extraValue || urls.length - showUrl.length}`"
+					align="center"
+					customStyle="justify-content: center"
+				></up-text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit } from '../../libs/function/index';
+	import test from '../../libs/function/test';
+	/**
+	 * AvatarGroup  头像组
+	 * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+	 * @tutorial https://ijry.github.io/uview-plus/components/avatar.html
+	 * 
+	 * @property {Array}           urls     头像图片组 (默认 [] )
+	 * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
+	 * @property {String}          shape    头像形状( 'circle' (默认) | 'square' )
+	 * @property {String}          mode     图片裁剪模式(默认 'scaleToFill' )
+	 * @property {Boolean}         showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
+	 * @property {String | Number} size      头像大小 (默认 40 )
+	 * @property {String}          keyName  指定从数组的对象元素中读取哪个属性作为图片地址 
+	 * @property {String | Number} gap      头像之间的遮挡比例(0.4代表遮挡40%)  (默认 0.5 )
+	 * @property {String | Number} extraValue  需额外显示的值
+	 * @event    {Function}        showMore 头像组更多点击
+	 * @example  <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
+	 */
+	export default {
+		name: 'u-avatar-group',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			showUrl() {
+				return this.urls.slice(0, this.maxCount)
+			}
+		},
+		emits: ["showMore"],
+		methods: {
+			addUnit,
+			testObject: test.object,
+			clickHandler() {
+				this.$emit('showMore')
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-avatar-group {
+		@include flex;
+
+		&__item {
+			margin-left: -10px;
+			position: relative;
+
+			&--no-indent {
+				// 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
+				margin-left: 0;
+			}
+
+			&__show-more {
+				position: absolute;
+				top: 0;
+				bottom: 0;
+				left: 0;
+				right: 0;
+				background-color: rgba(0, 0, 0, 0.3);
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				border-radius: 100px;
+			}
+		}
+	}
+</style>

+ 28 - 0
uni_modules/uview-plus/components/u-avatar/avatar.js

@@ -0,0 +1,28 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:49:22
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/avatar.js
+ */
+export default {
+    // avatar 组件
+    avatar: {
+        src: '',
+        shape: 'circle',
+        size: 40,
+        mode: 'scaleToFill',
+        text: '',
+        bgColor: '#c0c4cc',
+        color: '#ffffff',
+        fontSize: 18,
+        icon: '',
+        mpAvatar: false,
+        randomBgColor: false,
+        defaultUrl: '',
+        colorIndex: '',
+        name: ''
+    }
+}

+ 81 - 0
uni_modules/uview-plus/components/u-avatar/props.js

@@ -0,0 +1,81 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+import test from '../../libs/function/test';
+export const props = defineMixin({
+    props: {
+        // 头像图片路径(不能为相对路径)
+        src: {
+            type: String,
+            default: () => defProps.avatar.src
+        },
+        // 头像形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: () => defProps.avatar.shape
+        },
+        // 头像尺寸
+        size: {
+            type: [String, Number],
+            default: () => defProps.avatar.size
+        },
+        // 裁剪模式
+        mode: {
+            type: String,
+            default: () => defProps.avatar.mode
+        },
+        // 显示的文字
+        text: {
+            type: String,
+            default: () => defProps.avatar.text
+        },
+        // 背景色
+        bgColor: {
+            type: String,
+            default: () => defProps.avatar.bgColor
+        },
+        // 文字颜色
+        color: {
+            type: String,
+            default: () => defProps.avatar.color
+        },
+        // 文字大小
+        fontSize: {
+            type: [String, Number],
+            default: () => defProps.avatar.fontSize
+        },
+        // 显示的图标
+        icon: {
+            type: String,
+            default: () => defProps.avatar.icon
+        },
+        // 显示小程序头像,只对百度,微信,QQ小程序有效
+        mpAvatar: {
+            type: Boolean,
+            default: () => defProps.avatar.mpAvatar
+        },
+        // 是否使用随机背景色
+        randomBgColor: {
+            type: Boolean,
+            default: () => defProps.avatar.randomBgColor
+        },
+        // 加载失败的默认头像(组件有内置默认图片)
+        defaultUrl: {
+            type: String,
+            default: () => defProps.avatar.defaultUrl
+        },
+        // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+        colorIndex: {
+            type: [String, Number],
+            // 校验参数规则,索引在0-19之间
+            validator(n) {
+                return test.range(n, [0, 19]) || n === ''
+            },
+            default: () => defProps.avatar.colorIndex
+        },
+        // 组件标识符
+        name: {
+            type: String,
+            default: () => defProps.avatar.name
+        }
+    }
+})

Різницю між файлами не показано, бо вона завелика
+ 61 - 0
uni_modules/uview-plus/components/u-avatar/u-avatar.vue


+ 27 - 0
uni_modules/uview-plus/components/u-back-top/backtop.js

@@ -0,0 +1,27 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:50:18
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/backtop.js
+ */
+export default {
+    // backtop组件
+    backtop: {
+        mode: 'circle',
+        icon: 'arrow-upward',
+        text: '',
+        duration: 100,
+        scrollTop: 0,
+        top: 400,
+        bottom: 100,
+        right: 20,
+        zIndex: 9,
+        iconStyle: {
+            color: '#909399',
+            fontSize: '19px'
+        }
+    }
+}

+ 56 - 0
uni_modules/uview-plus/components/u-back-top/props.js

@@ -0,0 +1,56 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 返回顶部的形状,circle-圆形,square-方形
+        mode: {
+            type: String,
+            default: () => defProps.backtop.mode
+        },
+        // 自定义图标
+        icon: {
+            type: String,
+            default: () => defProps.backtop.icon
+        },
+        // 提示文字
+        text: {
+            type: String,
+            default: () => defProps.backtop.text
+        },
+        // 返回顶部滚动时间
+        duration: {
+            type: [String, Number],
+            default: () => defProps.backtop.duration
+        },
+        // 滚动距离
+        scrollTop: {
+            type: [String, Number],
+            default: () => defProps.backtop.scrollTop
+        },
+        // 距离顶部多少距离显示,单位px
+        top: {
+            type: [String, Number],
+            default: () => defProps.backtop.top
+        },
+        // 返回顶部按钮到底部的距离,单位px
+        bottom: {
+            type: [String, Number],
+            default: () => defProps.backtop.bottom
+        },
+        // 返回顶部按钮到右边的距离,单位px
+        right: {
+            type: [String, Number],
+            default: () => defProps.backtop.right
+        },
+        // 层级
+        zIndex: {
+            type: [String, Number],
+            default: () => defProps.backtop.zIndex
+        },
+        // 图标的样式,对象形式
+        iconStyle: {
+            type: Object,
+            default: () => defProps.backtop.iconStyle
+        }
+    }
+})

+ 132 - 0
uni_modules/uview-plus/components/u-back-top/u-back-top.vue

@@ -0,0 +1,132 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :customStyle="backTopStyle"
+	    :show="show"
+	>
+		<view
+		    class="u-back-top"
+			:style="[contentStyle]"
+		    v-if="!$slots.default && !$slots.$default"
+			@click="backToTop"
+		>
+			<u-icon
+			    :name="icon"
+			    :custom-style="iconStyle"
+			></u-icon>
+			<text
+			    v-if="text"
+			    class="u-back-top__text"
+			>{{text}}</text>
+		</view>
+		<slot v-else />
+	</u-transition>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit, addStyle, getPx, deepMerge, error } from '../../libs/function/index';
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom')
+	// #endif
+	/**
+	 * backTop 返回顶部
+	 * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/backTop.html
+	 * 
+	 * @property {String}			mode  		返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
+	 * @property {String} 			icon 		自定义图标 (默认 'arrow-upward' ) 见官方文档示例
+	 * @property {String} 			text 		提示文字 
+	 * @property {String | Number}  duration	返回顶部滚动时间 (默认 100)
+	 * @property {String | Number}  scrollTop	滚动距离 (默认 0 )
+	 * @property {String | Number}  top  		距离顶部多少距离显示,单位px (默认 400 )
+	 * @property {String | Number}  bottom  	返回顶部按钮到底部的距离,单位px (默认 100 )
+	 * @property {String | Number}  right  		返回顶部按钮到右边的距离,单位px (默认 20 )
+	 * @property {String | Number}  zIndex 		层级   (默认 9 )
+	 * @property {Object<Object>}  	iconStyle 	图标的样式,对象形式   (默认 {color: '#909399',fontSize: '19px'})
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * 
+	 * @example <u-back-top :scrollTop="scrollTop"></u-back-top>
+	 */
+	export default {
+		name: 'u-back-top',
+		mixins: [mpMixin, mixin, props],
+		computed: {
+			backTopStyle() {
+				// 动画组件样式
+				const style = {
+					bottom: addUnit(this.bottom),
+					right: addUnit(this.right),
+					width: '40px',
+					height: '40px',
+					position: 'fixed',
+					zIndex: 10,
+				}
+				return style
+			},
+			show() {
+				return getPx(this.scrollTop) > getPx(this.top)
+			},
+			contentStyle() {
+				const style = {}
+				let radius = 0
+				// 是否圆形
+				if(this.mode === 'circle') {
+					radius = '100px'
+				} else {
+					radius = '4px'
+				}
+				// 为了兼容安卓nvue,只能这么分开写
+				style.borderTopLeftRadius = radius
+				style.borderTopRightRadius = radius
+				style.borderBottomLeftRadius = radius
+				style.borderBottomRightRadius = radius
+				return deepMerge(style, addStyle(this.customStyle))
+			}
+		},
+		emits: ["click"],
+		methods: {
+			backToTop() {
+				// #ifdef APP-NVUE
+				if (!this.$parent.$refs['u-back-top']) {
+					error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
+				}
+				dom.scrollToElement(this.$parent.$refs['u-back-top'], {
+					offset: 0
+				})
+				// #endif
+				
+				// #ifndef APP-NVUE
+				uni.pageScrollTo({
+					scrollTop: 0,
+					duration: this.duration
+				});
+				// #endif
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+     $u-back-top-flex:1 !default;
+     $u-back-top-height:100% !default;
+     $u-back-top-background-color:#E1E1E1 !default;
+     $u-back-top-tips-font-size:12px !default;
+	.u-back-top {
+		@include flex;
+		flex-direction: column;
+		align-items: center;
+		flex:$u-back-top-flex;
+		height: $u-back-top-height;
+		justify-content: center;
+		background-color: $u-back-top-background-color;
+
+		&__tips {
+			font-size:$u-back-top-tips-font-size;
+			transform: scale(0.8);
+		}
+	}
+</style>

+ 27 - 0
uni_modules/uview-plus/components/u-badge/badge.js

@@ -0,0 +1,27 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-23 19:51:50
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/badge.js
+ */
+export default {
+    // 徽标数组件
+    badge: {
+        isDot: false,
+        value: '',
+        show: true,
+        max: 999,
+        type: 'error',
+        showZero: false,
+        bgColor: null,
+        color: null,
+        shape: 'circle',
+        numberType: 'overflow',
+        offset: [],
+        inverted: false,
+        absolute: false
+    }
+}

+ 79 - 0
uni_modules/uview-plus/components/u-badge/props.js

@@ -0,0 +1,79 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 是否显示圆点
+        isDot: {
+            type: Boolean,
+            default: () => defProps.badge.isDot
+        },
+        // 显示的内容
+        value: {
+            type: [Number, String],
+            default: () => defProps.badge.value
+        },
+        // 显示的内容
+        modelValue: {
+            type: [Number, String],
+            default: () => defProps.badge.modelValue
+        },
+        // 是否显示
+        show: {
+            type: Boolean,
+            default: () => defProps.badge.show
+        },
+        // 最大值,超过最大值会显示 '{max}+'
+        max: {
+            type: [Number, String],
+            default: () => defProps.badge.max
+        },
+        // 主题类型,error|warning|success|primary
+        type: {
+            type: String,
+            default: () => defProps.badge.type
+        },
+        // 当数值为 0 时,是否展示 Badge
+        showZero: {
+            type: Boolean,
+            default: () => defProps.badge.showZero
+        },
+        // 背景颜色,优先级比type高,如设置,type参数会失效
+        bgColor: {
+            type: [String, null],
+            default: () => defProps.badge.bgColor
+        },
+        // 字体颜色
+        color: {
+            type: [String, null],
+            default: () => defProps.badge.color
+        },
+        // 徽标形状,circle-四角均为圆角,horn-左下角为直角
+        shape: {
+            type: String,
+            default: () => defProps.badge.shape
+        },
+        // 设置数字的显示方式,overflow|ellipsis|limit
+        // overflow会根据max字段判断,超出显示`${max}+`
+        // ellipsis会根据max判断,超出显示`${max}...`
+        // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
+        numberType: {
+            type: String,
+            default: () => defProps.badge.numberType
+        },
+        // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+        offset: {
+            type: Array,
+            default: () => defProps.badge.offset
+        },
+        // 是否反转背景和字体颜色
+        inverted: {
+            type: Boolean,
+            default: () => defProps.badge.inverted
+        },
+        // 是否绝对定位
+        absolute: {
+            type: Boolean,
+            default: () => defProps.badge.absolute
+        }
+    }
+})

+ 176 - 0
uni_modules/uview-plus/components/u-badge/u-badge.vue

@@ -0,0 +1,176 @@
+<template>
+	<text
+		v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
+		:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
+		:style="[addStyle(customStyle), badgeStyle]"
+		class="u-badge"
+	>{{ isDot ? '' :showValue }}</text>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addStyle, addUnit } from '../../libs/function/index';
+	/**
+	 * badge 徽标数
+	 * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/badge.html
+	 * 
+	 * @property {Boolean} 			isDot 		是否显示圆点 (默认 false )
+	 * @property {String | Number} 	value 		显示的内容
+	 * @property {Boolean} 			show 		是否显示 (默认 true )
+	 * @property {String | Number} 	max 		最大值,超过最大值会显示 '{max}+'  (默认999)
+	 * @property {String} 			type 		主题类型,error|warning|success|primary (默认 'error' )
+	 * @property {Boolean} 			showZero	当数值为 0 时,是否展示 Badge (默认 false )
+	 * @property {String} 			bgColor 	背景颜色,优先级比type高,如设置,type参数会失效
+	 * @property {String} 			color 		字体颜色 (默认 '#ffffff' )
+	 * @property {String} 			shape 		徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
+	 * @property {String} 			numberType	设置数字的显示方式,overflow|ellipsis|limit  (默认 'overflow' )
+	 * @property {Array}} 			offset		设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+	 * @property {Boolean} 			inverted	是否反转背景和字体颜色(默认 false )
+	 * @property {Boolean} 			absolute	是否绝对定位(默认 false )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @example <u-badge :type="type" :count="count"></u-badge>
+	 */
+	export default {
+		name: 'u-badge',
+		mixins: [mpMixin, props, mixin],
+		computed: {
+			// 是否将badge中心与父组件右上角重合
+			boxStyle() {
+				let style = {};
+				return style;
+			},
+			// 整个组件的样式
+			badgeStyle() {
+				const style = {}
+				if(this.color) {
+					style.color = this.color
+				}
+				if (this.bgColor && !this.inverted) {
+					style.backgroundColor = this.bgColor
+				}
+				if (this.absolute) {
+					style.position = 'absolute'
+					// 如果有设置offset参数
+					if(this.offset.length) {
+						// top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
+						const top = this.offset[0]
+						const right = this.offset[1] || top
+						style.top = addUnit(top)
+						style.right = addUnit(right)
+					}
+				}
+				return style
+			},
+			showValue() {
+				switch (this.numberType) {
+					case "overflow":
+						return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
+						break;
+					case "ellipsis":
+						return Number(this.value) > Number(this.max) ? "..." : this.value
+						break;
+					case "limit":
+						return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
+							Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
+								1e3 * 100) / 100 + "k" : this.value
+						break;
+					default:
+						return Number(this.value)
+				}
+			},
+		},
+		methods: {
+			addStyle
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	$u-badge-primary: $u-primary !default;
+	$u-badge-error: $u-error !default;
+	$u-badge-success: $u-success !default;
+	$u-badge-info: $u-info !default;
+	$u-badge-warning: $u-warning !default;
+	$u-badge-dot-radius: 100px !default;
+	$u-badge-dot-size: 8px !default;
+	$u-badge-dot-right: 4px !default;
+	$u-badge-dot-top: 0 !default;
+	$u-badge-text-font-size: 11px !default;
+	$u-badge-text-right: 10px !default;
+	$u-badge-text-padding: 2px 5px !default;
+	$u-badge-text-align: center !default;
+	$u-badge-text-color: #FFFFFF !default;
+
+	.u-badge {
+		border-top-right-radius: $u-badge-dot-radius;
+		border-top-left-radius: $u-badge-dot-radius;
+		border-bottom-left-radius: $u-badge-dot-radius;
+		border-bottom-right-radius: $u-badge-dot-radius;
+		@include flex;
+		line-height: $u-badge-text-font-size;
+		text-align: $u-badge-text-align;
+		font-size: $u-badge-text-font-size;
+		color: $u-badge-text-color;
+
+		&--dot {
+			height: $u-badge-dot-size;
+			width: $u-badge-dot-size;
+		}
+		
+		&--inverted {
+			font-size: 13px;
+		}
+		
+		&--not-dot {
+			padding: $u-badge-text-padding;
+		}
+
+		&--horn {
+			border-bottom-left-radius: 0;
+		}
+
+		&--primary {
+			background-color: $u-badge-primary;
+		}
+		
+		&--primary--inverted {
+			color: $u-badge-primary;
+		}
+
+		&--error {
+			background-color: $u-badge-error;
+		}
+		
+		&--error--inverted {
+			color: $u-badge-error;
+		}
+
+		&--success {
+			background-color: $u-badge-success;
+		}
+		
+		&--success--inverted {
+			color: $u-badge-success;
+		}
+
+		&--info {
+			background-color: $u-badge-info;
+		}
+		
+		&--info--inverted {
+			color: $u-badge-info;
+		}
+
+		&--warning {
+			background-color: $u-badge-warning;
+		}
+		
+		&--warning--inverted {
+			color: $u-badge-warning;
+		}
+	}
+</style>

+ 27 - 0
uni_modules/uview-plus/components/u-box/props.js

@@ -0,0 +1,27 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const propsBox = defineMixin({
+    props: {
+        // 背景色
+        bgColors: {
+            type: [Array],
+            default: ['#EEFCFF', '#FCF8FF', '#FDF8F2']
+        },
+        // 高度
+        height: {
+            type: [String],
+            default: "160px"
+        },
+        // 圆角
+        borderRadius: {
+            type: [String],
+            default: "6px"
+        },
+        // 间隔
+        gap: {
+            type: [String],
+            default: "15px"
+        },
+    }
+})

+ 91 - 0
uni_modules/uview-plus/components/u-box/u-box.vue

@@ -0,0 +1,91 @@
+<template>
+	<view class="u-box" :style="[{height: height}, addStyle(customStyle)]">
+        <view class="u-box__left" :style="{borderRadius: borderRadius, backgroundColor: bgColors[0]}">
+            <slot name="left">左</slot>
+        </view>
+        <view class="u-box__gap" :style="{width: gap, height: height}"></view>
+        <view class="u-box__right">
+            <view class="u-box__right-top" :style="{borderRadius: borderRadius, backgroundColor: bgColors[1]}">
+                <slot name="rightTop">右上</slot>
+            </view>
+            <view class="u-box__right-gap" :style="{height: gap}"></view>
+            <view class="u-box__right-bottom" :style="{borderRadius: borderRadius, backgroundColor: bgColors[2]}">
+                <slot name="rightBottom">右下</slot>
+            </view>
+        </view>
+	</view>
+</template>
+
+<script>
+	import { propsBox } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addStyle } from '../../libs/function/index';
+	import test from '../../libs/function/test';
+	/**
+	 * box 盒子
+	 * @description box盒子一般为左边一个盒子,右侧两个等高的半盒组成,常用于App首页座位重点突出。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/box.html
+	 * @property {Array}	bgColors			背景色
+	 * @property {String}	height			    高度
+     * @property {String}	borderRadius		圆角
+	 * @property {Object}   customStyle		    定义需要用到的外部样式
+	 * 
+	 * @event {Function}			click			点击cell列表时触发
+	 * @example <up-box colors=['blue', 'red', 'yellow'] height="200px"></up-box>
+	 */
+	export default {
+		name: 'up-box',
+		data() {
+			return {
+			}
+		},
+		mixins: [mpMixin, mixin, propsBox],
+		computed: {
+		},
+		emits: [],
+		methods: {
+			addStyle,
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-box {
+        /* #ifndef APP-NVUE */
+        /* #endif */
+        @include flex();
+        flex: 1;
+
+		&__left {
+            @include flex();
+            justify-content: center;
+            align-items: center;
+            flex: 1;
+        }
+        &__gap {
+            @include flex();
+            flex-direction: column;
+        }
+        &__right {
+            @include flex();
+            flex-direction: column;
+            flex: 1;
+        }
+
+        &__right-top {
+            @include flex();
+            flex: 1;
+            justify-content: center;
+            align-items: center;
+        }
+
+        &__right-bottom {
+            @include flex();
+            flex: 1;
+            justify-content: center;
+            align-items: center;
+        }
+	}
+</style>

+ 43 - 0
uni_modules/uview-plus/components/u-button/button.js

@@ -0,0 +1,43 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:51:27
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/button.js
+ */
+export default {
+    // button组件
+    button: {
+        hairline: false,
+        type: 'info',
+        size: 'normal',
+        shape: 'square',
+        plain: false,
+        disabled: false,
+        loading: false,
+        loadingText: '',
+        loadingMode: 'spinner',
+        loadingSize: 15,
+        openType: '',
+        formType: '',
+        appParameter: '',
+        hoverStopPropagation: true,
+        lang: 'en',
+        sessionFrom: '',
+        sendMessageTitle: '',
+        sendMessagePath: '',
+        sendMessageImg: '',
+        showMessageCard: false,
+        dataName: '',
+        throttleTime: 0,
+        hoverStartTime: 0,
+        hoverStayTime: 200,
+        text: '',
+        icon: '',
+        iconColor: '',
+        color: '',
+        stop: true,
+    }
+}

+ 46 - 0
uni_modules/uview-plus/components/u-button/nvue.scss

@@ -0,0 +1,46 @@
+$u-button-active-opacity:0.75 !default;
+$u-button-loading-text-margin-left:4px !default;
+$u-button-text-color: #FFFFFF !default;
+$u-button-text-plain-error-color:$u-error !default;
+$u-button-text-plain-warning-color:$u-warning !default;
+$u-button-text-plain-success-color:$u-success !default;
+$u-button-text-plain-info-color:$u-info !default;
+$u-button-text-plain-primary-color:$u-primary !default;
+.u-button {
+	&--active {
+		opacity: $u-button-active-opacity;
+	}
+	
+	&--active--plain {
+		background-color: rgb(217, 217, 217);
+	}
+	
+	&__loading-text {
+		margin-left:$u-button-loading-text-margin-left;
+	}
+	
+	&__text,
+	&__loading-text {
+		color:$u-button-text-color;
+	}
+	
+	&__text--plain--error {
+		color:$u-button-text-plain-error-color;
+	}
+	
+	&__text--plain--warning {
+		color:$u-button-text-plain-warning-color;
+	}
+	
+	&__text--plain--success{
+		color:$u-button-text-plain-success-color;
+	}
+	
+	&__text--plain--info {
+		color:$u-button-text-plain-info-color;
+	}
+	
+	&__text--plain--primary {
+		color:$u-button-text-plain-primary-color;
+	}
+}

+ 159 - 0
uni_modules/uview-plus/components/u-button/props.js

@@ -0,0 +1,159 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 是否细边框
+        hairline: {
+            type: Boolean,
+            default: () => defProps.button.hairline
+        },
+        // 按钮的预置样式,info,primary,error,warning,success
+        type: {
+            type: String,
+            default: () => defProps.button.type
+        },
+        // 按钮尺寸,large,normal,small,mini
+        size: {
+            type: String,
+            default: () => defProps.button.size
+        },
+        // 按钮形状,circle(两边为半圆),square(带圆角)
+        shape: {
+            type: String,
+            default: () => defProps.button.shape
+        },
+        // 按钮是否镂空
+        plain: {
+            type: Boolean,
+            default: () => defProps.button.plain
+        },
+        // 是否禁止状态
+        disabled: {
+            type: Boolean,
+            default: () => defProps.button.disabled
+        },
+        // 是否加载中
+        loading: {
+            type: Boolean,
+            default: () => defProps.button.loading
+        },
+        // 加载中提示文字
+        loadingText: {
+            type: [String, Number],
+            default: () => defProps.button.loadingText
+        },
+        // 加载状态图标类型
+        loadingMode: {
+            type: String,
+            default: () => defProps.button.loadingMode
+        },
+        // 加载图标大小
+        loadingSize: {
+            type: [String, Number],
+            default: () => defProps.button.loadingSize
+        },
+        // 开放能力,具体请看uniapp稳定关于button组件部分说明
+        // https://uniapp.dcloud.io/component/button
+        openType: {
+            type: String,
+            default: () => defProps.button.openType
+        },
+        // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+        // 取值为submit(提交表单),reset(重置表单)
+        formType: {
+            type: String,
+            default: () => defProps.button.formType
+        },
+        // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
+        // 只微信小程序、QQ小程序有效
+        appParameter: {
+            type: String,
+            default: () => defProps.button.appParameter
+        },
+        // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
+        hoverStopPropagation: {
+            type: Boolean,
+            default: () => defProps.button.hoverStopPropagation
+        },
+        // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
+        lang: {
+            type: String,
+            default: () => defProps.button.lang
+        },
+        // 会话来源,open-type="contact"时有效。只微信小程序有效
+        sessionFrom: {
+            type: String,
+            default: () => defProps.button.sessionFrom
+        },
+        // 会话内消息卡片标题,open-type="contact"时有效
+        // 默认当前标题,只微信小程序有效
+        sendMessageTitle: {
+            type: String,
+            default: () => defProps.button.sendMessageTitle
+        },
+        // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
+        // 默认当前分享路径,只微信小程序有效
+        sendMessagePath: {
+            type: String,
+            default: () => defProps.button.sendMessagePath
+        },
+        // 会话内消息卡片图片,open-type="contact"时有效
+        // 默认当前页面截图,只微信小程序有效
+        sendMessageImg: {
+            type: String,
+            default: () => defProps.button.sendMessageImg
+        },
+        // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
+        // 用户点击后可以快速发送小程序消息,open-type="contact"时有效
+        showMessageCard: {
+            type: Boolean,
+            default: () => defProps.button.showMessageCard
+        },
+        // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+        dataName: {
+            type: String,
+            default: () => defProps.button.dataName
+        },
+        // 节流,一定时间内只能触发一次
+        throttleTime: {
+            type: [String, Number],
+            default: () => defProps.button.throttleTime
+        },
+        // 按住后多久出现点击态,单位毫秒
+        hoverStartTime: {
+            type: [String, Number],
+            default: () => defProps.button.hoverStartTime
+        },
+        // 手指松开后点击态保留时间,单位毫秒
+        hoverStayTime: {
+            type: [String, Number],
+            default: () => defProps.button.hoverStayTime
+        },
+        // 按钮文字,之所以通过props传入,是因为slot传入的话
+        // nvue中无法控制文字的样式
+        text: {
+            type: [String, Number],
+            default: () => defProps.button.text
+        },
+        // 按钮图标
+        icon: {
+            type: String,
+            default: () => defProps.button.icon
+        },
+        // 按钮图标
+        iconColor: {
+            type: String,
+            default: () => defProps.button.icon
+        },
+        // 按钮颜色,支持传入linear-gradient渐变色
+        color: {
+            type: String,
+            default: () => defProps.button.color
+        },
+        // 停止冒泡
+        stop: {
+            type: Boolean,
+            default: () => defProps.button.stop
+        },
+    }
+})

+ 503 - 0
uni_modules/uview-plus/components/u-button/u-button.vue

@@ -0,0 +1,503 @@
+<template>
+    <!-- #ifndef APP-NVUE -->
+    <button
+        :hover-start-time="Number(hoverStartTime)"
+        :hover-stay-time="Number(hoverStayTime)"
+        :form-type="formType"
+        :open-type="openType"
+        :app-parameter="appParameter"
+        :hover-stop-propagation="hoverStopPropagation"
+        :send-message-title="sendMessageTitle"
+        :send-message-path="sendMessagePath"
+        :lang="lang"
+        :data-name="dataName"
+        :session-from="sessionFrom"
+        :send-message-img="sendMessageImg"
+        :show-message-card="showMessageCard"
+        @getphonenumber="getphonenumber"
+        @getuserinfo="getuserinfo"
+        @error="error"
+        @opensetting="opensetting"
+        @launchapp="launchapp"
+        @agreeprivacyauthorization="agreeprivacyauthorization"
+        :hover-class="!disabled && !loading ? 'u-button--active' : ''"
+        class="u-button u-reset-button"
+        :style="[baseColor, addStyle(customStyle)]"
+        @tap="clickHandler"
+        :class="bemClass"
+    >
+        <template v-if="loading">
+            <u-loading-icon
+                :mode="loadingMode"
+                :size="loadingSize * 1.15"
+                :color="loadingColor"
+            ></u-loading-icon>
+            <text
+                class="u-button__loading-text"
+                :style="[{ fontSize: textSize + 'px' }]"
+                >{{ loadingText || text }}</text
+            >
+        </template>
+        <template v-else>
+            <u-icon
+                v-if="icon"
+                :name="icon"
+                :color="iconColorCom"
+                :size="textSize * 1.35"
+                :customStyle="{ marginRight: '2px' }"
+            ></u-icon>
+            <slot>
+                <text
+                    class="u-button__text"
+                    :style="[{ fontSize: textSize + 'px' }]"
+                    >{{ text }}</text
+                >
+            </slot>
+        </template>
+    </button>
+    <!-- #endif -->
+
+    <!-- #ifdef APP-NVUE -->
+    <view
+        :hover-start-time="Number(hoverStartTime)"
+        :hover-stay-time="Number(hoverStayTime)"
+        class="u-button"
+        :hover-class="
+            !disabled && !loading && !color && (plain || type === 'info')
+                ? 'u-button--active--plain'
+                : !disabled && !loading && !plain
+                ? 'u-button--active'
+                : ''
+        "
+        @tap="clickHandler"
+        :class="bemClass"
+        :style="[baseColor, addStyle(customStyle)]"
+    >
+        <template v-if="loading">
+            <u-loading-icon
+                :mode="loadingMode"
+                :size="loadingSize * 1.15"
+                :color="loadingColor"
+            ></u-loading-icon>
+            <text
+                class="u-button__loading-text"
+                :style="[nvueTextStyle]"
+                :class="[plain && `u-button__text--plain--${type}`]"
+                >{{ loadingText || text }}</text
+            >
+        </template>
+        <template v-else>
+            <u-icon
+                v-if="icon"
+                :name="icon"
+                :color="iconColorCom"
+                :size="textSize * 1.35"
+            ></u-icon>
+            <text
+                class="u-button__text"
+                :style="[
+                    {
+                        marginLeft: icon ? '2px' : 0,
+                    },
+                    nvueTextStyle,
+                ]"
+                :class="[plain && `u-button__text--plain--${type}`]"
+                >{{ text }}</text
+            >
+        </template>
+    </view>
+    <!-- #endif -->
+</template>
+
+<script lang="ts">
+import { buttonMixin } from "../../libs/mixin/button";
+import { openType } from "../../libs/mixin/openType";
+import { mpMixin } from '../../libs/mixin/mpMixin';
+import { mixin } from '../../libs/mixin/mixin';
+import { props } from "./props";
+import { addStyle } from '../../libs/function/index';
+import { throttle } from '../../libs/function/throttle';
+import color from '../../libs/config/color';
+/**
+ * button 按钮
+ * @description Button 按钮
+ * @tutorial https://ijry.github.io/uview-plus/components/button.html
+ *
+ * @property {Boolean}			hairline				是否显示按钮的细边框 (默认 true )
+ * @property {String}			type					按钮的预置样式,info,primary,error,warning,success (默认 'info' )
+ * @property {String}			size					按钮尺寸,large,normal,mini (默认 normal)
+ * @property {String}			shape					按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' )
+ * @property {Boolean}			plain					按钮是否镂空,背景色透明 (默认 false)
+ * @property {Boolean}			disabled				是否禁用 (默认 false)
+ * @property {Boolean}			loading					按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false)
+ * @property {String | Number}	loadingText				加载中提示文字
+ * @property {String}			loadingMode				加载状态图标类型 (默认 'spinner' )
+ * @property {String | Number}	loadingSize				加载图标大小 (默认 15 )
+ * @property {String}			openType				开放能力,具体请看uniapp稳定关于button组件部分说明
+ * @property {String}			formType				用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+ * @property {String}			appParameter			打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效)
+ * @property {Boolean}			hoverStopPropagation	指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true )
+ * @property {String}			lang					指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en )
+ * @property {String}			sessionFrom				会话来源,openType="contact"时有效
+ * @property {String}			sendMessageTitle		会话内消息卡片标题,openType="contact"时有效
+ * @property {String}			sendMessagePath			会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+ * @property {String}			sendMessageImg			会话内消息卡片图片,openType="contact"时有效
+ * @property {Boolean}			showMessageCard			是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false)
+ * @property {String}			dataName				额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+ * @property {String | Number}	throttleTime			节流,一定时间内只能触发一次 (默认 0 )
+ * @property {String | Number}	hoverStartTime			按住后多久出现点击态,单位毫秒 (默认 0 )
+ * @property {String | Number}	hoverStayTime			手指松开后点击态保留时间,单位毫秒 (默认 200 )
+ * @property {String | Number}	text					按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式)
+ * @property {String}			icon					按钮图标
+ * @property {String}			iconColor				按钮图标颜色
+ * @property {String}			color					按钮颜色,支持传入linear-gradient渐变色
+ * @property {Object}			customStyle				定义需要用到的外部样式
+ *
+ * @event {Function}	click			非禁止并且非加载中,才能点击
+ * @event {Function}	getphonenumber	open-type="getPhoneNumber"时有效
+ * @event {Function}	getuserinfo		用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
+ * @event {Function}	error			当使用开放能力时,发生错误的回调
+ * @event {Function}	opensetting		在打开授权设置页并关闭后回调
+ * @event {Function}	launchapp		打开 APP 成功的回调
+ * @event {Function}	agreeprivacyauthorization	用户同意隐私协议事件回调
+ * @example <u-button>月落</u-button>
+ */
+export default {
+    name: "u-button",
+    // #ifdef MP
+    mixins: [mpMixin, mixin, buttonMixin, openType, props],
+    // #endif
+    // #ifndef MP
+    mixins: [mpMixin, mixin, props],
+    // #endif
+    data() {
+        return {};
+    },
+    computed: {
+        // 生成bem风格的类名
+        bemClass() {
+            // this.bem为一个computed变量,在mixin中
+            if (!this.color) {
+                return this.bem(
+                    "button",
+                    ["type", "shape", "size"],
+                    ["disabled", "plain", "hairline"]
+                );
+            } else {
+                // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式
+                return this.bem(
+                    "button",
+                    ["shape", "size"],
+                    ["disabled", "plain", "hairline"]
+                );
+            }
+        },
+        loadingColor() {
+            if (this.plain) {
+                // 如果有设置color值,则用color值,否则使用type主题颜色
+                return this.color
+                    ? this.color
+                    : color[`u-${this.type}`];
+            }
+            if (this.type === "info") {
+                return "#c9c9c9";
+            }
+            return "rgb(200, 200, 200)";
+        },
+        iconColorCom() {
+            // 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
+            // u-icon的color能接受一个主题颜色的值
+			if (this.iconColor) return this.iconColor;
+			if (this.plain) {
+                return this.color ? this.color : this.type;
+            } else {
+                return this.type === "info" ? "#000000" : "#ffffff";
+            }
+        },
+        baseColor() {
+            let style = {};
+            if (this.color) {
+                // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
+                style.color = this.plain ? this.color : "white";
+                if (!this.plain) {
+                    // 非镂空,背景色使用自定义的颜色
+                    style["background-color"] = this.color;
+                }
+                if (this.color.indexOf("gradient") !== -1) {
+                    // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色
+                    // weex文档说明可以写borderWidth的形式,为什么这里需要分开写?
+                    // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效
+                    style.borderTopWidth = 0;
+                    style.borderRightWidth = 0;
+                    style.borderBottomWidth = 0;
+                    style.borderLeftWidth = 0;
+                    if (!this.plain) {
+                        style.backgroundImage = this.color;
+                    }
+                } else {
+                    // 非渐变色,则设置边框相关的属性
+                    style.borderColor = this.color;
+                    style.borderWidth = "1px";
+                    style.borderStyle = "solid";
+                }
+            }
+            return style;
+        },
+        // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
+        nvueTextStyle() {
+            let style = {};
+            // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
+            if (this.type === "info") {
+                style.color = "#323233";
+            }
+            if (this.color) {
+                style.color = this.plain ? this.color : "white";
+            }
+            style.fontSize = this.textSize + "px";
+            return style;
+        },
+        // 字体大小
+        textSize() {
+            let fontSize = 14,
+                { size } = this;
+            if (size === "large") fontSize = 16;
+            if (size === "normal") fontSize = 14;
+            if (size === "small") fontSize = 12;
+            if (size === "mini") fontSize = 10;
+            return fontSize;
+        },
+    },
+	emits: ['click', 'getphonenumber', 'getuserinfo',
+		'error', 'opensetting', 'launchapp', 'agreeprivacyauthorization'],
+    methods: {
+        addStyle,
+        clickHandler(e: any) {
+            // 非禁止并且非加载中,才能点击
+            if (!this.disabled && !this.loading) {
+				// 进行节流控制,每this.throttle毫秒内,只在开始处执行
+				throttle(() => {
+					this.$emit("click", e);
+				}, this.throttleTime);
+            }
+            // 是否阻止事件传播
+            this.stop && this.preventEvent(e)
+        },
+        // 下面为对接uniapp官方按钮开放能力事件回调的对接
+        getphonenumber(res: any) {
+            this.$emit("getphonenumber", res);
+        },
+        getuserinfo(res: any) {
+            this.$emit("getuserinfo", res);
+        },
+        error(res: any) {
+            this.$emit("error", res);
+        },
+        opensetting(res: any) {
+            this.$emit("opensetting", res);
+        },
+        launchapp(res: any) {
+            this.$emit("launchapp", res);
+        },
+        agreeprivacyauthorization(res) {
+            this.$emit("agreeprivacyauthorization", res);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+/* #ifndef APP-NVUE */
+@import "./vue.scss";
+/* #endif */
+
+/* #ifdef APP-NVUE */
+@import "./nvue.scss";
+/* #endif */
+
+$u-button-u-button-height: 40px !default;
+$u-button-text-font-size: 15px !default;
+$u-button-loading-text-font-size: 15px !default;
+$u-button-loading-text-margin-left: 4px !default;
+$u-button-large-width: 100% !default;
+$u-button-large-height: 50px !default;
+$u-button-normal-padding: 0 12px !default;
+$u-button-large-padding: 0 15px !default;
+$u-button-normal-font-size: 14px !default;
+$u-button-small-min-width: 60px !default;
+$u-button-small-height: 30px !default;
+$u-button-small-padding: 0px 8px !default;
+$u-button-mini-padding: 0px 8px !default;
+$u-button-small-font-size: 12px !default;
+$u-button-mini-height: 22px !default;
+$u-button-mini-font-size: 10px !default;
+$u-button-mini-min-width: 50px !default;
+$u-button-disabled-opacity: 0.5 !default;
+$u-button-info-color: #323233 !default;
+$u-button-info-background-color: #fff !default;
+$u-button-info-border-color: #ebedf0 !default;
+$u-button-info-border-width: 1px !default;
+$u-button-info-border-style: solid !default;
+$u-button-success-color: #fff !default;
+$u-button-success-background-color: $u-success !default;
+$u-button-success-border-color: $u-button-success-background-color !default;
+$u-button-success-border-width: 1px !default;
+$u-button-success-border-style: solid !default;
+$u-button-primary-color: #fff !default;
+$u-button-primary-background-color: $u-primary !default;
+$u-button-primary-border-color: $u-button-primary-background-color !default;
+$u-button-primary-border-width: 1px !default;
+$u-button-primary-border-style: solid !default;
+$u-button-error-color: #fff !default;
+$u-button-error-background-color: $u-error !default;
+$u-button-error-border-color: $u-button-error-background-color !default;
+$u-button-error-border-width: 1px !default;
+$u-button-error-border-style: solid !default;
+$u-button-warning-color: #fff !default;
+$u-button-warning-background-color: $u-warning !default;
+$u-button-warning-border-color: $u-button-warning-background-color !default;
+$u-button-warning-border-width: 1px !default;
+$u-button-warning-border-style: solid !default;
+$u-button-block-width: 100% !default;
+$u-button-circle-border-top-right-radius: 100px !default;
+$u-button-circle-border-top-left-radius: 100px !default;
+$u-button-circle-border-bottom-left-radius: 100px !default;
+$u-button-circle-border-bottom-right-radius: 100px !default;
+$u-button-square-border-top-right-radius: 3px !default;
+$u-button-square-border-top-left-radius: 3px !default;
+$u-button-square-border-bottom-left-radius: 3px !default;
+$u-button-square-border-bottom-right-radius: 3px !default;
+$u-button-icon-min-width: 1em !default;
+$u-button-plain-background-color: #fff !default;
+$u-button-hairline-border-width: 0.5px !default;
+
+.u-button {
+    height: $u-button-u-button-height;
+    position: relative;
+    align-items: center;
+    justify-content: center;
+    @include flex;
+    /* #ifndef APP-NVUE */
+    box-sizing: border-box;
+    /* #endif */
+    flex-direction: row;
+
+    &__text {
+        font-size: $u-button-text-font-size;
+    }
+
+    &__loading-text {
+        font-size: $u-button-loading-text-font-size;
+        margin-left: $u-button-loading-text-margin-left;
+    }
+
+    &--large {
+        /* #ifndef APP-NVUE */
+        width: $u-button-large-width;
+        /* #endif */
+        height: $u-button-large-height;
+        padding: $u-button-large-padding;
+    }
+
+    &--normal {
+        padding: $u-button-normal-padding;
+        font-size: $u-button-normal-font-size;
+    }
+
+    &--small {
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-small-min-width;
+        /* #endif */
+        height: $u-button-small-height;
+        padding: $u-button-small-padding;
+        font-size: $u-button-small-font-size;
+    }
+
+    &--mini {
+        height: $u-button-mini-height;
+        font-size: $u-button-mini-font-size;
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-mini-min-width;
+        /* #endif */
+        padding: $u-button-mini-padding;
+    }
+
+    &--disabled {
+        opacity: $u-button-disabled-opacity;
+    }
+
+    &--info {
+        color: $u-button-info-color;
+        background-color: $u-button-info-background-color;
+        border-color: $u-button-info-border-color;
+        border-width: $u-button-info-border-width;
+        border-style: $u-button-info-border-style;
+    }
+
+    &--success {
+        color: $u-button-success-color;
+        background-color: $u-button-success-background-color;
+        border-color: $u-button-success-border-color;
+        border-width: $u-button-success-border-width;
+        border-style: $u-button-success-border-style;
+    }
+
+    &--primary {
+        color: $u-button-primary-color;
+        background-color: $u-button-primary-background-color;
+        border-color: $u-button-primary-border-color;
+        border-width: $u-button-primary-border-width;
+        border-style: $u-button-primary-border-style;
+    }
+
+    &--error {
+        color: $u-button-error-color;
+        background-color: $u-button-error-background-color;
+        border-color: $u-button-error-border-color;
+        border-width: $u-button-error-border-width;
+        border-style: $u-button-error-border-style;
+    }
+
+    &--warning {
+        color: $u-button-warning-color;
+        background-color: $u-button-warning-background-color;
+        border-color: $u-button-warning-border-color;
+        border-width: $u-button-warning-border-width;
+        border-style: $u-button-warning-border-style;
+    }
+
+    &--block {
+        @include flex;
+        width: $u-button-block-width;
+    }
+
+    &--circle {
+        border-top-right-radius: $u-button-circle-border-top-right-radius;
+        border-top-left-radius: $u-button-circle-border-top-left-radius;
+        border-bottom-left-radius: $u-button-circle-border-bottom-left-radius;
+        border-bottom-right-radius: $u-button-circle-border-bottom-right-radius;
+    }
+
+    &--square {
+        border-bottom-left-radius: $u-button-square-border-top-right-radius;
+        border-bottom-right-radius: $u-button-square-border-top-left-radius;
+        border-top-left-radius: $u-button-square-border-bottom-left-radius;
+        border-top-right-radius: $u-button-square-border-bottom-right-radius;
+    }
+
+    &__icon {
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-icon-min-width;
+        line-height: inherit !important;
+        vertical-align: top;
+        /* #endif */
+    }
+
+    &--plain {
+        background-color: $u-button-plain-background-color;
+    }
+
+    &--hairline {
+        border-width: $u-button-hairline-border-width !important;
+    }
+}
+</style>

+ 81 - 0
uni_modules/uview-plus/components/u-button/vue.scss

@@ -0,0 +1,81 @@
+// nvue下hover-class无效
+$u-button-before-top:50% !default;
+$u-button-before-left:50% !default;
+$u-button-before-width:100% !default;
+$u-button-before-height:100% !default;
+$u-button-before-transform:translate(-50%, -50%) !default;
+$u-button-before-opacity:0 !default;
+$u-button-before-background-color:#000 !default;
+$u-button-before-border-color:#000 !default;
+$u-button-active-before-opacity:.15 !default;
+$u-button-icon-margin-left:4px !default;
+$u-button-plain-u-button-info-color:$u-info;
+$u-button-plain-u-button-success-color:$u-success;
+$u-button-plain-u-button-error-color:$u-error;
+$u-button-plain-u-button-warning-color:$u-warning;
+
+.u-button {
+	width: 100%;
+	white-space: nowrap;
+	
+	&__text {
+		white-space: nowrap;
+		line-height: 1;
+	}
+	
+	&:before {
+		position: absolute;
+		top:$u-button-before-top;
+		left:$u-button-before-left;
+		width:$u-button-before-width;
+		height:$u-button-before-height;
+		border: inherit;
+		border-radius: inherit;
+		transform:$u-button-before-transform;
+		opacity:$u-button-before-opacity;
+		content: " ";
+		background-color:$u-button-before-background-color;
+		border-color:$u-button-before-border-color;
+	}
+	
+	&--active {
+		&:before {
+			opacity: .15
+		}
+	}
+	
+	&__icon+&__text:not(:empty),
+	&__loading-text {
+		margin-left:$u-button-icon-margin-left;
+	}
+	
+	&--plain {
+		&.u-button--primary {
+			color: $u-primary;
+		}
+	}
+	
+	&--plain {
+		&.u-button--info {
+			color:$u-button-plain-u-button-info-color;
+		}
+	}
+	
+	&--plain {
+		&.u-button--success {
+			color:$u-button-plain-u-button-success-color;
+		}
+	}
+	
+	&--plain {
+		&.u-button--error {
+			color:$u-button-plain-u-button-error-color;
+		}
+	}
+	
+	&--plain {
+		&.u-button--warning {
+			color:$u-button-plain-u-button-warning-color;
+		}
+	}
+}

+ 45 - 0
uni_modules/uview-plus/components/u-calendar/calendar.js

@@ -0,0 +1,45 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:52:43
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/calendar.js
+ */
+export default {
+    // calendar 组件
+    calendar: {
+        title: '日期选择',
+        showTitle: true,
+        showSubtitle: true,
+        mode: 'single',
+        startText: '开始',
+        endText: '结束',
+        customList: [],
+        color: '#3c9cff',
+        minDate: 0,
+        maxDate: 0,
+        defaultDate: null,
+        maxCount: Number.MAX_SAFE_INTEGER, // Infinity
+        rowHeight: 56,
+        formatter: null,
+        showLunar: false,
+        showMark: true,
+        confirmText: '确定',
+        confirmDisabledText: '确定',
+        show: false,
+        closeOnClickOverlay: false,
+        readonly: false,
+        showConfirm: true,
+        maxRange: Number.MAX_SAFE_INTEGER, // Infinity
+        rangePrompt: '',
+        showRangePrompt: true,
+        allowSameDay: false,
+		round: 0,
+		monthNum: 3,
+        weekText: ['一', '二', '三', '四', '五', '六', '日'],
+        forbidDays: [],
+        forbidDaysToast: '该日期已禁用',
+    }
+}

+ 109 - 0
uni_modules/uview-plus/components/u-calendar/header.vue

@@ -0,0 +1,109 @@
+<template>
+	<view class="u-calendar-header u-border-bottom">
+		<text
+			class="u-calendar-header__title"
+			v-if="showTitle"
+		>{{ title }}</text>
+		<text
+			class="u-calendar-header__subtitle"
+			v-if="showSubtitle"
+		>{{ subtitle }}</text>
+		<view class="u-calendar-header__weekdays">
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[0] }}</text>
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[1] }}</text>
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[2] }}</text>
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[3] }}</text>
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[4] }}</text>
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[5] }}</text>
+			<text class="u-calendar-header__weekdays__weekday">{{ weekText[6] }}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	export default {
+		name: 'u-calendar-header',
+		mixins: [mpMixin, mixin],
+		props: {
+			// 标题
+			title: {
+				type: String,
+				default: ''
+			},
+			// 副标题
+			subtitle: {
+				type: String,
+				default: ''
+			},
+			// 是否显示标题
+			showTitle: {
+				type: Boolean,
+				default: true
+			},
+			// 是否显示副标题
+			showSubtitle: {
+				type: Boolean,
+				default: true
+			},
+			// 星期文本
+			weekText: {
+				type: Array,
+				default: () => {
+					return ['一', '二', '三', '四', '五', '六', '日']
+				}
+			},
+		},
+		data() {
+			return {
+
+			}
+		},
+		methods: {
+			name() {
+
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-calendar-header {
+		display: flex;
+		flex-direction: column;
+		padding-bottom: 4px;
+
+		&__title {
+			font-size: 16px;
+			color: $u-main-color;
+			text-align: center;
+			height: 42px;
+			line-height: 42px;
+			font-weight: bold;
+		}
+
+		&__subtitle {
+			font-size: 14px;
+			color: $u-main-color;
+			height: 40px;
+			text-align: center;
+			line-height: 40px;
+			font-weight: bold;
+		}
+
+		&__weekdays {
+			@include flex;
+			justify-content: space-between;
+
+			&__weekday {
+				font-size: 13px;
+				color: $u-main-color;
+				line-height: 30px;
+				flex: 1;
+				text-align: center;
+			}
+		}
+	}
+</style>

+ 608 - 0
uni_modules/uview-plus/components/u-calendar/month.vue

@@ -0,0 +1,608 @@
+<template>
+	<view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
+		<view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]"
+			:ref="`u-calendar-month-${index}`" :id="`month-${index}`">
+			<text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text>
+			<view class="u-calendar-month__days">
+				<view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper">
+					<text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
+				</view>
+				<view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1"
+					:style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)"
+					:class="[item1.selected && 'u-calendar-month__days__day__select--selected']">
+					<view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
+						<text class="u-calendar-month__days__day__select__info"
+							:class="[(item1.disabled || isForbid(item1) ) ? 'u-calendar-month__days__day__select__info--disabled' : '']"
+							:style="[textStyle(item1)]">{{ item1.day }}</text>
+						<text v-if="getBottomInfo(index, index1, item1)"
+							class="u-calendar-month__days__day__select__buttom-info"
+							:class="[(item1.disabled || isForbid(item1) ) ? 'u-calendar-month__days__day__select__buttom-info--disabled' : '']"
+							:style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
+						<text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// #ifdef APP-NVUE
+	// 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度
+	const dom = uni.requireNativePlugin('dom')
+	// #endif
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit, deepClone, toast, sleep } from '../../libs/function/index';
+	import { colorGradient } from '../../libs/function/colorGradient';
+	import test from '../../libs/function/test';
+	import defProps from '../../libs/config/props';
+	import dayjs from 'dayjs/esm/index'
+	export default {
+		name: 'u-calendar-month',
+		mixins: [mpMixin, mixin],
+		props: {
+			// 是否显示月份背景色
+			showMark: {
+				type: Boolean,
+				default: true
+			},
+			// 主题色,对底部按钮和选中日期有效
+			color: {
+				type: String,
+				default: '#3c9cff'
+			},
+			// 月份数据
+			months: {
+				type: Array,
+				default: () => []
+			},
+			// 日期选择类型
+			mode: {
+				type: String,
+				default: 'single'
+			},
+			// 日期行高
+			rowHeight: {
+				type: [String, Number],
+				default: 58
+			},
+			// mode=multiple时,最多可选多少个日期
+			maxCount: {
+				type: [String, Number],
+				default: Infinity
+			},
+			// mode=range时,第一个日期底部的提示文字
+			startText: {
+				type: String,
+				default: '开始'
+			},
+			// mode=range时,最后一个日期底部的提示文字
+			endText: {
+				type: String,
+				default: '结束'
+			},
+			// 默认选中的日期,mode为multiple或range是必须为数组格式
+			defaultDate: {
+				type: [Array, String, Date],
+				default: null
+			},
+			// 最小的可选日期
+			minDate: {
+				type: [String, Number],
+				default: 0
+			},
+			// 最大可选日期
+			maxDate: {
+				type: [String, Number],
+				default: 0
+			},
+			// 如果没有设置maxDate,则往后推多少个月
+			maxMonth: {
+				type: [String, Number],
+				default: 2
+			},
+			// 是否为只读状态,只读状态下禁止选择日期
+			readonly: {
+				type: Boolean,
+				default: () => defProps.calendar.readonly
+			},
+			// 日期区间最多可选天数,默认无限制,mode = range时有效
+			maxRange: {
+				type: [Number, String],
+				default: Infinity
+			},
+			// 范围选择超过最多可选天数时的提示文案,mode = range时有效
+			rangePrompt: {
+				type: String,
+				default: ''
+			},
+			// 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
+			showRangePrompt: {
+				type: Boolean,
+				default: true
+			},
+			// 是否允许日期范围的起止时间为同一天,mode = range时有效
+			allowSameDay: {
+				type: Boolean,
+				default: false
+			},
+			forbidDays: {
+				type: Array,
+				default: () => []
+			},
+			forbidDaysToast: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				// 每个日期的宽度
+				width: 0,
+				// 当前选中的日期item
+				item: {},
+				selected: []
+			}
+		},
+		watch: {
+			selectedChange: {
+				immediate: true,
+				handler(n) {
+					this.setDefaultDate()
+				}
+			}
+		},
+		computed: {
+			// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
+			selectedChange() {
+				return [this.minDate, this.maxDate, this.defaultDate]
+			},
+			dayStyle(index1, index2, item) {
+				return (index1, index2, item) => {
+					const style = {}
+					let week = item.week
+					// 不进行四舍五入的形式保留2位小数
+					const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
+					// 得出每个日期的宽度
+					// #ifdef APP-NVUE
+					style.width = addUnit(dayWidth, 'px')
+					// #endif
+					style.height = addUnit(this.rowHeight)
+					if (index2 === 0) {
+						// 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
+						week = (week === 0 ? 7 : week) - 1
+						style.marginLeft = addUnit(week * dayWidth, 'px')
+					}
+					if (this.mode === 'range') {
+						// 之所以需要这么写,是因为DCloud公司的iOS客户端导致的bug
+						style.paddingLeft = 0
+						style.paddingRight = 0
+						style.paddingBottom = 0
+						style.paddingTop = 0
+					}
+					return style
+				}
+			},
+			daySelectStyle() {
+				return (index1, index2, item) => {
+					let date = dayjs(item.date).format("YYYY-MM-DD"),
+						style = {}
+					// 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断
+					if (this.selected.some(item => this.dateSame(item, date))) {
+						style.backgroundColor = this.color
+					}
+					if (this.mode === 'single') {
+						if (date === this.selected[0]) {
+							// 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等
+							style.borderTopLeftRadius = '3px'
+							style.borderBottomLeftRadius = '3px'
+							style.borderTopRightRadius = '3px'
+							style.borderBottomRightRadius = '3px'
+						}
+					} else if (this.mode === 'range') {
+						if (this.selected.length >= 2) {
+							const len = this.selected.length - 1
+							// 第一个日期设置左上角和左下角的圆角
+							if (this.dateSame(date, this.selected[0])) {
+								style.borderTopLeftRadius = '3px'
+								style.borderBottomLeftRadius = '3px'
+							}
+							// 最后一个日期设置右上角和右下角的圆角
+							if (this.dateSame(date, this.selected[len])) {
+								style.borderTopRightRadius = '3px'
+								style.borderBottomRightRadius = '3px'
+							}
+							// 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值
+							if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
+									.selected[len]))) {
+								style.backgroundColor = colorGradient(this.color, '#ffffff', 100)[90]
+								// 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符
+								style.opacity = 0.7
+							}
+						} else if (this.selected.length === 1) {
+							// 之所以需要这么写,是因为uni-app的iOS客户端的bug
+							// 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现
+							style.borderTopLeftRadius = '3px'
+							style.borderBottomLeftRadius = '3px'
+						}
+					} else {
+						if (this.selected.some(item => this.dateSame(item, date))) {
+							style.borderTopLeftRadius = '3px'
+							style.borderBottomLeftRadius = '3px'
+							style.borderTopRightRadius = '3px'
+							style.borderBottomRightRadius = '3px'
+						}
+					}
+					return style
+				}
+			},
+			// 某个日期是否被选中
+			textStyle() {
+				return (item) => {
+					const date = dayjs(item.date).format("YYYY-MM-DD"),
+						style = {}
+					// 选中的日期,提示文字设置白色
+					if (this.selected.some(item => this.dateSame(item, date))) {
+						style.color = '#ffffff'
+					}
+					if (this.mode === 'range') {
+						const len = this.selected.length - 1
+						// 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色
+						if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
+								.selected[len]))) {
+							style.color = this.color
+						}
+					}
+					return style
+				}
+			},
+			// 获取底部的提示文字
+			getBottomInfo() {
+				return (index1, index2, item) => {
+					const date = dayjs(item.date).format("YYYY-MM-DD")
+					const bottomInfo = item.bottomInfo
+					// 当为日期范围模式时,且选择的日期个数大于0时
+					if (this.mode === 'range' && this.selected.length > 0) {
+						if (this.selected.length === 1) {
+							// 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始”
+							if (this.dateSame(date, this.selected[0])) return this.startText
+							else return bottomInfo
+						} else {
+							const len = this.selected.length - 1
+							// 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期
+							if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) &&
+								len === 1) {
+								// 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中
+								return `${this.startText}/${this.endText}`
+							} else if (this.dateSame(date, this.selected[0])) {
+								return this.startText
+							} else if (this.dateSame(date, this.selected[len])) {
+								return this.endText
+							} else {
+								return bottomInfo
+							}
+						}
+					} else {
+						return bottomInfo
+					}
+				}
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		emits: ['monthSelected', 'updateMonthTop'],
+		methods: {
+			init() {
+				// 初始化默认选中
+				this.$emit('monthSelected', this.selected)
+				this.$nextTick(() => {
+					// 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度
+					// 因为nvue下,$nextTick并不是100%可靠的
+					sleep(10).then(() => {
+						this.getWrapperWidth()
+						this.getMonthRect()
+					})
+				})
+			},
+			isForbid(item) {
+				let date = dayjs(item.date).format("YYYY-MM-DD")
+				if (this.mode !== 'range' && this.forbidDays.includes(date)) {
+					return true
+				}
+				return false
+			},
+			// 判断两个日期是否相等
+			dateSame(date1, date2) {
+				return dayjs(date1).isSame(dayjs(date2))
+			},
+			// 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度
+			getWrapperWidth() {
+				// #ifdef APP-NVUE
+				dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => {
+					this.width = res.size.width
+				})
+				// #endif
+				// #ifndef APP-NVUE
+				this.$uGetRect('.u-calendar-month-wrapper').then(size => {
+					this.width = size.width
+				})
+				// #endif
+			},
+			getMonthRect() {
+				// 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份
+				const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise(
+					`u-calendar-month-${index}`))
+				// 一次性返回
+				Promise.all(promiseAllArr).then(
+					sizes => {
+						let height = 1
+						const topArr = []
+						for (let i = 0; i < this.months.length; i++) {
+							// 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份
+							topArr[i] = height
+							height += sizes[i].height
+						}
+						// 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出
+						this.$emit('updateMonthTop', topArr)
+					})
+			},
+			// 获取每个月份区域的尺寸
+			getMonthRectByPromise(el) {
+				// #ifndef APP-NVUE
+				// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://ijry.github.io/uview-plus/js/getRect.html
+				// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
+				return new Promise(resolve => {
+					this.$uGetRect(`.${el}`).then(size => {
+						resolve(size)
+					})
+				})
+				// #endif
+
+				// #ifdef APP-NVUE
+				// nvue下,使用dom模块查询元素高度
+				// 返回一个promise,让调用此方法的主体能使用then回调
+				return new Promise(resolve => {
+					dom.getComponentRect(this.$refs[el][0], res => {
+						resolve(res.size)
+					})
+				})
+				// #endif
+			},
+			// 点击某一个日期
+			clickHandler(index1, index2, item) {
+				if (this.readonly) {
+					return;
+				}
+				this.item = item
+				const date = dayjs(item.date).format("YYYY-MM-DD")
+				if (item.disabled) return
+				if (this.isForbid(item)) {
+					uni.showToast({
+						title: this.forbidDaysToast
+					})
+					return
+				}
+				// 对上一次选择的日期数组进行深度克隆
+				let selected = deepClone(this.selected)
+				if (this.mode === 'single') {
+					// 单选情况下,让数组中的元素为当前点击的日期
+					selected = [date]
+				} else if (this.mode === 'multiple') {
+					if (selected.some(item => this.dateSame(item, date))) {
+						// 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果
+						const itemIndex = selected.findIndex(item => item === date)
+						selected.splice(itemIndex, 1)
+					} else {
+						// 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去
+						if (selected.length < this.maxCount) selected.push(date)
+					}
+				} else {
+					// 选择区间形式
+					if (selected.length === 0 || selected.length >= 2) {
+						// 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期
+						selected = [date]
+					} else if (selected.length === 1) {
+						// 如果已经选择了开始日期
+						const existsDate = selected[0]
+						// 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期
+						if (dayjs(date).isBefore(existsDate)) {
+							selected = [date]
+						} else if (dayjs(date).isAfter(existsDate)) {
+							// 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示
+							if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) {
+								if(this.rangePrompt) {
+									toast(this.rangePrompt)
+								} else {
+									toast(`选择天数不能超过 ${this.maxRange} 天`)
+								}
+								return
+							}
+							// 如果当前日期大于已有日期,将当前的添加到数组尾部
+							selected.push(date)
+							const startDate = selected[0]
+							const endDate = selected[1]
+							const arr = []
+							let i = 0
+							do {
+								// 将开始和结束日期之间的日期添加到数组中
+								arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD"))
+								i++
+								// 累加的日期小于结束日期时,继续下一次的循环
+							} while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate)))
+							// 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来
+							arr.push(endDate)
+							selected = arr
+						} else {
+							// 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己
+							if (selected[0] === date && !this.allowSameDay) return
+							selected.push(date)
+						}
+					}
+				}
+				this.setSelected(selected)
+			},
+			// 设置默认日期
+			setDefaultDate() {
+				if (!this.defaultDate) {
+					// 如果没有设置默认日期,则将当天日期设置为默认选中的日期
+					const selected = [dayjs().format("YYYY-MM-DD")]
+					return this.setSelected(selected, false)
+				}
+				let defaultDate = []
+				const minDate = this.minDate || dayjs().format("YYYY-MM-DD")
+				const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD")
+				if (this.mode === 'single') {
+					// 单选模式,可以是字符串或数组,Date对象等
+					if (!test.array(this.defaultDate)) {
+						defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
+					} else {
+						defaultDate = [this.defaultDate[0]]
+					}
+				} else {
+					// 如果为非数组,则不执行
+					if (!test.array(this.defaultDate)) return
+					defaultDate = this.defaultDate
+				}
+				// 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素
+				defaultDate = defaultDate.filter(item => {
+					return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs(
+						maxDate).add(1, 'day'))
+				})
+				this.setSelected(defaultDate, false)
+			},
+			setSelected(selected, event = true) {
+				this.selected = selected
+				event && this.$emit('monthSelected', this.selected,'tap')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-calendar-month-wrapper {
+		margin-top: 4px;
+	}
+
+	.u-calendar-month {
+
+		&__title {
+			display: flex;
+			flex-direction: column;
+			font-size: 14px;
+			line-height: 42px;
+			height: 42px;
+			color: $u-main-color;
+			text-align: center;
+			font-weight: bold;
+		}
+
+		&__days {
+			position: relative;
+			@include flex;
+			flex-wrap: wrap;
+
+			&__month-mark-wrapper {
+				position: absolute;
+				top: 0;
+				bottom: 0;
+				left: 0;
+				right: 0;
+				@include flex;
+				justify-content: center;
+				align-items: center;
+
+				&__text {
+					font-size: 155px;
+					color: rgba(231, 232, 234, 0.83);
+				}
+			}
+
+			&__day {
+				@include flex;
+				padding: 2px;
+				/* #ifndef APP-NVUE */
+				// vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移
+				width: calc(100% / 7);
+				box-sizing: border-box;
+				/* #endif */
+
+				&__select {
+					flex: 1;
+					@include flex;
+					align-items: center;
+					justify-content: center;
+					position: relative;
+
+					&__dot {
+						width: 7px;
+						height: 7px;
+						border-radius: 100px;
+						background-color: $u-error;
+						position: absolute;
+						top: 12px;
+						right: 7px;
+					}
+
+					&__buttom-info {
+						color: $u-content-color;
+						text-align: center;
+						position: absolute;
+						bottom: 5px;
+						font-size: 10px;
+						text-align: center;
+						left: 0;
+						right: 0;
+
+						&--selected {
+							color: #ffffff;
+						}
+
+						&--disabled {
+							color: #cacbcd;
+						}
+					}
+
+					&__info {
+						text-align: center;
+						font-size: 16px;
+
+						&--selected {
+							color: #ffffff;
+						}
+
+						&--disabled {
+							color: #cacbcd;
+						}
+					}
+
+					&--selected {
+						background-color: $u-primary;
+						@include flex;
+						justify-content: center;
+						align-items: center;
+						flex: 1;
+						border-radius: 3px;
+					}
+
+					&--range-selected {
+						opacity: 0.3;
+						border-radius: 0;
+					}
+
+					&--range-start-selected {
+						border-top-right-radius: 0;
+						border-bottom-right-radius: 0;
+					}
+
+					&--range-end-selected {
+						border-top-left-radius: 0;
+						border-bottom-left-radius: 0;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 160 - 0
uni_modules/uview-plus/components/u-calendar/props.js

@@ -0,0 +1,160 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+    props: {
+        // 日历顶部标题
+        title: {
+            type: String,
+            default: () => defProps.calendar.title
+        },
+        // 是否显示标题
+        showTitle: {
+            type: Boolean,
+            default: () => defProps.calendar.showTitle
+        },
+        // 是否显示副标题
+        showSubtitle: {
+            type: Boolean,
+            default: () => defProps.calendar.showSubtitle
+        },
+        // 日期类型选择,single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围
+        mode: {
+            type: String,
+            default: () => defProps.calendar.mode
+        },
+        // mode=range时,第一个日期底部的提示文字
+        startText: {
+            type: String,
+            default: () => defProps.calendar.startText
+        },
+        // mode=range时,最后一个日期底部的提示文字
+        endText: {
+            type: String,
+            default: () => defProps.calendar.endText
+        },
+        // 自定义列表
+        customList: {
+            type: Array,
+            default: () => defProps.calendar.customList
+        },
+        // 主题色,对底部按钮和选中日期有效
+        color: {
+            type: String,
+            default: () => defProps.calendar.color
+        },
+        // 最小的可选日期
+        minDate: {
+            type: [String, Number],
+            default: () => defProps.calendar.minDate
+        },
+        // 最大可选日期
+        maxDate: {
+            type: [String, Number],
+            default: () => defProps.calendar.maxDate
+        },
+        // 默认选中的日期,mode为multiple或range是必须为数组格式
+        defaultDate: {
+            type: [Array, String, Date, null],
+            default: () => defProps.calendar.defaultDate
+        },
+        // mode=multiple时,最多可选多少个日期
+        maxCount: {
+            type: [String, Number],
+            default: () => defProps.calendar.maxCount
+        },
+        // 日期行高
+        rowHeight: {
+            type: [String, Number],
+            default: () => defProps.calendar.rowHeight
+        },
+        // 日期格式化函数
+        formatter: {
+            type: [Function, null],
+            default: () => defProps.calendar.formatter
+        },
+        // 是否显示农历
+        showLunar: {
+            type: Boolean,
+            default: () => defProps.calendar.showLunar
+        },
+        // 是否显示月份背景色
+        showMark: {
+            type: Boolean,
+            default: () => defProps.calendar.showMark
+        },
+        // 确定按钮的文字
+        confirmText: {
+            type: String,
+            default: () => defProps.calendar.confirmText
+        },
+        // 确认按钮处于禁用状态时的文字
+        confirmDisabledText: {
+            type: String,
+            default: () => defProps.calendar.confirmDisabledText
+        },
+        // 是否显示日历弹窗
+        show: {
+            type: Boolean,
+            default: () => defProps.calendar.show
+        },
+        // 是否允许点击遮罩关闭日历
+        closeOnClickOverlay: {
+            type: Boolean,
+            default: () => defProps.calendar.closeOnClickOverlay
+        },
+        // 是否为只读状态,只读状态下禁止选择日期
+        readonly: {
+            type: Boolean,
+            default: () => defProps.calendar.readonly
+        },
+        // 	是否展示确认按钮
+        showConfirm: {
+            type: Boolean,
+            default: () => defProps.calendar.showConfirm
+        },
+        // 日期区间最多可选天数,默认无限制,mode = range时有效
+        maxRange: {
+            type: [Number, String],
+            default: () => defProps.calendar.maxRange
+        },
+        // 范围选择超过最多可选天数时的提示文案,mode = range时有效
+        rangePrompt: {
+            type: String,
+            default: () => defProps.calendar.rangePrompt
+        },
+        // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
+        showRangePrompt: {
+            type: Boolean,
+            default: () => defProps.calendar.showRangePrompt
+        },
+        // 是否允许日期范围的起止时间为同一天,mode = range时有效
+        allowSameDay: {
+            type: Boolean,
+            default: () => defProps.calendar.allowSameDay
+        },
+		// 圆角值
+		round: {
+		    type: [Boolean, String, Number],
+		    default: () => defProps.calendar.round
+		},
+		// 最多展示月份数量
+		monthNum: {
+			type: [Number, String],
+			default: 3
+		},
+        // 星期文案
+        weekText: {
+			type: Array,
+			default: defProps.calendar.weekText
+		},
+        forbidDays: {
+			type: Array,
+			default: defProps.calendar.forbidDays
+		},
+        forbidDaysToast:{
+			type: String,
+			default: defProps.calendar.forbidDaysToast
+		},
+    }
+})

+ 411 - 0
uni_modules/uview-plus/components/u-calendar/u-calendar.vue

@@ -0,0 +1,411 @@
+<template>
+	<u-popup
+		:show="show"
+		mode="bottom"
+		closeable
+		@close="close"
+		:round="round"
+		:closeOnClickOverlay="closeOnClickOverlay"
+	>
+		<view class="u-calendar">
+			<uHeader
+				:title="title"
+				:subtitle="subtitle"
+				:showSubtitle="showSubtitle"
+				:showTitle="showTitle"
+				:weekText="weekText"
+			></uHeader>
+			<scroll-view
+				:style="{
+                    height: addUnit(listHeight)
+                }"
+				scroll-y
+				@scroll="onScroll"
+				:scroll-top="scrollTop"
+				:scrollIntoView="scrollIntoView"
+			>
+				<uMonth
+					:color="color"
+					:rowHeight="rowHeight"
+					:showMark="showMark"
+					:months="months"
+					:mode="mode"
+					:maxCount="maxCount"
+					:startText="startText"
+					:endText="endText"
+					:defaultDate="defaultDate"
+					:minDate="innerMinDate"
+					:maxDate="innerMaxDate"
+					:maxMonth="monthNum"
+					:readonly="readonly"
+					:maxRange="maxRange"
+					:rangePrompt="rangePrompt"
+					:showRangePrompt="showRangePrompt"
+					:allowSameDay="allowSameDay"
+					:forbidDays="forbidDays"
+					:forbidDaysToast="forbidDaysToast"
+					ref="month"
+					@monthSelected="monthSelected"
+					@updateMonthTop="updateMonthTop"
+				></uMonth>
+			</scroll-view>
+			<slot name="footer" v-if="showConfirm">
+				<view class="u-calendar__confirm">
+					<u-button
+						shape="circle"
+						:text="
+                            buttonDisabled ? confirmDisabledText : confirmText
+                        "
+						:color="color"
+						@click="confirm"
+						:disabled="buttonDisabled"
+					></u-button>
+				</view>
+			</slot>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import uHeader from './header.vue'
+import uMonth from './month.vue'
+import { props } from './props.js'
+import util from './util.js'
+import dayjs from 'dayjs/esm/index'
+import Calendar from '../../libs/util/calendar.js'
+import { mpMixin } from '../../libs/mixin/mpMixin.js'
+import { mixin } from '../../libs/mixin/mixin.js'
+import { addUnit, range, error, padZero } from '../../libs/function/index';
+import test from '../../libs/function/test';
+/**
+ * Calendar 日历
+ * @description  此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中.
+ * @tutorial https://ijry.github.io/uview-plus/components/calendar.html
+ *
+ * @property {String}				title				标题内容 (默认 日期选择 )
+ * @property {Boolean}				showTitle			是否显示标题  (默认 true )
+ * @property {Boolean}				showSubtitle		是否显示副标题	(默认 true )
+ * @property {String}				mode				日期类型选择  single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 ( 默认 'single' )
+ * @property {String}				startText			mode=range时,第一个日期底部的提示文字  (默认 '开始' )
+ * @property {String}				endText				mode=range时,最后一个日期底部的提示文字 (默认 '结束' )
+ * @property {Array}				customList			自定义列表
+ * @property {String}				color				主题色,对底部按钮和选中日期有效  (默认 ‘#3c9cff' )
+ * @property {String | Number}		minDate				最小的可选日期	 (默认 0 )
+ * @property {String | Number}		maxDate				最大可选日期  (默认 0 )
+ * @property {Array | String| Date}	defaultDate			默认选中的日期,mode为multiple或range是必须为数组格式
+ * @property {String | Number}		maxCount			mode=multiple时,最多可选多少个日期  (默认 	Number.MAX_SAFE_INTEGER  )
+ * @property {String | Number}		rowHeight			日期行高 (默认 56 )
+ * @property {Function}				formatter			日期格式化函数
+ * @property {Boolean}				showLunar			是否显示农历  (默认 false )
+ * @property {Boolean}				showMark			是否显示月份背景色 (默认 true )
+ * @property {String}				confirmText			确定按钮的文字 (默认 '确定' )
+ * @property {String}				confirmDisabledText	确认按钮处于禁用状态时的文字 (默认 '确定' )
+ * @property {Boolean}				show				是否显示日历弹窗 (默认 false )
+ * @property {Boolean}				closeOnClickOverlay	是否允许点击遮罩关闭日历 (默认 false )
+ * @property {Boolean}				readonly	        是否为只读状态,只读状态下禁止选择日期 (默认 false )
+ * @property {String | Number}		maxRange	        日期区间最多可选天数,默认无限制,mode = range时有效
+ * @property {String}				rangePrompt	        范围选择超过最多可选天数时的提示文案,mode = range时有效
+ * @property {Boolean}				showRangePrompt	    范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true )
+ * @property {Boolean}				allowSameDay	    是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false )
+ * @property {Number|String}	    round				圆角值,默认无圆角  (默认 0 )
+ * @property {Number|String}	    monthNum			最多展示的月份数量  (默认 3 )
+ * @property {Array}	            weekText			星期文案  (默认 ['一', '二', '三', '四', '五', '六', '日'] )
+ *
+ * @event {Function()} confirm 		点击确定按钮时触发		选择日期相关的返回参数
+ * @event {Function()} close 		日历关闭时触发			可定义页面关闭时的回调事件
+ * @example <u-calendar  :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm">
+	</u-calendar>
+ * */
+export default {
+	name: 'u-calendar',
+	mixins: [mpMixin, mixin, props],
+	components: {
+		uHeader,
+		uMonth
+	},
+	data() {
+		return {
+			// 需要显示的月份的数组
+			months: [],
+			// 在月份滚动区域中,当前视图中月份的index索引
+			monthIndex: 0,
+			// 月份滚动区域的高度
+			listHeight: 0,
+			// month组件中选择的日期数组
+			selected: [],
+			scrollIntoView: '',
+			scrollIntoViewScroll: '',
+			scrollTop:0,
+			// 过滤处理方法
+			innerFormatter: (value) => value
+		}
+	},
+	watch: {
+		scrollIntoView: {
+			immediate: true,
+			handler(n) {
+				// console.log('scrollIntoView', n)
+			}
+		},
+		selectedChange: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		},
+		// 打开弹窗时,设置月份数据
+		show: {
+			immediate: true,
+			handler(n) {
+				if (n) {
+					this.setMonth()
+				} else {
+					// 关闭时重置scrollIntoView,否则会出现二次打开日历,当前月份数据显示不正确。
+					// scrollIntoView需要有一个值变动过程,才会产生作用。
+					this.scrollIntoView = ''
+				}
+			}
+		}
+	},
+	computed: {
+		// 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理
+		innerMaxDate() {
+			return test.number(this.maxDate)
+				? Number(this.maxDate)
+				: this.maxDate
+		},
+		innerMinDate() {
+			return test.number(this.minDate)
+				? Number(this.minDate)
+				: this.minDate
+		},
+		// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
+		selectedChange() {
+			return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
+		},
+		subtitle() {
+			// 初始化时,this.months为空数组,所以需要特别判断处理
+			if (this.months.length) {
+				return `${this.months[this.monthIndex].year}年${
+					this.months[this.monthIndex].month
+				}月`
+			} else {
+				return ''
+			}
+		},
+		buttonDisabled() {
+			// 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态
+			if (this.mode === 'range') {
+				if (this.selected.length <= 1) {
+					return true
+				} else {
+					return false
+				}
+			} else {
+				return false
+			}
+		}
+	},
+	mounted() {
+		this.start = Date.now()
+		this.init()
+	},
+	emits: ["confirm", "close"],
+	methods: {
+		addUnit,
+		// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
+		setFormatter(e) {
+			this.innerFormatter = e
+		},
+		// month组件内部选择日期后,通过事件通知给父组件
+		monthSelected(e,scene ='init') {
+			this.selected = e
+			if (!this.showConfirm) {
+				// 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还
+				if (
+					this.mode === 'multiple' ||
+					this.mode === 'single' ||
+					(this.mode === 'range' && this.selected.length >= 2)
+				) {
+				   if( scene === 'init'){
+					 return
+				   }
+				   if( scene === 'tap') {
+					 this.$emit('confirm', this.selected)
+				   }
+				}
+			}
+		},
+		init() {
+			// 校验maxDate,不能小于minDate。
+			if (
+				this.innerMaxDate &&
+                this.innerMinDate &&
+				new Date(this.innerMaxDate).getTime() < new Date(this.innerMinDate).getTime()
+			) {
+				return error('maxDate不能小于minDate时间')
+			}
+			// 滚动区域的高度
+			this.listHeight = this.rowHeight * 5 + 30
+			this.setMonth()
+		},
+		close() {
+			this.$emit('close')
+		},
+		// 点击确定按钮
+		confirm() {
+			if (!this.buttonDisabled) {
+				this.$emit('confirm', this.selected)
+			}
+		},
+		// 获得两个日期之间的月份数
+		getMonths(minDate, maxDate) {
+			const minYear = dayjs(minDate).year()
+			const minMonth = dayjs(minDate).month() + 1
+			const maxYear = dayjs(maxDate).year()
+			const maxMonth = dayjs(maxDate).month() + 1
+			return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1
+		},
+		// 设置月份数据
+		setMonth() {
+			// 最小日期的毫秒数
+			const minDate = this.innerMinDate || dayjs().valueOf()
+			// 如果没有指定最大日期,则往后推3个月
+			const maxDate =
+				this.innerMaxDate ||
+				dayjs(minDate)
+					.add(this.monthNum - 1, 'month')
+					.valueOf()
+			// 最大最小月份之间的共有多少个月份,
+			const months = range(
+				1,
+				this.monthNum,
+				this.getMonths(minDate, maxDate)
+			)
+			// 先清空数组
+			this.months = []
+			for (let i = 0; i < months; i++) {
+				this.months.push({
+					date: new Array(
+						dayjs(minDate).add(i, 'month').daysInMonth()
+					)
+						.fill(1)
+						.map((item, index) => {
+							// 日期,取值1-31
+							let day = index + 1
+							// 星期,0-6,0为周日
+							const week = dayjs(minDate)
+								.add(i, 'month')
+								.date(day)
+								.day()
+							const date = dayjs(minDate)
+								.add(i, 'month')
+								.date(day)
+								.format('YYYY-MM-DD')
+							let bottomInfo = ''
+							if (this.showLunar) {
+								// 将日期转为农历格式
+								const lunar = Calendar.solar2lunar(
+									dayjs(date).year(),
+									dayjs(date).month() + 1,
+									dayjs(date).date()
+								)
+								bottomInfo = lunar.IDayCn
+							}
+							let config = {
+								day,
+								week,
+								// 小于最小允许的日期,或者大于最大的日期,则设置为disabled状态
+								disabled:
+									dayjs(date).isBefore(
+										dayjs(minDate).format('YYYY-MM-DD')
+									) ||
+									dayjs(date).isAfter(
+										dayjs(maxDate).format('YYYY-MM-DD')
+									),
+								// 返回一个日期对象,供外部的formatter获取当前日期的年月日等信息,进行加工处理
+								date: new Date(date),
+								bottomInfo,
+								dot: false,
+								month:
+									dayjs(minDate).add(i, 'month').month() + 1
+							}
+							const formatter =
+								this.formatter || this.innerFormatter
+							return formatter(config)
+						}),
+					// 当前所属的月份
+					month: dayjs(minDate).add(i, 'month').month() + 1,
+					// 当前年份
+					year: dayjs(minDate).add(i, 'month').year()
+				})
+			}
+		},
+		// 滚动到默认设置的月份
+		scrollIntoDefaultMonth(selected) {
+			// 查询默认日期在可选列表的下标
+			const _index = this.months.findIndex(({
+				  year,
+				  month
+			  }) => {
+				month = padZero(month)
+				return `${year}-${month}` === selected
+			})
+			if (_index !== -1) {
+				// #ifndef MP-WEIXIN
+				this.$nextTick(() => {
+					this.scrollIntoView = `month-${_index}`
+					this.scrollIntoViewScroll = this.scrollIntoView
+				})
+				// #endif
+				// #ifdef MP-WEIXIN
+				this.scrollTop = this.months[_index].top || 0;
+				// #endif
+			}
+		},
+		// scroll-view滚动监听
+		onScroll(event) {
+			// 不允许小于0的滚动值,如果scroll-view到顶了,继续下拉,会出现负数值
+			const scrollTop = Math.max(0, event.detail.scrollTop)
+			// 将当前滚动条数值,除以滚动区域的高度,可以得出当前滚动到了哪一个月份的索引
+			for (let i = 0; i < this.months.length; i++) {
+				if (scrollTop >= (this.months[i].top || this.listHeight)) {
+					this.monthIndex = i
+					this.scrollIntoViewScroll = `month-${i}`
+				}
+			}
+		},
+		// 更新月份的top值
+		updateMonthTop(topArr = []) {
+			// 设置对应月份的top值,用于onScroll方法更新月份
+			topArr.map((item, index) => {
+				this.months[index].top = item
+			})
+
+			// 获取默认日期的下标
+			if (!this.defaultDate) {
+				// 如果没有设置默认日期,则将当天日期设置为默认选中的日期
+				const selected = dayjs().format("YYYY-MM")
+				this.scrollIntoDefaultMonth(selected)
+				return
+			}
+			let selected = dayjs().format("YYYY-MM");
+			// 单选模式,可以是字符串或数组,Date对象等
+			if (!test.array(this.defaultDate)) {
+				selected = dayjs(this.defaultDate).format("YYYY-MM")
+			} else {
+				selected = dayjs(this.defaultDate[0]).format("YYYY-MM");
+			}
+			this.scrollIntoDefaultMonth(selected)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.u-calendar {
+	&__confirm {
+		padding: 7px 18px;
+	}
+}
+</style>

+ 86 - 0
uni_modules/uview-plus/components/u-calendar/util.js

@@ -0,0 +1,86 @@
+import dayjs from 'dayjs/esm/index'
+export default {
+    methods: {
+        // 设置月份数据
+        setMonth() {
+            // 月初是周几
+            const day = dayjs(this.date).date(1).day()
+            const start = day == 0 ? 6 : day - 1
+
+            // 本月天数
+            const days = dayjs(this.date).endOf('month').format('D')
+
+            // 上个月天数
+            const prevDays = dayjs(this.date).endOf('month').subtract(1, 'month').format('D')
+
+            // 日期数据
+            const arr = []
+            // 清空表格
+            this.month = []
+
+            // 添加上月数据
+            arr.push(
+                ...new Array(start).fill(1).map((e, i) => {
+                    const day = prevDays - start + i + 1
+
+                    return {
+                        value: day,
+                        disabled: true,
+                        date: dayjs(this.date).subtract(1, 'month').date(day).format('YYYY-MM-DD')
+                    }
+                })
+            )
+
+            // 添加本月数据
+            arr.push(
+                ...new Array(days - 0).fill(1).map((e, i) => {
+                    const day = i + 1
+
+                    return {
+                        value: day,
+                        date: dayjs(this.date).date(day).format('YYYY-MM-DD')
+                    }
+                })
+            )
+
+            // 添加下个月
+            arr.push(
+                ...new Array(42 - days - start).fill(1).map((e, i) => {
+                    const day = i + 1
+
+                    return {
+                        value: day,
+                        disabled: true,
+                        date: dayjs(this.date).add(1, 'month').date(day).format('YYYY-MM-DD')
+                    }
+                })
+            )
+
+            // 分割数组
+            for (let n = 0; n < arr.length; n += 7) {
+                this.month.push(
+                    arr.slice(n, n + 7).map((e, i) => {
+                        e.index = i + n
+
+                        // 自定义信息
+                        const custom = this.customList.find((c) => c.date == e.date)
+
+                        // 农历
+                        if (this.lunar) {
+                            const {
+                                IDayCn,
+                                IMonthCn
+                            } = this.getLunar(e.date)
+                            e.lunar = IDayCn == '初一' ? IMonthCn : IDayCn
+                        }
+
+                        return {
+                            ...e,
+                            ...custom
+                        }
+                    })
+                )
+            }
+        }
+    }
+}

+ 15 - 0
uni_modules/uview-plus/components/u-car-keyboard/carKeyboard.js

@@ -0,0 +1,15 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:53:20
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/carKeyboard.js
+ */
+export default {
+    // 车牌号键盘
+    carKeyboard: {
+        random: false
+    }
+}

+ 17 - 0
uni_modules/uview-plus/components/u-car-keyboard/props.js

@@ -0,0 +1,17 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+    props: {
+        // 是否打乱键盘按键的顺序
+        random: {
+            type: Boolean,
+            default: false
+        },
+        // 输入一个中文后,是否自动切换到英文
+        autoChange: {
+            type: Boolean,
+            default: false
+        }
+    }
+})

+ 314 - 0
uni_modules/uview-plus/components/u-car-keyboard/u-car-keyboard.vue

@@ -0,0 +1,314 @@
+<template>
+	<view
+		class="u-keyboard"
+		@touchmove.stop.prevent="noop"
+	>
+		<view
+			v-for="(group, i) in abc ? engKeyBoardList : areaList"
+			:key="i"
+			class="u-keyboard__button"
+			:index="i"
+			:class="[i + 1 === 4 && 'u-keyboard__button--center']"
+		>
+			<view
+				v-if="i === 3"
+				class="u-keyboard__button__inner-wrapper"
+			>
+				<view
+					class="u-keyboard__button__inner-wrapper__left"
+					hover-class="u-hover-class"
+					:hover-stay-time="200"
+					@tap="changeCarInputMode"
+				>
+					<text
+						class="u-keyboard__button__inner-wrapper__left__lang"
+						:class="[!abc && 'u-keyboard__button__inner-wrapper__left__lang--active']"
+					>中</text>
+					<text class="u-keyboard__button__inner-wrapper__left__line">/</text>
+					<text
+						class="u-keyboard__button__inner-wrapper__left__lang"
+						:class="[abc && 'u-keyboard__button__inner-wrapper__left__lang--active']"
+					>英</text>
+				</view>
+			</view>
+			<view
+				class="u-keyboard__button__inner-wrapper"
+				v-for="(item, j) in group"
+				:key="j"
+			>
+				<view
+					class="u-keyboard__button__inner-wrapper__inner"
+					:hover-stay-time="200"
+					@tap="carInputClick(i, j)"
+					hover-class="u-hover-class"
+				>
+					<text class="u-keyboard__button__inner-wrapper__inner__text">{{ item }}</text>
+				</view>
+			</view>
+			<view
+				v-if="i === 3"
+				@touchstart="backspaceClick"
+				@touchend="clearTimer"
+				class="u-keyboard__button__inner-wrapper"
+			>
+				<view
+					class="u-keyboard__button__inner-wrapper__right"
+					hover-class="u-hover-class"
+					:hover-stay-time="200"
+				>
+					<u-icon
+						size="28"
+						name="backspace"
+						color="#303133"
+					></u-icon>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { randomArray, sleep } from '../../libs/function/index';
+	/**
+	 * keyboard 键盘组件
+	 * @description 此为uview-plus自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/keyboard.html
+	 * @property {Boolean} random 是否打乱键盘的顺序
+	 * @event {Function} change 点击键盘触发
+	 * @event {Function} backspace 点击退格键触发
+	 * @example <u-keyboard ref="uKeyboard" mode="car" v-model="show"></u-keyboard>
+	 */
+	export default {
+		name: "u-car-keyboard",
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称
+				abc: false
+			};
+		},
+		computed: {
+			areaList() {
+				let data = [
+					'京',
+					'沪',
+					'粤',
+					'津',
+					'冀',
+					'豫',
+					'云',
+					'辽',
+					'黑',
+					'湘',
+					'皖',
+					'鲁',
+					'苏',
+					'浙',
+					'赣',
+					'鄂',
+					'桂',
+					'甘',
+					'晋',
+					'陕',
+					'蒙',
+					'吉',
+					'闽',
+					'贵',
+					'渝',
+					'川',
+					'青',
+					'琼',
+					'宁',
+					'挂',
+					'藏',
+					'港',
+					'澳',
+					'新',
+					'使',
+					'学'
+				];
+				let tmp = [];
+				// 打乱顺序
+				if (this.random) data = randomArray(data);
+				// 切割成二维数组
+				tmp[0] = data.slice(0, 10);
+				tmp[1] = data.slice(10, 20);
+				tmp[2] = data.slice(20, 30);
+				tmp[3] = data.slice(30, 36);
+				return tmp;
+			},
+			engKeyBoardList() {
+				let data = [
+					1,
+					2,
+					3,
+					4,
+					5,
+					6,
+					7,
+					8,
+					9,
+					0,
+					'Q',
+					'W',
+					'E',
+					'R',
+					'T',
+					'Y',
+					'U',
+					'I',
+					'O',
+					'P',
+					'A',
+					'S',
+					'D',
+					'F',
+					'G',
+					'H',
+					'J',
+					'K',
+					'L',
+					'Z',
+					'X',
+					'C',
+					'V',
+					'B',
+					'N',
+					'M'
+				];
+				let tmp = [];
+				if (this.random) data = randomArray(data);
+				tmp[0] = data.slice(0, 10);
+				tmp[1] = data.slice(10, 20);
+				tmp[2] = data.slice(20, 30);
+				tmp[3] = data.slice(30, 36);
+				return tmp;
+			}
+		},
+		emits: ["change", "backspace"],
+		methods: {
+			// 点击键盘按钮
+			carInputClick(i, j) {
+				let value = '';
+				// 不同模式,获取不同数组的值
+				if (this.abc) value = this.engKeyBoardList[i][j];
+				else value = this.areaList[i][j];
+				// 如果允许自动切换,则将中文状态切换为英文
+				if (!this.abc && this.autoChange) sleep(200).then(() => this.abc = true)
+				this.$emit('change', value);
+			},
+			// 修改汽车牌键盘的输入模式,中文|英文
+			changeCarInputMode() {
+				this.abc = !this.abc;
+			},
+			// 点击退格键
+			backspaceClick() {
+				this.$emit('backspace');
+				clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
+				this.timer = null;
+				this.timer = setInterval(() => {
+					this.$emit('backspace');
+				}, 250);
+			},
+			clearTimer() {
+				clearInterval(this.timer);
+				this.timer = null;
+			},
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	$u-car-keyboard-background-color: rgb(224, 228, 230) !default;
+	$u-car-keyboard-padding:6px 0 6px !default;
+	$u-car-keyboard-button-inner-width:64rpx !default;
+	$u-car-keyboard-button-inner-background-color:#FFFFFF !default;
+	$u-car-keyboard-button-height:80rpx !default;
+	$u-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default;
+	$u-car-keyboard-button-border-radius:4px !default;
+	$u-car-keyboard-button-inner-margin:8rpx 5rpx !default;
+	$u-car-keyboard-button-text-font-size:16px !default;
+	$u-car-keyboard-button-text-color:$u-main-color !default;
+	$u-car-keyboard-center-inner-margin: 0 4rpx !default;
+	$u-car-keyboard-special-button-width:134rpx !default;
+	$u-car-keyboard-lang-font-size:16px !default;
+	$u-car-keyboard-lang-color:$u-main-color !default;
+	$u-car-keyboard-active-color:$u-primary !default;
+	$u-car-keyboard-line-font-size:15px !default;
+	$u-car-keyboard-line-color:$u-main-color !default;
+	$u-car-keyboard-line-margin:0 1px !default;
+	$u-car-keyboard-u-hover-class-background-color:#BBBCC6 !default;
+
+	.u-keyboard {
+		@include flex(column);
+		justify-content: space-around;
+		background-color: $u-car-keyboard-background-color;
+		align-items: stretch;
+		padding: $u-car-keyboard-padding;
+
+		&__button {
+			@include flex;
+			justify-content: center;
+			flex: 1;
+			/* #ifndef APP-NVUE */
+			/* #endif */
+
+			&__inner-wrapper {
+				box-shadow: $u-car-keyboard-button-inner-box-shadow;
+				margin: $u-car-keyboard-button-inner-margin;
+				border-radius: $u-car-keyboard-button-border-radius;
+
+				&__inner {
+					@include flex;
+					justify-content: center;
+					align-items: center;
+					width: $u-car-keyboard-button-inner-width;
+					background-color: $u-car-keyboard-button-inner-background-color;
+					height: $u-car-keyboard-button-height;
+					border-radius: $u-car-keyboard-button-border-radius;
+
+					&__text {
+						font-size: $u-car-keyboard-button-text-font-size;
+						color: $u-car-keyboard-button-text-color;
+					}
+				}
+
+				&__left,
+				&__right {
+					border-radius: $u-car-keyboard-button-border-radius;
+					width: $u-car-keyboard-special-button-width;
+					height: $u-car-keyboard-button-height;
+					background-color: $u-car-keyboard-u-hover-class-background-color;
+					@include flex;
+					justify-content: center;
+					align-items: center;
+					box-shadow: $u-car-keyboard-button-inner-box-shadow;
+				}
+
+				&__left {
+					&__line {
+						font-size: $u-car-keyboard-line-font-size;
+						color: $u-car-keyboard-line-color;
+						margin: $u-car-keyboard-line-margin;
+					}
+
+					&__lang {
+						font-size: $u-car-keyboard-lang-font-size;
+						color: $u-car-keyboard-lang-color;
+
+						&--active {
+							color: $u-car-keyboard-active-color;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	.u-hover-class {
+		background-color: $u-car-keyboard-u-hover-class-background-color;
+	}
+</style>

+ 40 - 0
uni_modules/uview-plus/components/u-card/card.js

@@ -0,0 +1,40 @@
+/*
+ * @Author       : jry
+ * @Description  :
+ * @version      : 3.0
+ * @Date         : 2025-04-26 16:37:21
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-04-26 16:37:21
+ * @FilePath     : /uview-plus/libs/config/props/card.js
+ */
+export default {
+	// card组件的props
+	card: {
+		full: false,
+		title: '',
+		titleColor: '#303133',
+		titleSize: '15px',
+		subTitle: '',
+		subTitleColor: '#909399',
+		subTitleSize: '13px',
+		border: true,
+		index: '',
+		margin: '15px',
+		borderRadius: '8px',
+		headStyle: {},
+		bodyStyle: {},
+		footStyle: {},
+		headBorderBottom: true,
+		footBorderTop: true,
+		thumb: '',
+		thumbWidth: '30px',
+		thumbCircle: false,
+		padding: '15px',
+		paddingHead: '',
+        paddingBody: '',
+        paddingFoot: '',
+        showHead: true,
+        showFoot: true,
+        boxShadow: 'none'
+	}
+}

+ 134 - 0
uni_modules/uview-plus/components/u-card/props.js

@@ -0,0 +1,134 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const propsCard = defineMixin({
+    props: {
+        // 与屏幕两侧是否留空隙
+		full: {
+			type: Boolean,
+			default: () => defProps.card.full
+		},
+		// 标题
+		title: {
+			type: String,
+			default: () => defProps.card.title
+		},
+		// 标题颜色
+		titleColor: {
+			type: String,
+			default: () => defProps.card.titleColor
+		},
+		// 标题字体大小
+		titleSize: {
+			type: [Number, String],
+			default: () => defProps.card.titleSize
+		},
+		// 副标题
+		subTitle: {
+			type: String,
+			default: () => defProps.card.subTitle
+		},
+		// 副标题颜色
+		subTitleColor: {
+			type: String,
+			default: () => defProps.card.subTitleColor
+		},
+		// 副标题字体大小
+		subTitleSize: {
+			type: [Number, String],
+			default: () => defProps.card.subTitleSize
+		},
+		// 是否显示外部边框,只对full=false时有效(卡片与边框有空隙时)
+		border: {
+			type: Boolean,
+			default: () => defProps.card.border
+		},
+		// 用于标识点击了第几个
+		index: {
+			type: [Number, String, Object],
+			default: () => defProps.card.index
+		},
+		// 用于隔开上下左右的边距,带单位的写法,如:"30px 30px","20px 20px 30px 30px"
+		margin: {
+			type: String,
+			default: () => defProps.card.margin
+		},
+		// card卡片的圆角
+		borderRadius: {
+			type: [Number, String],
+			default: () => defProps.card.borderRadius
+		},
+		// 头部自定义样式,对象形式
+		headStyle: {
+			type: Object,
+			default: () => defProps.card.headStyle
+		},
+		// 主体自定义样式,对象形式
+		bodyStyle: {
+			type: Object,
+			default: () => defProps.card.bodyStyle
+		},
+		// 底部自定义样式,对象形式
+		footStyle: {
+			type: Object,
+			default: () => defProps.card.footStyle
+		},
+		// 头部是否下边框
+		headBorderBottom: {
+			type: Boolean,
+			default: () => defProps.card.headBorderBottom
+		},
+		// 底部是否有上边框
+		footBorderTop: {
+			type: Boolean,
+			default: () => defProps.card.footBorderTop
+		},
+		// 标题左边的缩略图
+		thumb: {
+			type: String,
+			default: () => defProps.card.thumb
+		},
+		// 缩略图宽高
+		thumbWidth: {
+			type: [String, Number],
+			default: () => defProps.card.thumbWidth
+		},
+		// 缩略图是否为圆形
+		thumbCircle: {
+			type: Boolean,
+			default: () => defProps.card.thumbCircle
+		},
+		// 给head,body,foot的内边距
+		padding: {
+			type: [String, Number],
+			default: () => defProps.card.padding
+		},
+		paddingHead: {
+			type: [String, Number],
+			default: () => defProps.card.paddingHead
+		},
+		paddingBody: {
+			type: [String, Number],
+			default: () => defProps.card.paddingBody
+		},
+		paddingFoot: {
+			type: [String, Number],
+			default: () => defProps.card.paddingFoot
+		},
+		// 是否显示头部
+		showHead: {
+			type: Boolean,
+			default: () => defProps.card.showHead
+		},
+		// 是否显示尾部
+		showFoot: {
+			type: Boolean,
+			default: () => defProps.card.showFoot
+		},
+		// 卡片外围阴影,字符串形式
+		boxShadow: {
+			type: String,
+			default: () => defProps.card.boxShadow
+		}
+    }
+})

+ 184 - 0
uni_modules/uview-plus/components/u-card/u-card.vue

@@ -0,0 +1,184 @@
+<template>
+	<view
+		class="u-card"
+		@tap.stop="click"
+		:class="{ 'u-border': border, 'u-card-full': full, 'u-card--border': getPx(borderRadius) > 0 }"
+		:style="{
+			borderRadius: addUnit(borderRadius),
+			margin: margin,
+			boxShadow: boxShadow
+		}"
+	>
+		<view
+			v-if="showHead"
+			class="u-card__head"
+			:style="[{padding: addUnit(paddingHead || padding)}, headStyle]"
+			:class="{
+				'u-border-bottom': headBorderBottom
+			}"
+			@tap="headClick"
+		>
+			<view v-if="!$slots.head" class="u-flex u-flex-between">
+				<view class="u-card__head--left u-flex u-line-1" v-if="title">
+					<image
+						:src="thumb"
+						class="u-card__head--left__thumb"
+						mode="aspectFill"
+						v-if="thumb"
+						:style="{ 
+							height: addUnit(thumbWidth), 
+							width: addUnit(thumbWidth), 
+							borderRadius: thumbCircle ? '50px' : '4px' 
+						}"
+					></image>
+					<text
+						class="u-card__head--left__title u-line-1"
+						:style="{
+							fontSize: addUnit(titleSize),
+							color: titleColor
+						}"
+					>
+						{{ title }}
+					</text>
+				</view>
+				<view class="u-card__head--right u-line-1" v-if="subTitle">
+					<text
+						class="u-card__head__title__text"
+						:style="{
+							fontSize: addUnit(subTitleSize),
+							color: subTitleColor
+						}"
+					>
+						{{ subTitle }}
+					</text>
+				</view>
+			</view>
+			<slot name="head" v-else />
+		</view>
+		<view @tap="bodyClick" class="u-card__body"
+			:style="[{padding: addUnit(paddingBody || padding)}, bodyStyle]"><slot name="body" /></view>
+		<view
+			v-if="showFoot"
+			class="u-card__foot"
+			 @tap="footClick"
+			:style="[{padding: $slots.foot ? addUnit(paddingFoot || padding) : 0}, footStyle]"
+			:class="{
+				'u-border-top': footBorderTop
+			}"
+		>
+			<slot name="foot" />
+		</view>
+	</view>
+</template>
+
+<script>
+    import { propsCard } from './props';
+    import { mpMixin } from '../../libs/mixin/mpMixin';
+    import { mixin } from '../../libs/mixin/mixin';
+    import { addStyle, addUnit, getPx } from '../../libs/function/index';
+    /**
+     * card 卡片
+     * @description 卡片组件一般用于多个列表条目,且风格统一的场景
+     * @tutorial https://uview-plus.jiangruyi.com/components/card.html
+     * @property {Boolean} full 卡片与屏幕两侧是否留空隙(默认false)
+     * @property {String} title 头部左边的标题
+     * @property {String} title-color 标题颜色(默认#303133)
+     * @property {String | Number} title-size 标题字体大小,单位rpx(默认15px)
+     * @property {String} sub-title 头部右边的副标题
+     * @property {String} sub-title-color 副标题颜色(默认#909399)
+     * @property {String | Number} sub-title-size 副标题字体大小(默认13px
+     * @property {Boolean} border 是否显示边框(默认true)
+     * @property {String | Number} index 用于标识点击了第几个卡片
+     * @property {String} box-shadow 卡片外围阴影,字符串形式(默认none)
+     * @property {String} margin 卡片与屏幕两边和上下元素的间距,需带单位,如"30px 20px"(默认15px)
+     * @property {String | Number} border-radius 卡片整体的圆角值,单位rpx(默认8px)
+     * @property {Object} head-style 头部自定义样式,对象形式
+     * @property {Object} body-style 中部自定义样式,对象形式
+     * @property {Object} foot-style 底部自定义样式,对象形式
+     * @property {Boolean} head-border-bottom 是否显示头部的下边框(默认true)
+     * @property {Boolean} foot-border-top 是否显示底部的上边框(默认true)
+     * @property {Boolean} show-head 是否显示头部(默认true)
+     * @property {Boolean} show-foot 是否显示尾部(默认true)
+     * @property {String} thumb 缩略图路径,如设置将显示在标题的左边,不建议使用相对路径
+     * @property {String | Number} thumb-width 缩略图的宽度,高等于宽,单位px(默认30px)
+     * @property {Boolean} thumb-circle 缩略图是否为圆形(默认false)
+     * @event {Function} click 整个卡片任意位置被点击时触发
+     * @event {Function} head-click 卡片头部被点击时触发
+     * @event {Function} body-click 卡片主体部分被点击时触发
+     * @event {Function} foot-click 卡片底部部分被点击时触发
+     * @example <u-card paddingFoot="2px 15px" title="card"></u-card>
+     */
+    export default {
+        name: 'up-card',
+        data() {
+            return {};
+        },
+        mixins: [mpMixin, mixin, propsCard],
+        emits: ['click', 'head-click', 'body-click', 'foot-click'],
+        methods: {
+			addStyle,
+			addUnit,
+			getPx,
+            click() {
+                this.$emit('click', this.index);
+            },
+            headClick() {
+                this.$emit('head-click', this.index);
+            },
+            bodyClick() {
+                this.$emit('body-click', this.index);
+            },
+            footClick() {
+                this.$emit('foot-click', this.index);
+            }
+        }
+    };
+</script>
+
+<style lang="scss" scoped>
+.u-card {
+	position: relative;
+	overflow: hidden;
+	font-size: 28rpx;
+	background-color: #ffffff;
+	box-sizing: border-box;
+	
+	&-full {
+		// 如果是与屏幕之间不留空隙,应该设置左右边距为0
+		margin-left: 0 !important;
+		margin-right: 0 !important;
+		width: 100%;
+	}
+	
+	&--border:after {
+		border-radius: 16rpx;
+	}
+
+	&__head {
+		&--left {
+			color: $u-main-color;
+			
+			&__thumb {
+				margin-right: 16rpx;
+			}
+			
+			&__title {
+				max-width: 400rpx;
+			}
+		}
+
+		&--right {
+			color: $u-tips-color;
+			margin-left: 6rpx;
+		}
+	}
+
+	&__body {
+		color: $u-content-color;
+	}
+
+	&__foot {
+		color: $u-tips-color;
+	}
+}
+</style>

+ 340 - 0
uni_modules/uview-plus/components/u-cate-tab/u-cate-tab.vue

@@ -0,0 +1,340 @@
+<template>
+	<view class="u-cate-tab" :style="{ height: addUnit(height) }">
+		<view class="u-cate-tab__wrap">
+			<scroll-view class="u-cate-tab__view u-cate-tab__menu-scroll-view"
+                scroll-y scroll-with-animation :scroll-top="scrollTop"
+			    :scroll-into-view="itemId">
+				<view v-for="(item, index) in tabList" :key="index" class="u-cate-tab__item"
+                    :class="[innerCurrent == index ? 'u-cate-tab__item-active' : '']"
+				 @tap.stop="swichMenu(index)">
+					<slot name="tabItem" :item="item">
+                    </slot>
+                    <text v-if="!$slots['tabItem']" class="u-line-1">{{item[tabKeyName]}}</text>
+				</view>
+			</scroll-view>
+			<scroll-view :scroll-top="scrollRightTop" scroll-with-animation
+				scroll-y class="u-cate-tab__right-box" @scroll="rightScroll">
+				<slot name="rightTop" :tabList="tabList">
+                </slot>
+				<view class="u-cate-tab__page-view">
+					<view class="u-cate-tab__page-item" :id="'item' + index"
+						v-for="(item , index) in tabList" :key="index">
+                        <slot name="itemList" :item="item">
+                        </slot>
+						<template v-if="!$slots['itemList']">
+							<view class="item-title">
+                                <text>{{item[tabKeyName]}}</text>
+                            </view>
+                            <view class="item-container">
+                                <template v-for="(item1, index1) in item.children" :key="index1">
+                                    <slot name="pageItem" :pageItem="item1">
+                                        <view class="thumb-box" >
+                                            <image class="item-menu-image" :src="item1.icon" mode=""></image>
+                                            <view class="item-menu-name">{{item1[itemKeyName]}}</view>
+                                        </view>
+                                    </slot>
+                                </template>
+                            </view>
+						</template>
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+<script>
+	import { addUnit } from '../../libs/function/index';
+	export default {
+		name: 'up-cate-tab',
+        props: {
+			height: {
+                type: String,
+                default: '100%'
+            },
+            tabList: {
+                type: Array,
+                default: () => {
+                    return []
+                }
+            },
+            tabKeyName: {
+                type: String,
+                default: 'name'
+            },
+            itemKeyName: {
+                type: String,
+                default: 'name'
+            },
+			current: {
+                type: Number,
+                default: 0
+            }
+        },
+        watch: {
+            tabList() {
+                this.getMenuItemTop()
+            },
+			current(nval) {
+				this.innerCurrent = nval;
+				this.leftMenuStatus(this.innerCurrent);
+			}
+        },
+		emits: ['update:current'],
+		data() {
+			return {
+				scrollTop: 0, //tab标题的滚动条位置
+				oldScrollTop: 0,
+				innerCurrent: 0, // 预设当前项的值
+				menuHeight: 0, // 左边菜单的高度
+				menuItemHeight: 0, // 左边菜单item的高度
+				itemId: '', // 栏目右边scroll-view用于滚动的id
+				menuItemPos: [],
+                rects: [],
+				arr: [],
+				scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
+				timer: null, // 定时器
+			}
+		},
+		onMounted() {
+			this.innerCurrent = this.current;
+			this.leftMenuStatus(this.innerCurrent);
+			this.getMenuItemTop()
+		},
+		methods: {
+			addUnit,
+			// 点击左边的栏目切换
+			async swichMenu(index) {
+				if(this.arr.length == 0) {
+					await this.getMenuItemTop();
+				}
+				if (index == this.innerCurrent) return;
+				this.scrollRightTop = this.oldScrollTop;
+				this.$nextTick(function(){
+					this.scrollRightTop = this.arr[index];
+					this.innerCurrent = index;
+					this.leftMenuStatus(index);
+					this.$emit('update:current', index);
+				})
+			},
+			// 获取一个目标元素的高度
+			getElRect(elClass, dataVal) {
+				new Promise((resolve, reject) => {
+					const query = uni.createSelectorQuery().in(this);
+					query.select('.' + elClass).fields({
+						size: true
+					}, res => {
+						// 如果节点尚未生成,res值为null,循环调用执行
+						if (!res) {
+							setTimeout(() => {
+								this.getElRect(elClass);
+							}, 10);
+							return;
+						}
+						this[dataVal] = res.height;
+						resolve();
+					}).exec();
+				})
+			},
+			// 观测元素相交状态
+			async observer() {
+				this.tabList.map((val, index) => {
+					let observer = uni.createIntersectionObserver(this);
+					// 检测右边scroll-view的id为itemxx的元素与u-cate-tab__right-box的相交状态
+					// 如果跟.u-cate-tab__right-box底部相交,就动态设置左边栏目的活动状态
+					observer.relativeTo('.u-cate-tab__right-box', {
+						top: 0
+					}).observe('#item' + index, res => {
+						if (res.intersectionRatio > 0) {
+							let id = res.id.substring(4);
+							this.leftMenuStatus(id);
+						}
+					})
+				})
+			},
+			// 设置左边菜单的滚动状态
+			async leftMenuStatus(index) {
+				this.innerCurrent = index;
+				this.$emit('update:current', index);
+				// 如果为0,意味着尚未初始化
+				if (this.menuHeight == 0 || this.menuItemHeight == 0) {
+					await this.getElRect('u-cate-tab__menu-scroll-view', 'menuHeight');
+					await this.getElRect('u-cate-tab__item', 'menuItemHeight');
+				}
+				// 将菜单活动item垂直居中
+				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
+			},
+			// 获取右边菜单每个item到顶部的距离
+			getMenuItemTop() {
+				return new Promise(resolve => {
+					let selectorQuery = uni.createSelectorQuery().in(this);
+					selectorQuery.selectAll('.u-cate-tab__page-item').boundingClientRect((rects) => {
+						// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
+						if(!rects.length) {
+							setTimeout(() => {
+								this.getMenuItemTop();
+							}, 10);
+							return ;
+						}
+                        this.rects = rects;
+						rects.forEach((rect) => {
+							// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
+							this.arr.push(rect.top - rects[0].top);
+						})
+                        resolve();
+					}).exec()
+				})
+			},
+			// 右边菜单滚动
+			async rightScroll(e) {
+				this.oldScrollTop = e.detail.scrollTop;
+                // console.log(e.detail.scrollTop)
+                // console.log(JSON.stringify(this.arr))
+				if(this.arr.length == 0) {
+					await this.getMenuItemTop();
+				}
+				if(this.timer) return ;
+				if(!this.menuHeight) {
+					await this.getElRect('u-cate-tab__menu-scroll-view', 'menuHeight');
+				}
+				setTimeout(() => { // 节流
+					this.timer = null;
+					// scrollHeight为右边菜单垂直中点位置
+					let scrollHeight = e.detail.scrollTop + 1;
+                    // console.log(e.detail.scrollTop)
+					for (let i = 0; i < this.arr.length; i++) {
+						let height1 = this.arr[i];
+						let height2 = this.arr[i + 1];
+                        // console.log('i', i)
+                        // console.log('height1', height1)
+                        // console.log('height2', height2)
+						// 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
+						if (!height2 || scrollHeight >= height1 && scrollHeight <= height2) {
+                            // console.log('scrollHeight', scrollHeight)
+                            // console.log('height1', height1)
+                            // console.log('height2', height2)
+							this.leftMenuStatus(i);
+							return ;
+						}
+					}
+				}, 10)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.u-cate-tab {
+		display: flex;
+		flex-direction: column;
+	}
+
+	.u-cate-tab__wrap {
+		flex: 1;
+		display: flex;
+		overflow: hidden;
+	}
+
+	.u-search-inner {
+		background-color: rgb(234, 234, 234);
+		border-radius: 100rpx;
+		display: flex;
+		align-items: center;
+		padding: 10rpx 16rpx;
+	}
+
+	.u-search-text {
+		font-size: 26rpx;
+		color: $u-tips-color;
+		margin-left: 10rpx;
+	}
+
+	.u-cate-tab__view {
+		width: 200rpx;
+		height: 100%;
+	}
+
+	.u-cate-tab__item {
+		height: 110rpx;
+		background: #f6f6f6;
+		box-sizing: border-box;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 26rpx;
+		color: #444;
+		font-weight: 400;
+		line-height: 1;
+	}
+
+	.u-cate-tab__item-active {
+		position: relative;
+		color: #000;
+		font-size: 30rpx;
+		font-weight: 600;
+		background: #fff;
+	}
+
+	.u-cate-tab__item-active::before {
+		content: "";
+		position: absolute;
+		border-left: 4px solid $u-primary;
+		height: 32rpx;
+		left: 0;
+		top: 39rpx;
+	}
+
+	.u-cate-tab__view {
+		height: 100%;
+	}
+
+	.u-cate-tab__right-box {
+		flex: 1;
+		background-color: rgb(250, 250, 250);
+	}
+
+	.u-cate-tab__page-view {
+		padding: 16rpx;
+	}
+
+	.u-cate-tab__page-item {
+		margin-bottom: 30rpx;
+		background-color: #fff;
+		padding: 16rpx;
+		border-radius: 8rpx;
+	}
+
+	.u-cate-tab__page-item:last-child {
+		min-height: 100vh;
+	}
+
+	.item-title {
+		font-size: 26rpx;
+		color: $u-main-color;
+		font-weight: bold;
+	}
+
+	.item-menu-name {
+		font-weight: normal;
+		font-size: 24rpx;
+		color: $u-main-color;
+	}
+
+	.item-container {
+		display: flex;
+		flex-wrap: wrap;
+	}
+
+	.thumb-box {
+		width: 33.333333%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-direction: column;
+		margin-top: 20rpx;
+	}
+
+	.item-menu-image {
+		width: 120rpx;
+		height: 120rpx;
+	}
+</style>

+ 17 - 0
uni_modules/uview-plus/components/u-cell-group/cellGroup.js

@@ -0,0 +1,17 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:54:16
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/cellGroup.js
+ */
+export default {
+    // cell-group组件的props
+    cellGroup: {
+        title: '',
+        border: true,
+        customStyle: {}
+    }
+}

+ 16 - 0
uni_modules/uview-plus/components/u-cell-group/props.js

@@ -0,0 +1,16 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 分组标题
+        title: {
+            type: String,
+            default: () => defProps.cellGroup.title
+        },
+        // 是否显示外边框
+        border: {
+            type: Boolean,
+            default: () => defProps.cellGroup.border
+        }
+    }
+})

+ 66 - 0
uni_modules/uview-plus/components/u-cell-group/u-cell-group.vue

@@ -0,0 +1,66 @@
+<template>
+    <view :style="[addStyle(customStyle)]" :class="[customClass]" class="u-cell-group">
+        <view v-if="title" class="u-cell-group__title">
+            <slot name="title">
+				<text class="u-cell-group__title__text">{{ title }}</text>
+			</slot>
+        </view>
+        <view class="u-cell-group__wrapper">
+			<u-line v-if="border"></u-line>
+            <slot />
+        </view>
+    </view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addStyle } from '../../libs/function/index';
+	/**
+	 * cellGroup  单元格
+	 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/cell.html
+	 * 
+	 * @property {String}	title		分组标题
+	 * @property {Boolean}	border		是否显示外边框 (默认 true )
+	 * @property {Object}	customStyle	定义需要用到的外部样式
+	 * 
+	 * @event {Function} click 	点击cell列表时触发
+	 * @example <u-cell-group title="设置喜好">
+	 */
+	export default {
+		name: 'u-cell-group',
+		mixins: [mpMixin, mixin, props],
+		methods: {
+			addStyle
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	
+	$u-cell-group-title-padding: 16px 16px 8px !default;
+	$u-cell-group-title-font-size: 15px !default;
+	$u-cell-group-title-line-height: 16px !default;
+	$u-cell-group-title-color: $u-main-color !default;
+
+    .u-cell-group {
+		flex: 1;
+		
+        &__title {
+            padding: $u-cell-group-title-padding;
+
+            &__text {
+                font-size: $u-cell-group-title-font-size;
+                line-height: $u-cell-group-title-line-height;
+                color: $u-cell-group-title-color;
+            }
+        }
+		
+		&__wrapper {
+			position: relative;
+		}
+    }
+</style>
+

+ 35 - 0
uni_modules/uview-plus/components/u-cell/cell.js

@@ -0,0 +1,35 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-23 20:53:09
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/cell.js
+ */
+export default {
+	// cell组件的props
+	cell: {
+		customClass: '',
+		title: '',
+		label: '',
+		value: '',
+		icon: '',
+		disabled: false,
+		border: true,
+		center: false,
+		url: '',
+		linkType: 'navigateTo',
+		clickable: false,
+		isLink: false,
+		required: false,
+		arrowDirection: '',
+		iconStyle: {},
+		rightIconStyle: {},
+		rightIcon: 'arrow-right',
+		titleStyle: {},
+		size: '',
+		stop: true,
+		name: ''
+	}
+}

+ 112 - 0
uni_modules/uview-plus/components/u-cell/props.js

@@ -0,0 +1,112 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 标题
+        title: {
+            type: [String, Number],
+            default: () => defProps.cell.title
+        },
+        // 标题下方的描述信息
+        label: {
+            type: [String, Number],
+            default: () => defProps.cell.label
+        },
+        // 右侧的内容
+        value: {
+            type: [String, Number],
+            default: () => defProps.cell.value
+        },
+        // 左侧图标名称,或者图片链接(本地文件建议使用绝对地址)
+        icon: {
+            type: String,
+            default: () => defProps.cell.icon
+        },
+        // 是否禁用cell
+        disabled: {
+            type: Boolean,
+            default: () => defProps.cell.disabled
+        },
+        // 是否显示下边框
+        border: {
+            type: Boolean,
+            default: () => defProps.cell.border
+        },
+        // 内容是否垂直居中(主要是针对右侧的value部分)
+        center: {
+            type: Boolean,
+            default: () => defProps.cell.center
+        },
+        // 点击后跳转的URL地址
+        url: {
+            type: String,
+            default: () => defProps.cell.url
+        },
+        // 链接跳转的方式,内部使用的是uView封装的route方法,可能会进行拦截操作
+        linkType: {
+            type: String,
+            default: () => defProps.cell.linkType
+        },
+        // 是否开启点击反馈(表现为点击时加上灰色背景)
+        clickable: {
+            type: Boolean,
+            default: () => defProps.cell.clickable
+        },
+        // 是否展示右侧箭头并开启点击反馈
+        isLink: {
+            type: Boolean,
+            default: () => defProps.cell.isLink
+        },
+        // 是否显示表单状态下的必填星号(此组件可能会内嵌入input组件)
+        required: {
+            type: Boolean,
+            default: () => defProps.cell.required
+        },
+        // 右侧的图标箭头
+        rightIcon: {
+            type: String,
+            default: () => defProps.cell.rightIcon
+        },
+        // 右侧箭头的方向,可选值为:left,up,down
+        arrowDirection: {
+            type: String,
+            default: () => defProps.cell.arrowDirection
+        },
+        // 左侧图标样式
+        iconStyle: {
+            type: [Object, String],
+            default: () => {
+				return defProps.cell.iconStyle
+			}
+        },
+        // 右侧箭头图标的样式
+        rightIconStyle: {
+            type: [Object, String],
+            default: () => {
+				return defProps.cell.rightIconStyle
+			}
+        },
+        // 标题的样式
+        titleStyle: {
+            type: [Object, String],
+			default: () => {
+				return defProps.cell.titleStyle
+			}
+        },
+        // 单位元的大小,可选值为large
+        size: {
+            type: String,
+            default: () => defProps.cell.size
+        },
+        // 点击cell是否阻止事件传播
+        stop: {
+            type: Boolean,
+            default: () => defProps.cell.stop
+        },
+        // 标识符,cell被点击时返回
+        name: {
+            type: [Number, String],
+            default: () => defProps.cell.name
+        }
+    }
+})

+ 267 - 0
uni_modules/uview-plus/components/u-cell/u-cell.vue

@@ -0,0 +1,267 @@
+<template>
+	<view class="u-cell" :class="[customClass]" :style="[addStyle(customStyle)]"
+		:hover-class="(!disabled && (clickable || isLink)) ? 'u-cell--clickable' : ''" :hover-stay-time="250"
+		@tap="clickHandler">
+		<view class="u-cell__body" :class="[ center && 'u-cell--center', size === 'large' && 'u-cell__body--large']">
+			<view class="u-cell__body__content">
+				<view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon">
+					<slot name="icon" v-if="$slots.icon">
+					</slot>
+					<u-icon v-else :name="icon"
+						:custom-style="iconStyle"
+						:size="size === 'large' ? 22 : 18"></u-icon>
+				</view>
+				<view class="u-cell__title">
+                    <!-- 将slot与默认内容用if/else分开主要是因为微信小程序不支持slot嵌套传递,这样才能解决collapse组件的slot不失效问题,label暂时未用到。 -->
+					<slot name="title" v-if="$slots.title || !title">
+					</slot>
+                    <text v-else class="u-cell__title-text" :style="[titleTextStyle]"
+                        :class="[required && 'u-cell--required', disabled && 'u-cell--disabled', size === 'large' && 'u-cell__title-text--large']">{{ title }}</text>
+					<slot name="label">
+						<text class="u-cell__label" v-if="label"
+							:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__label--large']">{{ label }}</text>
+					</slot>
+				</view>
+			</view>
+			<slot name="value">
+				<text class="u-cell__value"
+					:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__value--large']"
+					v-if="!testEmpty(value)">{{ value }}</text>
+			</slot>
+			<view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink"
+				:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
+				<u-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
+					:custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'"
+					:size="size === 'large' ? 18 : 16"></u-icon>
+				<slot v-else name="right-icon">
+				</slot>
+			</view>
+			<view class="u-cell__right-icon-wrap" v-if="$slots['righticon']"
+				:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
+				<slot name="righticon">
+				</slot>
+			</view>
+		</view>
+		<u-line v-if="border"></u-line>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addStyle } from '../../libs/function/index';
+	import test from '../../libs/function/test';
+	/**
+	 * cell  单元格
+	 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/cell.html
+	 * @property {String | Number}	title			标题
+	 * @property {String | Number}	label			标题下方的描述信息
+	 * @property {String | Number}	value			右侧的内容
+	 * @property {String}			icon			左侧图标名称,或者图片链接(本地文件建议使用绝对地址)
+	 * @property {Boolean}			disabled		是否禁用cell	
+	 * @property {Boolean}			border			是否显示下边框 (默认 true )
+	 * @property {Boolean}			center			内容是否垂直居中(主要是针对右侧的value部分) (默认 false )
+	 * @property {String}			url				点击后跳转的URL地址
+	 * @property {String}			linkType		链接跳转的方式,内部使用的是uView封装的route方法,可能会进行拦截操作 (默认 'navigateTo' )
+	 * @property {Boolean}			clickable		是否开启点击反馈(表现为点击时加上灰色背景) (默认 false ) 
+	 * @property {Boolean}			isLink			是否展示右侧箭头并开启点击反馈 (默认 false )
+	 * @property {Boolean}			required		是否显示表单状态下的必填星号(此组件可能会内嵌入input组件) (默认 false )
+	 * @property {String}			rightIcon		右侧的图标箭头 (默认 'arrow-right')
+	 * @property {String}			arrowDirection	右侧箭头的方向,可选值为:left,up,down
+	 * @property {Object | String}			rightIconStyle	右侧箭头图标的样式
+	 * @property {Object | String}			titleStyle		标题的样式
+	 * @property {Object | String}			iconStyle		左侧图标样式
+	 * @property {String}			size			单位元的大小,可选值为 large,normal,mini 
+	 * @property {Boolean}			stop			点击cell是否阻止事件传播 (默认 true )
+	 * @property {Object}			customStyle		定义需要用到的外部样式
+	 * 
+	 * @event {Function}			click			点击cell列表时触发
+	 * @example 该组件需要搭配cell-group组件使用,见官方文档示例
+	 */
+	export default {
+		name: 'u-cell',
+		data() {
+			return {
+
+			}
+		},
+		mixins: [mpMixin, mixin, props],
+		computed: {
+			titleTextStyle() {
+				return addStyle(this.titleStyle)
+			}
+		},
+		emits: ['click'],
+		methods: {
+			addStyle,
+			testEmpty: test.empty,
+			// 点击cell
+			clickHandler(e) {
+				if (this.disabled) return
+				this.$emit('click', {
+					name: this.name
+				})
+				// 如果配置了url(此props参数通过mixin引入)参数,跳转页面
+				this.openPage()
+				// 是否阻止事件传播
+				this.stop && this.preventEvent(e)
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	$u-cell-padding: 13px 15px !default;
+	$u-cell-font-size: 15px !default;
+	$u-cell-line-height: 24px !default;
+	$u-cell-color: $u-main-color !default;
+	$u-cell-icon-size: 16px !default;
+	$u-cell-title-font-size: 15px !default;
+	$u-cell-title-line-height: 22px !default;
+	$u-cell-title-color: $u-main-color !default;
+	$u-cell-label-font-size: 12px !default;
+	$u-cell-label-color: $u-tips-color !default;
+	$u-cell-label-line-height: 18px !default;
+	$u-cell-value-font-size: 14px !default;
+	$u-cell-value-color: $u-content-color !default;
+	$u-cell-clickable-color: $u-bg-color !default;
+	$u-cell-disabled-color: #c8c9cc !default;
+	$u-cell-padding-top-large: 13px !default;
+	$u-cell-padding-bottom-large: 13px !default;
+	$u-cell-value-font-size-large: 15px !default;
+	$u-cell-label-font-size-large: 14px !default;
+	$u-cell-title-font-size-large: 16px !default;
+	$u-cell-left-icon-wrap-margin-right: 4px !default;
+	$u-cell-right-icon-wrap-margin-left: 4px !default;
+	$u-cell-title-flex:1 !default;
+	$u-cell-label-margin-top:5px !default;
+
+
+	.u-cell {
+		&__body {
+			@include flex();
+			/* #ifndef APP-NVUE */
+			box-sizing: border-box;
+			/* #endif */
+			padding: $u-cell-padding;
+			font-size: $u-cell-font-size;
+			color: $u-cell-color;
+			// line-height: $u-cell-line-height;
+			align-items: center;
+
+			&__content {
+				@include flex(row);
+				align-items: center;
+				flex: 1;
+			}
+
+			&--large {
+				padding-top: $u-cell-padding-top-large;
+				padding-bottom: $u-cell-padding-bottom-large;
+			}
+		}
+
+		&__left-icon-wrap,
+		&__right-icon-wrap {
+			@include flex();
+			align-items: center;
+			// height: $u-cell-line-height;
+			font-size: $u-cell-icon-size;
+		}
+
+		&__left-icon-wrap {
+			margin-right: $u-cell-left-icon-wrap-margin-right;
+		}
+
+		&__right-icon-wrap {
+			margin-left: $u-cell-right-icon-wrap-margin-left;
+			transition: transform 0.3s;
+
+			&--up {
+				transform: rotate(-90deg);
+			}
+
+			&--down {
+				transform: rotate(90deg);
+			}
+		}
+
+		&__title {
+			flex: $u-cell-title-flex;
+			display: flex;
+			flex-direction: column;
+
+			&-text {
+				font-size: $u-cell-title-font-size;
+				line-height: $u-cell-title-line-height;
+				color: $u-cell-title-color;
+
+				&--large {
+					font-size: $u-cell-title-font-size-large;
+				}
+			}
+
+		}
+
+		&__label {
+			margin-top: $u-cell-label-margin-top;
+			font-size: $u-cell-label-font-size;
+			color: $u-cell-label-color;
+			line-height: $u-cell-label-line-height;
+
+			&--large {
+				font-size: $u-cell-label-font-size-large;
+			}
+		}
+
+		&__value {	
+			text-align: right;
+			/* #ifndef APP-NVUE */
+			margin-left: auto;
+			/* #endif */	
+			font-size: $u-cell-value-font-size;
+			line-height: $u-cell-line-height;
+			color: $u-cell-value-color;
+			&--large {
+				font-size: $u-cell-value-font-size-large;
+			}
+		}
+
+		&--required {
+			/* #ifndef APP-NVUE */
+			overflow: visible;
+			/* #endif */
+			@include flex;
+			align-items: center;
+		}
+
+		&--required:before {
+			position: absolute;
+			/* #ifndef APP-NVUE */
+			content: '*';
+			/* #endif */
+			left: -8px;
+			margin-top: 4rpx;
+			font-size: 14px;
+			color: $u-error;
+		}
+
+		&--clickable {
+			background-color: $u-cell-clickable-color;
+		}
+
+		&--disabled {
+			color: $u-cell-disabled-color;
+			/* #ifndef APP-NVUE */
+			cursor: not-allowed;
+			/* #endif */
+		}
+
+		&--center {
+			align-items: center;
+		}
+	}
+</style>

+ 29 - 0
uni_modules/uview-plus/components/u-checkbox-group/checkboxGroup.js

@@ -0,0 +1,29 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:54:47
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/checkboxGroup.js
+ */
+export default {
+    // checkbox-group组件
+    checkboxGroup: {
+        name: '',
+        value: [],
+        shape: 'square',
+        disabled: false,
+        activeColor: '#2979ff',
+        inactiveColor: '#c8c9cc',
+        size: 18,
+        placement: 'row',
+        labelSize: 14,
+        labelColor: '#303133',
+        labelDisabled: false,
+        iconColor: '#ffffff',
+        iconSize: 12,
+        iconPlacement: 'left',
+        borderBottom: false
+    }
+}

+ 93 - 0
uni_modules/uview-plus/components/u-checkbox-group/props.js

@@ -0,0 +1,93 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+
+export const props = defineMixin({
+    props: {
+        // 标识符
+        name: {
+            type: String,
+            default: () => defProps.checkboxGroup.name
+        },
+		// #ifdef VUE3
+		// 绑定的值
+		modelValue: {
+		    type: Array,
+		    default: () => defProps.checkboxGroup.value
+		},
+		// #endif
+		// #ifdef VUE2
+		// 绑定的值
+		value: {
+		    type: Array,
+		    default: () => defProps.checkboxGroup.value
+		},
+		// #endif
+        // 形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: () => defProps.checkboxGroup.shape
+        },
+        // 是否禁用全部checkbox
+        disabled: {
+            type: Boolean,
+            default: () => defProps.checkboxGroup.disabled
+        },
+
+        // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+        activeColor: {
+            type: String,
+            default: () => defProps.checkboxGroup.activeColor
+        },
+        // 未选中的颜色
+        inactiveColor: {
+            type: String,
+            default: () => defProps.checkboxGroup.inactiveColor
+        },
+
+        // 整个组件的尺寸,默认px
+        size: {
+            type: [String, Number],
+            default: () => defProps.checkboxGroup.size
+        },
+        // 布局方式,row-横向,column-纵向
+        placement: {
+            type: String,
+            default: () => defProps.checkboxGroup.placement
+        },
+        // label的字体大小,px单位
+        labelSize: {
+            type: [String, Number],
+            default: () => defProps.checkboxGroup.labelSize
+        },
+        // label的字体颜色
+        labelColor: {
+            type: [String],
+            default: () => defProps.checkboxGroup.labelColor
+        },
+        // 是否禁止点击文本操作
+        labelDisabled: {
+            type: Boolean,
+            default: () => defProps.checkboxGroup.labelDisabled
+        },
+        // 图标颜色
+        iconColor: {
+            type: String,
+            default: () => defProps.checkboxGroup.iconColor
+        },
+        // 图标的大小,单位px
+        iconSize: {
+            type: [String, Number],
+            default: () => defProps.checkboxGroup.iconSize
+        },
+        // 勾选图标的对齐方式,left-左边,right-右边
+        iconPlacement: {
+            type: String,
+            default: () => defProps.checkboxGroup.iconPlacement
+        },
+        // 竖向配列时,是否显示下划线
+        borderBottom: {
+            type: Boolean,
+            default: () => defProps.checkboxGroup.borderBottom
+        }
+    }
+})

+ 133 - 0
uni_modules/uview-plus/components/u-checkbox-group/u-checkbox-group.vue

@@ -0,0 +1,133 @@
+<template>
+	<view
+	    class="u-checkbox-group"
+	    :class="bemClass"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	/**
+	 * checkboxGroup 复选框组
+	 * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
+	 * @tutorial https://ijry.github.io/uview-plus/components/checkbox.html
+	 * @property {String}			name			标识符 
+	 * @property {Array}			value			绑定的值
+	 * @property {String}			shape			形状,circle-圆形,square-方形 (默认 'square' )
+	 * @property {Boolean}			disabled		是否禁用全部checkbox (默认 false )
+	 * @property {String}			activeColor		选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 (默认 '#2979ff' )
+	 * @property {String}			inactiveColor	未选中的颜色 (默认 '#c8c9cc' )
+	 * @property {String | Number}	size			整个组件的尺寸 单位px (默认 18 )
+	 * @property {String}			placement		布局方式,row-横向,column-纵向 (默认 'row' )
+	 * @property {String | Number}	labelSize		label的字体大小,px单位  (默认 14 )
+	 * @property {String}			labelColor		label的字体颜色 (默认 '#303133' )
+	 * @property {Boolean}			labelDisabled	是否禁止点击文本操作 (默认 false )
+	 * @property {String}			iconColor		图标颜色 (默认 '#ffffff' )
+	 * @property {String | Number}	iconSize		图标的大小,单位px (默认 12 )
+	 * @property {String}			iconPlacement	勾选图标的对齐方式,left-左边,right-右边  (默认 'left' )
+	 * @property {Boolean}			borderBottom	placement为row时,是否显示下边框 (默认 false )
+	 * @event {Function}	change	任一个checkbox状态发生变化时触发,回调为一个对象
+	 * @event {Function}	input	修改通过v-model绑定的值时触发,回调为一个对象
+	 * @example <u-checkbox-group></u-checkbox-group>
+	 */
+	export default {
+		name: 'u-checkbox-group',
+		mixins: [mpMixin, mixin,props],
+		computed: {
+			// 这里computed的变量,都是子组件u-checkbox需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化
+			// 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(u-checkbox-group)
+			// 拉取父组件新的变化后的参数
+			parentData() {
+			  return [
+				// #ifdef VUE2
+				this.value,
+				// #endif
+				// #ifdef VUE3
+				this.modelValue,
+				// #endif
+				this.disabled,
+				this.inactiveColor,
+				this.activeColor,
+				this.size,
+				this.labelDisabled,
+				this.shape,
+				this.iconSize,
+				this.borderBottom,
+				this.placement,
+			  ];
+			},
+			bemClass() {
+				// this.bem为一个computed变量,在mixin中
+				return this.bem('checkbox-group', ['placement'])
+			},
+		},
+		watch: {
+			// 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+			parentData: {
+			  handler() {
+				if (this.children.length) {
+				  this.children.map((child) => {
+					// 判断子组件(u-checkbox)如果有init方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+					typeof child.init === "function" && child.init();
+				  });
+				}
+			  },
+			  deep: true,
+			},
+		},
+		data() {
+			return {
+
+			}
+		},
+		created() {
+			this.children = []
+		},
+		// #ifdef VUE3
+		emits: ['update:modelValue', 'change'],
+		// #endif
+		methods: {
+			// 将其他的checkbox设置为未选中的状态
+			unCheckedOther(childInstance) {
+				const values = []
+				this.children.map(child => {
+					// 将被选中的checkbox,放到数组中返回
+					if (child.isChecked) {
+						values.push(child.name)
+					}
+				})
+			
+				// 修改通过v-model绑定的值
+				// #ifdef VUE3
+				this.$emit("update:modelValue", values);
+				// #endif
+				// #ifdef VUE2
+				this.$emit("input", values);
+				// #endif
+        // 放在最后更新,否则change事件传出去的values不会更新
+				this.$emit('change', values)
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-checkbox-group {
+
+		&--row {
+			/* #ifndef APP-NVUE */
+			display: flex;
+			/* #endif */
+			flex-flow: row wrap;
+		}
+
+		&--column {
+			@include flex(column);
+		}
+	}
+</style>

+ 27 - 0
uni_modules/uview-plus/components/u-checkbox/checkbox.js

@@ -0,0 +1,27 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-23 21:06:59
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/checkbox.js
+ */
+export default {
+    // checkbox组件
+    checkbox: {
+        name: '',
+        shape: '',
+        size: '',
+        checkbox: false,
+        disabled: '',
+        activeColor: '',
+        inactiveColor: '',
+        iconSize: '',
+        iconColor: '',
+        label: '',
+        labelSize: '',
+        labelColor: '',
+        labelDisabled: ''
+    }
+}

+ 76 - 0
uni_modules/uview-plus/components/u-checkbox/props.js

@@ -0,0 +1,76 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // checkbox的名称
+        name: {
+            type: [String, Number, Boolean],
+            default: () => defProps.checkbox.name
+        },
+        // 形状,square为方形,circle为圆型
+        shape: {
+            type: String,
+            default: () => defProps.checkbox.shape
+        },
+        // 整体的大小
+        size: {
+            type: [String, Number],
+            default: () => defProps.checkbox.size
+        },
+        // 是否默认选中
+        checked: {
+            type: Boolean,
+            default: () => defProps.checkbox.checked
+        },
+        // 是否禁用
+        disabled: {
+            type: [String, Boolean],
+            default: () => defProps.checkbox.disabled
+        },
+        // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+        activeColor: {
+            type: String,
+            default: () => defProps.checkbox.activeColor
+        },
+        // 未选中的颜色
+        inactiveColor: {
+            type: String,
+            default: () => defProps.checkbox.inactiveColor
+        },
+        // 图标的大小,单位px
+        iconSize: {
+            type: [String, Number],
+            default: () => defProps.checkbox.iconSize
+        },
+        // 图标颜色
+        iconColor: {
+            type: String,
+            default: () => defProps.checkbox.iconColor
+        },
+        // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
+        label: {
+            type: [String, Number],
+            default: () => defProps.checkbox.label
+        },
+        // label的字体大小,px单位
+        labelSize: {
+            type: [String, Number],
+            default: () => defProps.checkbox.labelSize
+        },
+        // label的颜色
+        labelColor: {
+            type: String,
+            default: () => defProps.checkbox.labelColor
+        },
+        // 是否禁止点击提示语选中复选框
+        labelDisabled: {
+            type: [String, Boolean],
+            default: () => defProps.checkbox.labelDisabled
+        },
+		// 是否独立使用
+        usedAlone: {
+            type: [Boolean],
+            default: () => false
+        }
+    }
+})

+ 388 - 0
uni_modules/uview-plus/components/u-checkbox/u-checkbox.vue

@@ -0,0 +1,388 @@
+<template>
+	<view
+	    class="u-checkbox cursor-pointer"
+	    :style="[checkboxStyle]"
+	    @tap.stop="wrapperClickHandler"
+	    :class="[`u-checkbox-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'u-border-bottom']"
+	>
+		<view
+		    class="u-checkbox__icon-wrap cursor-pointer"
+		    @tap.stop="iconClickHandler"
+		    :class="iconClasses"
+		    :style="[iconWrapStyle]"
+		>
+			<slot name="icon" :elIconSize="elIconSize" :elIconColor="elIconColor">
+				<u-icon
+				    class="u-checkbox__icon-wrap__icon"
+				    name="checkbox-mark"
+				    :size="elIconSize"
+				    :color="elIconColor"
+				/>
+			</slot>
+		</view>
+		<view class="u-checkbox__label-wrap cursor-pointer" @tap.stop="labelClickHandler">
+			<slot name="label" :label="label" :elDisabled="elDisabled">
+				<text
+					:style="{
+						color: elDisabled ? elInactiveColor : elLabelColor,
+						fontSize: elLabelSize,
+						lineHeight: elLabelSize
+					}"
+				>{{label}}</text>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addStyle, addUnit, deepMerge, formValidate, error } from '../../libs/function/index';
+	import test from '../../libs/function/test';
+	/**
+	 * checkbox  复选框
+	 * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
+	 * @tutorial https://uview-plus.jiangruyi.com/components/checkbox.html
+	 * @property {String | Number | Boolean}	name			checkbox组件的标示符
+	 * @property {String}						shape			形状,square为方形,circle为圆型
+	 * @property {String | Number}				size			整体的大小
+	 * @property {Boolean}						checked			是否默认选中
+	 * @property {String | Boolean}				disabled		是否禁用
+	 * @property {String}						activeColor		选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+	 * @property {String}						inactiveColor	未选中的颜色
+	 * @property {String | Number}				iconSize		图标的大小,单位px
+	 * @property {String}						iconColor		图标颜色
+	 * @property {String | Number}				label			label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
+	 * @property {String}						labelColor 		label的颜色
+	 * @property {String | Number}				labelSize		label的字体大小,px单位
+	 * @property {String | Boolean}				labelDisabled	是否禁止点击提示语选中复选框
+	 * @property {Object}						customStyle		定义需要用到的外部样式
+	 * 
+	 * @event {Function}	change	任一个checkbox状态发生变化时触发,回调为一个对象
+	 * @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox>
+	 */
+	export default {
+		name: "u-checkbox",
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				isChecked: false,
+				// 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式
+				// 故只能使用如此方法
+				parentData: {
+					iconSize: 12,
+					labelDisabled: null,
+					disabled: null,
+					shape: 'square',
+					activeColor: null,
+					inactiveColor: null,
+					size: 18,
+					// #ifdef VUE2
+					value: null,
+					// #endif
+					// #ifdef VUE3
+					modelValue: null,
+					// #endif
+					iconColor: null,
+					placement: 'row',
+					borderBottom: false,
+					iconPlacement: 'left'
+				}
+			}
+		},
+		computed: {
+			// 是否禁用,如果父组件u-radios-group禁用的话,将会忽略子组件的配置
+			elDisabled() {
+				return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false;
+			},
+			// 是否禁用label点击
+			elLabelDisabled() {
+				return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled :
+					false;
+			},
+			// 组件尺寸,对应size的值,默认值为21px
+			elSize() {
+				return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21);
+			},
+			// 组件的勾选图标的尺寸,默认12px
+			elIconSize() {
+				return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12);
+			},
+			// 组件选中激活时的颜色
+			elActiveColor() {
+				return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff');
+			},
+			// 组件选未中激活时的颜色
+			elInactiveColor() {
+				return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor :
+					'#c8c9cc');
+			},
+			// label的颜色
+			elLabelColor() {
+				return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266')
+			},
+			// 组件的形状
+			elShape() {
+				return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle');
+			},
+			// label大小
+			elLabelSize() {
+				return addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize :
+					'15'))
+			},
+			elIconColor() {
+				const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor :
+					'#ffffff');
+				// 图标的颜色
+				if (this.elDisabled) {
+					// disabled状态下,已勾选的checkbox图标改为elInactiveColor
+					return this.isChecked ? this.elInactiveColor : 'transparent'
+				} else {
+					return this.isChecked ? iconColor : 'transparent'
+				}
+			},
+			iconClasses() {
+				let classes = []
+				// 组件的形状
+				classes.push('u-checkbox__icon-wrap--' + this.elShape)
+				if (this.elDisabled) {
+					classes.push('u-checkbox__icon-wrap--disabled')
+				}
+				if (this.isChecked && this.elDisabled) {
+					classes.push('u-checkbox__icon-wrap--disabled--checked')
+				}
+				// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+				// #ifdef MP-ALIPAY || MP-TOUTIAO
+				classes = classes.join(' ')
+				// #endif
+				return classes
+			},
+			iconWrapStyle() {
+				// checkbox的整体样式
+				const style = {}
+				style.backgroundColor = this.isChecked && !this.elDisabled ? this.elActiveColor : '#ffffff'
+				style.borderColor = this.isChecked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor
+				style.width = addUnit(this.elSize)
+				style.height = addUnit(this.elSize)
+				// 如果是图标在右边的话,移除它的右边距
+				if (!this.usedAlone) {
+					if (this.parentData.iconPlacement === 'right') {
+						style.marginRight = 0
+					}
+				}
+				return style
+			},
+			checkboxStyle() {
+				const style = {}
+				if (!this.usedAlone) {
+					if (this.parentData.borderBottom && this.parentData.placement === 'row') {
+						error('检测到您将borderBottom设置为true,需要同时将u-checkbox-group的placement设置为column才有效')
+					}
+					// 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔
+					if (this.parentData.borderBottom && this.parentData.placement === 'column') {
+						style.paddingBottom = '8px'
+					}
+				}
+				return deepMerge(style, addStyle(this.customStyle))
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		emits: ["change", "update:checked"],
+		methods: {
+			init() {
+				if (!this.usedAlone) {
+					// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
+					this.updateParentData()
+					if (!this.parent) {
+						error('u-checkbox必须搭配u-checkbox-group组件使用')
+					}
+					// #ifdef VUE2
+					const value = this.parentData.value
+					// #endif
+					// #ifdef VUE3
+					const value = this.parentData.modelValue
+					// #endif
+					// 设置初始化时,是否默认选中的状态,父组件u-checkbox-group的value可能是array,所以额外判断
+					if (this.checked) {
+						this.isChecked = true
+					} else if (!this.usedAlone && test.array(value)) {
+						// 查找数组是是否存在this.name元素值
+						this.isChecked = value.some(item => {
+							return item === this.name
+						})
+					}
+				} else {
+					if (this.checked) {
+						this.isChecked = true
+					}
+				}
+			},
+			updateParentData() {
+				this.getParentData('u-checkbox-group')
+			},
+			// 横向两端排列时,点击组件即可触发选中事件
+			wrapperClickHandler(e) {
+				if (!this.usedAlone) {
+					this.parentData.iconPlacement === 'right' && this.iconClickHandler(e)
+				} else {
+					this.iconClickHandler(e)
+				}
+			},
+			// 点击图标
+			iconClickHandler(e) {
+				this.preventEvent(e)
+				// 如果整体被禁用,不允许被点击
+				if (!this.elDisabled) {
+					this.setRadioCheckedStatus()
+				}
+			},
+			// 点击label
+			labelClickHandler(e) {
+				this.preventEvent(e)
+				// 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态
+				if (!this.elLabelDisabled && !this.elDisabled) {
+					this.setRadioCheckedStatus()
+				}
+			},
+			emitEvent() {
+				this.$emit('change', this.isChecked, {
+					name: this.name
+				})
+				// 双向绑定
+				if (this.usedAlone) {
+					this.$emit('update:checked', this.isChecked)
+				}
+				// 尝试调用u-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时
+				this.$nextTick(() => {
+					formValidate(this, 'change')
+				})
+			},
+			// 改变组件选中状态
+			// 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有u-checkbox实例
+			// 将本组件外的其他u-checkbox的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态
+			setRadioCheckedStatus() {
+				// 将本组件标记为与原来相反的状态
+				this.isChecked = !this.isChecked
+				this.emitEvent()
+				if (!this.usedAlone) {
+					typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this)
+				}
+			}
+		},
+		watch:{
+			checked(newValue, oldValue){
+				if (newValue !== this.isChecked) {
+					this.isChecked = newValue
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$u-checkbox-icon-wrap-margin-right:6px !default;
+	$u-checkbox-icon-wrap-font-size:6px !default;
+	$u-checkbox-icon-wrap-border-width:1px !default;
+	$u-checkbox-icon-wrap-border-color:#c8c9cc !default;
+	$u-checkbox-icon-wrap-icon-line-height:0 !default;
+	$u-checkbox-icon-wrap-circle-border-radius:100% !default;
+	$u-checkbox-icon-wrap-square-border-radius:3px !default;
+	$u-checkbox-icon-wrap-checked-color:#fff !default;
+	$u-checkbox-icon-wrap-checked-background-color:red !default;
+	$u-checkbox-icon-wrap-checked-border-color:#2979ff !default;
+	$u-checkbox-icon-wrap-disabled-background-color:#ebedf0 !default;
+	$u-checkbox-icon-wrap-disabled-checked-color:#c8c9cc !default;
+	$u-checkbox-label-margin-left:5px !default;
+	$u-checkbox-label-margin-right:12px !default;
+	$u-checkbox-label-color:$u-content-color !default;
+	$u-checkbox-label-font-size:15px !default;
+	$u-checkbox-label-disabled-color:#c8c9cc !default;
+
+	.u-checkbox {
+		/* #ifndef APP-NVUE */
+		@include flex(row);
+		/* #endif */
+		overflow: hidden;
+		flex-direction: row;
+		align-items: center;
+		margin-bottom: 5px;
+		margin-top: 5px;
+
+		&-label--left {
+			flex-direction: row
+		}
+
+		&-label--right {
+			flex-direction: row-reverse;
+			justify-content: space-between
+		}
+
+		&__icon-wrap {
+			/* #ifndef APP-NVUE */
+			box-sizing: border-box;
+			// nvue下,border-color过渡有问题
+			transition-property: border-color, background-color, color;
+			transition-duration: 0.2s;
+			/* #endif */
+			color: $u-content-color;
+			@include flex;
+			align-items: center;
+			justify-content: center;
+			color: transparent;
+			text-align: center;
+			margin-right: $u-checkbox-icon-wrap-margin-right;
+
+			font-size: $u-checkbox-icon-wrap-font-size;
+			border-width: $u-checkbox-icon-wrap-border-width;
+			border-color: $u-checkbox-icon-wrap-border-color;
+			border-style: solid;
+
+			/* #ifdef MP-TOUTIAO */
+			// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
+			&__icon {
+				line-height: $u-checkbox-icon-wrap-icon-line-height;
+			}
+
+			/* #endif */
+
+			&--circle {
+				border-radius: $u-checkbox-icon-wrap-circle-border-radius;
+			}
+
+			&--square {
+				border-radius: $u-checkbox-icon-wrap-square-border-radius;
+			}
+
+			&--checked {
+				color: $u-checkbox-icon-wrap-checked-color;
+				background-color: $u-checkbox-icon-wrap-checked-background-color;
+				border-color: $u-checkbox-icon-wrap-checked-border-color;
+			}
+
+			&--disabled {
+				background-color: $u-checkbox-icon-wrap-disabled-background-color !important;
+			}
+
+			&--disabled--checked {
+				color: $u-checkbox-icon-wrap-disabled-checked-color !important;
+			}
+		}
+
+		&__label {
+			/* #ifndef APP-NVUE */
+			word-wrap: break-word;
+			/* #endif */
+			margin-left: $u-checkbox-label-margin-left;
+			margin-right: $u-checkbox-label-margin-right;
+			color: $u-checkbox-label-color;
+			font-size: $u-checkbox-label-font-size;
+
+			&--disabled {
+				color: $u-checkbox-label-disabled-color;
+			}
+		}
+	}
+</style>

+ 15 - 0
uni_modules/uview-plus/components/u-circle-progress/circleProgress.js

@@ -0,0 +1,15 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:55:02
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/circleProgress.js
+ */
+export default {
+    // circleProgress 组件
+    circleProgress: {
+        percentage: 30
+    }
+}

+ 10 - 0
uni_modules/uview-plus/components/u-circle-progress/props.js

@@ -0,0 +1,10 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        percentage: {
+            type: [String, Number],
+            default: () => defProps.circleProgress.percentage
+        }
+    }
+})

+ 200 - 0
uni_modules/uview-plus/components/u-circle-progress/u-circle-progress.vue

@@ -0,0 +1,200 @@
+<template>
+	<view class="u-circle-progress">
+		<view class="u-circle-progress__left">
+			<view
+			    class="u-circle-progress__left__circle"
+			    :style="[leftSyle]"
+			    ref="left-circle"
+			>
+
+			</view>
+		</view>
+		<view
+		    class="u-circle-progress__right"
+		>
+			<view
+			    class="u-circle-progress__right__circle"
+			    ref="right-circle"
+				:style="[rightSyle]"
+			>
+
+			</view>
+		</view>
+		<view class="u-circle-progress__circle">
+
+		</view>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import {sleep } from '../../libs/function/index';
+	// #ifdef APP-NVUE
+	const animation = uni.requireNativePlugin('animation')
+	// #endif
+	/**
+	 * CircleProgress 圆形进度条 TODO: 待完善 
+	 * @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度环。
+	 * @tutorial https://ijry.github.io/uview-plus/components/circleProgress.html
+	 * @property {String | Number}	percentage	圆环进度百分比值,为数值类型,0-100 (默认 30 )
+	 * @example
+	 */
+	export default {
+		name: 'u-circle-progress',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				leftBorderColor: 'rgb(200, 200, 200)',
+				rightBorderColor: 'rgb(200, 200, 200)',
+			}
+		},
+		computed: {
+			leftSyle() {
+				const style = {}
+				style.borderTopColor = this.leftBorderColor
+				style.borderRightColor = this.leftBorderColor
+				return style
+			},
+			rightSyle() {
+				const style = {}
+				style.borderLeftColor = this.rightBorderColor
+				style.borderBottomColor = this.rightBorderColor
+				return style
+			}
+		},
+		mounted() {
+			sleep().then(() => {
+				this.rightBorderColor = 'rgb(66, 185, 131)'
+				// this.init()
+			})
+		},
+		methods: {
+			init() {
+				animation.transition(this.$refs['right-circle'].ref, {
+					styles: {
+						transform: 'rotate(45deg)',
+						transformOrigin: 'center center'
+					},
+				}, () => {
+					this.rightBorderColor = 'rgb(66, 185, 131)'
+					// animation.transition(this.$refs['right-circle'].ref, {
+					// 	styles: {
+					// 		transform: 'rotate(225deg)',
+					// 		transformOrigin: 'center center'
+					// 	},
+					// 	duration: 3000,
+					// }, () => {
+					// 	animation.transition(this.$refs['left-circle'].ref, {
+					// 		styles: {
+					// 			transform: 'rotate(45deg)',
+					// 			transformOrigin: 'center center'
+					// 		},
+					// 	}, () => {
+					// 		this.leftBorderColor = 'rgb(66, 185, 131)'
+					// 		animation.transition(this.$refs['left-circle'].ref, {
+					// 			styles: {
+					// 				transform: 'rotate(225deg)',
+					// 				transformOrigin: 'center center'
+					// 			},
+					// 			duration: 1500,
+					// 		}, () => {
+
+					// 		})
+					// 	})
+					// })
+				})
+
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-circle-progress {
+		@include flex(row);
+		position: relative;
+		border-radius: 100px;
+		height: 100px;
+		width: 100px;
+		// transform: rotate(0deg);
+		// background-color: rgb(66, 185, 131);
+		background-color: rgb(200, 200, 200);
+		overflow: hidden;
+		justify-content: space-between;
+
+		&__circle {
+			border-radius: 100px;
+			height: 90px;
+			width: 90px;
+			transform: translate(-50%, -50%);
+			background-color: rgb(255, 255, 255);
+			left: 50px;
+			top: 50px;
+			position: absolute;
+		}
+
+		&__left {
+			position: absolute;
+			left: 0;
+			width: 50px;
+			height: 100px;
+			overflow: hidden;
+			box-sizing: border-box;
+			// background-color: rgb(66, 185, 131);
+			// background-color: rgb(200, 200, 200);
+			// transform-origin: left center;
+
+			&__circle {
+				box-sizing: border-box;
+				// background-color: red;
+				border-left-color: transparent;
+				border-bottom-color: transparent;
+				border-top-left-radius: 50px;
+				border-top-right-radius: 50px;
+				border-bottom-right-radius: 50px;
+				// border-left-color: rgb(66, 185, 131);
+				// border-bottom-color: rgb(66, 185, 131);
+				border-top-color: rgb(66, 185, 131);
+				border-right-color: rgb(66, 185, 131);
+				border-width: 5px;
+				width: 100px;
+				height: 100px;
+				transform: rotate(225deg);
+				// border-radius: 100px;
+			}
+		}
+
+		&__right {
+			position: absolute;
+			right: 0;
+			width: 50px;
+			height: 100px;
+			overflow: hidden;
+
+			&__circle {
+				position: absolute;
+				right: 0;
+				box-sizing: border-box;
+				// background-color: red;
+				border-top-color: transparent;
+				border-right-color: transparent;
+				border-top-left-radius: 50px;
+				border-bottom-left-radius: 50px;
+				border-bottom-right-radius: 50px;
+				// border-left-color: rgb(66, 185, 131);
+				// border-bottom-color: rgb(66, 185, 131);
+				border-left-color: rgb(200, 200, 200);
+				border-bottom-color: rgb(200, 200, 200);
+				border-width: 5px;
+				width: 100px;
+				height: 100px;
+				transform: rotate(45deg);
+				transform-origin: center center;
+				// border-radius: 100px;
+			}
+		}
+	}
+</style>

+ 161 - 0
uni_modules/uview-plus/components/u-city-locate/u-city-locate.vue

@@ -0,0 +1,161 @@
+<template>
+	<view class="u-city-locate">
+		<up-index-list :indexList="indexList">
+			<template #header>
+				<view class="u-current-city-wrap">
+					<view class="u-current-city-title">定位城市</view>
+					<view class="u-current-city-item" @tap="location">
+						<view class="u-location-city">{{locationCity}}</view>
+					</view>
+				</view>
+			</template>
+			<template :key="index" v-for="(item, index) in cityList">
+				<!-- #ifdef APP-NVUE -->
+				<up-index-anchor :text="indexList[index]"></up-index-anchor>
+				<!-- #endif -->
+				<up-index-item>
+					<!-- #ifndef APP-NVUE -->
+					<up-index-anchor :text="indexList[index]"></up-index-anchor>
+					<!-- #endif -->
+					<view class="hot-city-list" v-if="index == 0">
+						<view class="" v-for="(item1, index1) in item" @tap="selectedCity(item1)">
+							<view class="hot-city-item">{{ item1[nameKey] }}</view>
+						</view>
+					</view>
+					<view v-else class="item-list" v-for="(item1, index1) in item" :key="index1">
+						<view class="list__item" @tap="selectedCity(item1)">
+							<text class="list__item__city-name">{{item1[nameKey]}}</text>
+						</view>
+						<up-line></up-line>
+					</view>
+				</up-index-item>
+			</template>
+			<template #footer>
+				<view class="u-safe-area-inset--bottom">
+					<text class="list__footer"></text>
+				</view>
+			</template>
+		</up-index-list>
+	</view>
+</template>
+
+<script>
+	export default{
+		name: 'u-city-locate',
+		props:{
+			indexList: {
+				type: Array,
+				default: ['🔥']
+			},
+			cityList:{
+				type: Array,
+				default: () => {
+					return [
+						[{
+							name: '北京',
+							value: 'beijing'
+						},
+						{
+							name: '上海',
+							value: 'shanghai'
+						},
+						{
+							name: '广州',
+							value: 'guangzhou'
+						},
+						{
+							name: '深圳',
+							value: 'shenzhen'
+						},
+						{
+							name: '杭州',
+							value: 'hangzhou'
+						}]
+					]
+				}
+			},
+			locationType: {
+				type: String,
+				default: 'wgs84'
+			},
+			currentCity: {
+				type: String,
+				default: ''
+			},
+			nameKey: {
+				type: String,
+				default: 'name'
+			}
+		},
+		computed:{
+		},
+		watch:{
+			currentCity(val) {
+				this.locationCity = val;
+			}
+		},
+		data(){
+			return{
+				locationCity: '定位中....'
+			}
+		},
+		emits: ['location-success', 'select-city'],
+		methods:{
+			// 获取城市
+			selectedCity(city){
+				this.locationCity = city[this.nameKey];
+				this.$emit('select-city', {
+					locationCity: this.locationCity
+				});
+			},
+			// 定位操作
+			location(){
+				let That = this;
+				uni.getLocation({
+				    type: this.locationType,
+					geocode:true,
+				    success(res){
+						console.log(res);
+						That.locationCity = res.address && res.address.city;
+						That.$emit('location-success', {
+							...res,
+							locationCity: That.locationCity
+						});
+				    },
+					fail(){
+						That.locationCity = "定位失败,请点击重试"
+					}
+				});
+			},
+		},
+		// 页面挂载后进行异步操作
+		created(){
+		},
+		mounted(){
+			this.location();
+		}
+	}
+</script>
+
+<style lang="scss">
+	.list__item {
+		padding: 8px 1px;
+	}
+	.u-current-city-title {
+		color: grey;
+		margin-bottom: 5px;
+	}
+	.u-current-city-item {
+		height: 30px;
+	}
+	.hot-city-list {
+		display: flex !important;
+		flex-direction: row !important;
+		padding: 12px 0;
+		.hot-city-item {
+			padding: 6px 12px;
+			margin: 5px;
+			border: 1px solid #ededed;
+		}
+	}
+</style>

+ 29 - 0
uni_modules/uview-plus/components/u-code-input/codeInput.js

@@ -0,0 +1,29 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:55:58
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/codeInput.js
+ */
+export default {
+    // codeInput 组件
+    codeInput: {
+		adjustPosition: true,
+        maxlength: 6,
+        dot: false,
+        mode: 'box',
+        hairline: false,
+        space: 10,
+        value: '',
+        focus: false,
+        bold: false,
+        color: '#606266',
+        fontSize: 18,
+        size: 35,
+        disabledKeyboard: false,
+        borderColor: '#c9cacc',
+		disabledDot: true
+    }
+}

+ 90 - 0
uni_modules/uview-plus/components/u-code-input/props.js

@@ -0,0 +1,90 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+		// 键盘弹起时,是否自动上推页面
+		adjustPosition: {
+			type: Boolean,
+            default: () => defProps.codeInput.adjustPosition
+		},
+        // 最大输入长度
+        maxlength: {
+            type: [String, Number],
+            default: () => defProps.codeInput.maxlength
+        },
+        // 是否用圆点填充
+        dot: {
+            type: Boolean,
+            default: () => defProps.codeInput.dot
+        },
+        // 显示模式,box-盒子模式,line-底部横线模式
+        mode: {
+            type: String,
+            default: () => defProps.codeInput.mode
+        },
+        // 是否细边框
+        hairline: {
+            type: Boolean,
+            default: () => defProps.codeInput.hairline
+        },
+        // 字符间的距离
+        space: {
+            type: [String, Number],
+            default: () => defProps.codeInput.space
+        },
+		// #ifdef VUE3
+		// 预置值
+		modelValue: {
+			type: [String, Number],
+			default: () => defProps.codeInput.value
+		},
+		// #endif
+		// #ifdef VUE2
+		// 预置值
+		value: {
+			type: [String, Number],
+			default: () => defProps.codeInput.value
+		},
+		// #endif
+        // 是否自动获取焦点
+        focus: {
+            type: Boolean,
+            default: () => defProps.codeInput.focus
+        },
+        // 字体是否加粗
+        bold: {
+            type: Boolean,
+            default: () => defProps.codeInput.bold
+        },
+        // 字体颜色
+        color: {
+            type: String,
+            default: () => defProps.codeInput.color
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: () => defProps.codeInput.fontSize
+        },
+        // 输入框的大小,宽等于高
+        size: {
+            type: [String, Number],
+            default: () => defProps.codeInput.size
+        },
+        // 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true
+        disabledKeyboard: {
+            type: Boolean,
+            default: () => defProps.codeInput.disabledKeyboard
+        },
+        // 边框和线条颜色
+        borderColor: {
+            type: String,
+            default: () => defProps.codeInput.borderColor
+        },
+		// 是否禁止输入"."符号
+		disabledDot: {
+			type: Boolean,
+			default: () => defProps.codeInput.disabledDot
+		}
+    }
+})

+ 299 - 0
uni_modules/uview-plus/components/u-code-input/u-code-input.vue

@@ -0,0 +1,299 @@
+<template>
+	<view class="u-code-input">
+		<view
+			class="u-code-input__item"
+			:style="[itemStyle(index)]"
+			v-for="(item, index) in codeLength"
+			:key="index"
+		>
+			<view
+				class="u-code-input__item__dot"
+				v-if="dot && codeArray.length > index"
+			></view>
+			<text
+				v-else
+				:style="{
+					fontSize: addUnit(fontSize),
+					fontWeight: bold ? 'bold' : 'normal',
+					color: color
+				}"
+			>{{codeArray[index]}}</text>
+			<view
+				class="u-code-input__item__line"
+				v-if="mode === 'line'"
+				:style="[lineStyle]"
+			></view>
+			<!-- #ifndef APP-NVUE -->
+			<view v-if="isFocus && codeArray.length === index"
+				:style="{backgroundColor: color}" class="u-code-input__item__cursor"></view>
+			<!-- #endif -->
+			<!-- #ifdef APP-NVUE -->
+			 <view v-if="isFocus && codeArray.length === index"
+			 :style="{backgroundColor: color, opacity: opacity}" class="u-code-input__item__cursor"></view>
+			<!-- #endif -->
+		</view>
+		<input
+			:disabled="disabledKeyboard"
+			type="number"
+			:focus="focus"
+			:value="inputValue"
+			:maxlength="maxlength"
+			:adjustPosition="adjustPosition"
+			class="u-code-input__input"
+			@input="inputHandler"
+			:style="{
+				height: addUnit(size)
+			}"
+			@focus="isFocus = true"
+			@blur="isFocus = false"
+		/>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit, getPx } from '../../libs/function/index';
+	/**
+	 * CodeInput 验证码输入
+	 * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uview-plus的键盘组件使用
+	 * @tutorial https://ijry.github.io/uview-plus/components/codeInput.html
+	 * @property {String | Number}	maxlength			最大输入长度 (默认 6 )
+	 * @property {Boolean}			dot					是否用圆点填充 (默认 false )
+	 * @property {String}			mode				显示模式,box-盒子模式,line-底部横线模式 (默认 'box' )
+	 * @property {Boolean}			hairline			是否细边框 (默认 false )
+	 * @property {String | Number}	space				字符间的距离 (默认 10 )
+	 * @property {String | Number}	value				预置值
+	 * @property {Boolean}			focus				是否自动获取焦点 (默认 false )
+	 * @property {Boolean}			bold				字体和输入横线是否加粗 (默认 false )
+	 * @property {String}			color				字体颜色 (默认 '#606266' )
+	 * @property {String | Number}	fontSize			字体大小,单位px (默认 18 )
+	 * @property {String | Number}	size				输入框的大小,宽等于高 (默认 35 )
+	 * @property {Boolean}			disabledKeyboard	是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true (默认 false )
+	 * @property {String}			borderColor			边框和线条颜色 (默认 '#c9cacc' )
+	 * @property {Boolean}			disabledDot			是否禁止输入"."符号 (默认 true )
+	 * 
+	 * @event {Function}	change	输入内容发生改变时触发,具体见上方说明			value:当前输入的值
+	 * @event {Function}	finish	输入字符个数达maxlength值时触发,见上方说明	value:当前输入的值
+	 * @example	<u-code-input v-model="value4" :focus="true"></u-code-input>
+	 */
+	export default {
+		name: 'u-code-input',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				inputValue: '',
+				isFocus: this.focus,
+				timer: null,
+				opacity: 1
+			}
+		},
+		watch: {
+			// #ifdef VUE2
+			value: {
+				immediate: true,
+				handler(val) {
+					// 转为字符串,超出部分截掉
+					this.inputValue = String(val).substring(0, this.maxlength)
+				}
+			},
+			// #endif
+			// #ifdef VUE3
+			modelValue: {
+				immediate: true,
+				handler(val) {
+					// 转为字符串,超出部分截掉
+					this.inputValue = String(val).substring(0, this.maxlength)
+				}
+			},
+			// #endif
+			isFocus: {
+				handler(val) {
+					// #ifdef APP-NVUE
+					if (val) {
+						this.timer = setInterval(() => {
+							this.opacity = Math.abs(this.opacity - 1)
+						}, 600)
+					} else {
+						clearInterval(this.timer)
+					}
+					// #endif
+				}
+			}
+		},
+		created() {
+			
+		},
+		beforeUnmount() {
+			// #ifdef APP-NVUE
+			clearInterval(this.timer)
+			// #endif
+		},
+		computed: {
+			// 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for
+			codeLength() {
+				return new Array(Number(this.maxlength))
+			},
+			// 循环item的样式
+			itemStyle() {
+				return index => {
+					const style = {
+						width: addUnit(this.size),
+						height: addUnit(this.size)
+					}
+					// 盒子模式下,需要额外进行处理
+					if (this.mode === 'box') {
+						// 设置盒子的边框,如果是细边框,则设置为0.5px宽度
+						style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}`
+						// 如果盒子间距为0的话
+						if (getPx(this.space) === 0) {
+							// 给第一和最后一个盒子设置圆角
+							if (index === 0) {
+								style.borderTopLeftRadius = '3px'
+								style.borderBottomLeftRadius = '3px'
+							}
+							if (index === this.codeLength.length - 1) {
+								style.borderTopRightRadius = '3px'
+								style.borderBottomRightRadius = '3px'
+							}
+							// 最后一个盒子的右边框需要保留
+							if (index !== this.codeLength.length - 1) {
+								style.borderRight = 'none'
+							}
+						}
+					}
+					if (index !== this.codeLength.length - 1) {
+						// 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框
+						style.marginRight = addUnit(this.space)
+					} else {
+						// 最后一个盒子的有边框需要保留
+						style.marginRight = 0
+					}
+
+					return style
+				}
+			},
+			// 将输入的值,转为数组,给item历遍时,根据当前的索引显示数组的元素
+			codeArray() {
+				return String(this.inputValue).split('')
+			},
+			// 下划线模式下,横线的样式
+			lineStyle() {
+				const style = {}
+				style.height = this.hairline ? '2px' : '4px'
+				style.width = addUnit(this.size)
+				// 线条模式下,背景色即为边框颜色
+				style.backgroundColor = this.borderColor
+				return style
+			}
+		},
+		emits: ["change", 'finish', "update:modelValue"],
+		methods: {
+			addUnit,
+			// 监听输入框的值发生变化
+			inputHandler(e) {
+				const value = e.detail.value
+				this.inputValue = value
+				// 是否允许输入“.”符号
+				if(this.disabledDot) {
+					this.$nextTick(() => {
+						this.inputValue = value.replace('.', '')
+					})
+				}
+				// 未达到maxlength之前,发送change事件,达到后发送finish事件
+				this.$emit('change', value)
+				// 修改通过v-model双向绑定的值
+				// #ifdef VUE3
+                this.$emit("update:modelValue", value);
+                // #endif
+                // #ifdef VUE2
+                this.$emit("input", value);
+                // #endif
+				// 达到用户指定输入长度时,发出完成事件
+				if (String(value).length >= Number(this.maxlength)) {
+					this.$emit('finish', value)
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$u-code-input-cursor-width: 1px;
+	$u-code-input-cursor-height: 20px;
+	$u-code-input-cursor-animation-duration: 1s;
+	$u-code-input-cursor-animation-name: u-cursor-flicker;
+
+	.u-code-input {
+		@include flex;
+		position: relative;
+		overflow: hidden;
+
+		&__item {
+			@include flex;
+			justify-content: center;
+			align-items: center;
+			position: relative;
+
+			&__text {
+				font-size: 15px;
+				color: $u-content-color;
+			}
+
+			&__dot {
+				width: 7px;
+				height: 7px;
+				border-radius: 100px;
+				background-color: $u-content-color;
+			}
+
+			&__line {
+				position: absolute;
+				bottom: 0;
+				height: 4px;
+				border-radius: 100px;
+				width: 40px;
+				background-color: $u-content-color;
+			}
+			&__cursor {
+				position: absolute;
+				/* #ifndef APP-NVUE */
+				top: 50%;
+				left: 50%;
+				opacity: 1;
+				transform: translate(-50%,-50%);
+				/* #endif */
+				width: $u-code-input-cursor-width;
+				height: $u-code-input-cursor-height;
+				animation: $u-code-input-cursor-animation-duration u-cursor-flicker infinite;
+			}
+		}
+
+		&__input {
+			// 之所以需要input输入框,是因为有它才能唤起键盘
+			// 这里将它设置为两倍的屏幕宽度,再将左边的一半移出屏幕,为了不让用户看到输入的内容
+			position: absolute;
+			left: -750rpx;
+			width: 1500rpx;
+			top: 0;
+			background-color: transparent;
+			text-align: left;
+		}
+	}
+	
+	/* #ifndef APP-NVUE */
+	@keyframes u-cursor-flicker {
+		0% {
+		    opacity: 0;
+		}
+		50% {
+		    opacity: 1;
+		}
+		100% {
+		    opacity: 0;
+		}
+	}
+	/* #endif */
+
+</style>

+ 21 - 0
uni_modules/uview-plus/components/u-code/code.js

@@ -0,0 +1,21 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:55:27
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/code.js
+ */
+
+export default {
+    // code 组件
+    code: {
+        seconds: 60,
+        startText: '获取验证码',
+        changeText: 'X秒重新获取',
+        endText: '重新获取',
+        keepRunning: false,
+        uniqueKey: ''
+    }
+}

+ 36 - 0
uni_modules/uview-plus/components/u-code/props.js

@@ -0,0 +1,36 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 倒计时总秒数
+        seconds: {
+            type: [String, Number],
+            default: () => defProps.code.seconds
+        },
+        // 尚未开始时提示
+        startText: {
+            type: String,
+            default: () => defProps.code.startText
+        },
+        // 正在倒计时中的提示
+        changeText: {
+            type: String,
+            default: () => defProps.code.changeText
+        },
+        // 倒计时结束时的提示
+        endText: {
+            type: String,
+            default: () => defProps.code.endText
+        },
+        // 是否在H5刷新或各端返回再进入时继续倒计时
+        keepRunning: {
+            type: Boolean,
+            default: () => defProps.code.keepRunning
+        },
+        // 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了
+        uniqueKey: {
+            type: String,
+            default: () => defProps.code.uniqueKey
+        }
+    }
+})

+ 131 - 0
uni_modules/uview-plus/components/u-code/u-code.vue

@@ -0,0 +1,131 @@
+<template>
+	<view class="u-code">
+		<!-- 此组件功能由js完成,无需写html逻辑 -->
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	/**
+	 * Code 验证码输入框
+	 * @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件 不提供界面显示,只提供提示语,由用户将提示语嵌入到具体的场景
+	 * @tutorial https://ijry.github.io/uview-plus/components/code.html
+	 * @property {String | Number}	seconds			倒计时所需的秒数(默认 60 )
+	 * @property {String}			startText		开始前的提示语,见官网说明(默认 '获取验证码' )
+	 * @property {String}			changeText		倒计时期间的提示语,必须带有字母"x",见官网说明(默认 'X秒重新获取' )
+	 * @property {String}			endText			倒计结束的提示语,见官网说明(默认 '重新获取' )
+	 * @property {Boolean}			keepRunning		是否在H5刷新或各端返回再进入时继续倒计时( 默认false )
+	 * @property {String}			uniqueKey		为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了
+	 *
+	 * @event {Function}	change	倒计时期间,每秒触发一次
+	 * @event {Function}	start	开始倒计时触发
+	 * @event {Function}	end		结束倒计时触发
+	 * @example <u-code ref="uCode" @change="codeChange" seconds="20"></u-code>
+	 */
+	export default {
+		name: "u-code",
+		mixins: [mpMixin, mixin,props],
+		data() {
+			return {
+				secNum: this.seconds,
+				timer: null,
+				canGetCode: true, // 是否可以执行验证码操作
+			}
+		},
+		mounted() {
+			this.checkKeepRunning()
+		},
+		watch: {
+			seconds: {
+				immediate: true,
+				handler(n) {
+					this.secNum = n
+				}
+			}
+		},
+		emits: ["start", "end", "change"],
+		methods: {
+			checkKeepRunning() {
+				// 获取上一次退出页面(H5还包括刷新)时的时间戳,如果没有上次的保存,此值可能为空
+				let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp'))
+				if(!lastTimestamp) return this.changeEvent(this.startText)
+				// 当前秒的时间戳
+				let nowTimestamp = Math.floor((+ new Date()) / 1000)
+				// 判断当前的时间戳,是否小于上一次的本该按设定结束,却提前结束的时间戳
+				if(this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) {
+					// 剩余尚未执行完的倒计秒数
+					this.secNum = lastTimestamp - nowTimestamp
+					// 清除本地保存的变量
+					uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp')
+					// 开始倒计时
+					this.start()
+				} else {
+					// 如果不存在需要继续上一次的倒计时,执行正常的逻辑
+					this.changeEvent(this.startText)
+				}
+			},
+			// 开始倒计时
+			start() {
+				// 防止快速点击获取验证码的按钮而导致内部产生多个定时器导致混乱
+				if(this.timer) {
+					clearInterval(this.timer)
+					this.timer = null
+				}
+				this.$emit('start')
+				this.canGetCode = false
+				// 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示
+				this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
+				this.timer = setInterval(() => {
+					if (--this.secNum) {
+						// 用当前倒计时的秒数替换提示字符串中的"x"字母
+						this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
+					} else {
+						clearInterval(this.timer)
+						this.timer = null
+						this.changeEvent(this.endText)
+						this.secNum = this.seconds
+						this.$emit('end')
+						this.canGetCode = true
+					}
+				}, 1000)
+				this.setTimeToStorage()
+			},
+			// 重置,可以让用户再次获取验证码
+			reset() {
+				this.canGetCode = true
+				clearInterval(this.timer)
+				this.secNum = this.seconds
+				this.changeEvent(this.endText)
+			},
+			changeEvent(text) {
+				this.$emit('change', text)
+			},
+			// 保存时间戳,为了防止倒计时尚未结束,H5刷新或者各端的右上角返回上一页再进来
+			setTimeToStorage() {
+				if(!this.keepRunning || !this.timer) return
+				// 记录当前的时间戳,为了下次进入页面,如果还在倒计时内的话,继续倒计时
+				// 倒计时尚未结束,结果大于0;倒计时已经开始,就会小于初始值,如果等于初始值,说明没有开始倒计时,无需处理
+				if(this.secNum > 0 && this.secNum <= this.seconds) {
+					// 获取当前时间戳(+ new Date()为特殊写法),除以1000变成秒,再去除小数部分
+					let nowTimestamp = Math.floor((+ new Date()) / 1000)
+					// 将本该结束时候的时间戳保存起来 => 当前时间戳 + 剩余的秒数
+					uni.setStorage({
+						key: this.uniqueKey + '_$uCountDownTimestamp',
+						data: nowTimestamp + Number(this.secNum)
+					})
+				}
+			}
+		},
+		// 组件销毁的时候,清除定时器,否则定时器会继续存在,系统不会自动清除
+		beforeUnmount() {
+			this.setTimeToStorage()
+			clearTimeout(this.timer)
+			this.timer = null
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 19 - 0
uni_modules/uview-plus/components/u-col/col.js

@@ -0,0 +1,19 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:56:12
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/col.js
+ */
+export default {
+    // col 组件
+    col: {
+        span: 12,
+        offset: 0,
+        justify: 'start',
+        align: 'stretch',
+        textAlign: 'left'
+    }
+}

+ 31 - 0
uni_modules/uview-plus/components/u-col/props.js

@@ -0,0 +1,31 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 占父容器宽度的多少等分,总分为12份
+        span: {
+            type: [String, Number],
+            default: () => defProps.col.span
+        },
+        // 指定栅格左侧的间隔数(总12栏)
+        offset: {
+            type: [String, Number],
+            default: () => defProps.col.offset
+        },
+        // 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)
+        justify: {
+            type: String,
+            default: () => defProps.col.justify
+        },
+        // 垂直对齐方式,可选值为top、center、bottom、stretch
+        align: {
+            type: String,
+            default: () => defProps.col.align
+        },
+        // 文字对齐方式
+        textAlign: {
+            type: String,
+            default: () => defProps.col.textAlign
+        }
+    }
+})

+ 169 - 0
uni_modules/uview-plus/components/u-col/u-col.vue

@@ -0,0 +1,169 @@
+<template>
+	<view
+	    class="u-col"
+		ref="u-col"
+	    :class="[
+			'u-col-' + span
+		]"
+	    :style="[colStyle]"
+	    @tap="clickHandler"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addStyle, addUnit, deepMerge, getPx } from '../../libs/function/index';
+	/**
+	 * CodeInput 栅格系统的列 
+	 * @description 该组件一般用于Layout 布局 通过基础的 12 分栏,迅速简便地创建布局
+	 * @tutorial https://ijry.github.io/uview-plus/components/Layout.html
+	 * @property {String | Number}	span		栅格占据的列数,总12等份 (默认 12 ) 
+	 * @property {String | Number}	offset		分栏左边偏移,计算方式与span相同 (默认 0 ) 
+	 * @property {String}			justify		水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)  (默认 'start' ) 
+	 * @property {String}			align		垂直对齐方式,可选值为top、center、bottom、stretch (默认 'stretch' ) 
+	 * @property {String}			textAlign	文字水平对齐方式 (默认 'left' ) 
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @event {Function}	click	col被点击,会阻止事件冒泡到row
+	 * @example	 <u-col  span="3" offset="3" > <view class="demo-layout bg-purple"></view> </u-col>
+	 */
+	export default {
+		name: 'u-col',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				width: 0,
+				parentData: {
+					gutter: 0
+				},
+				gridNum: 12
+			}
+		},
+		//  微信小程序中 options 选项
+		options: {
+			virtualHost: true // 将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等
+		},
+		computed: {
+			uJustify() {
+				if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify
+				else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify
+				else return this.justify
+			},
+			uAlignItem() {
+				if (this.align == 'top') return 'flex-start'
+				if (this.align == 'bottom') return 'flex-end'
+				else return this.align
+			},
+			colStyle() {
+				const style = {
+					// 这里写成"padding: 0 10px"的形式是因为nvue的需要
+					paddingLeft: addUnit(getPx(this.parentData.gutter)/2),
+					paddingRight: addUnit(getPx(this.parentData.gutter)/2),
+					alignItems: this.uAlignItem,
+					justifyContent: this.uJustify,
+					textAlign: this.textAlign,
+					// #ifndef APP-NVUE
+					// 在非nvue上,使用百分比形式
+					flex: `0 0 ${100 / this.gridNum * this.span}%`,
+					marginLeft: 100 / 12 * this.offset + '%',
+					// #endif
+					// #ifdef APP-NVUE
+					// 在nvue上,由于无法使用百分比单位,这里需要获取父组件的宽度,再计算得出该有对应的百分比尺寸
+					width: addUnit(Math.floor(this.width / this.gridNum * Number(this.span))),
+					marginLeft: addUnit(Math.floor(this.width / this.gridNum * Number(this.offset))),
+					// #endif
+				}
+				return deepMerge(style, addStyle(this.customStyle))
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		emits: ["click"],
+		methods: {
+			async init() {
+				// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
+				this.updateParentData()
+				this.width = await this.parent.getComponentWidth()
+			},
+			updateParentData() {
+				this.getParentData('u-row')
+			},
+			clickHandler(e) {
+				this.$emit('click');
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+
+	.u-col {
+		padding: 0;
+		/* #ifndef APP-NVUE */
+		box-sizing:border-box;
+		/* #endif */
+		/* #ifdef MP */
+		display: block;
+		/* #endif */
+	}
+
+	// nvue下百分比无效
+	/* #ifndef APP-NVUE */
+	.u-col-0 {
+		width: 0;
+	}
+
+	.u-col-1 {
+		width: calc(100%/12);
+	}
+
+	.u-col-2 {
+		width: calc(100%/12 * 2);
+	}
+
+	.u-col-3 {
+		width: calc(100%/12 * 3);
+	}
+
+	.u-col-4 {
+		width: calc(100%/12 * 4);
+	}
+
+	.u-col-5 {
+		width: calc(100%/12 * 5);
+	}
+
+	.u-col-6 {
+		width: calc(100%/12 * 6);
+	}
+
+	.u-col-7 {
+		width: calc(100%/12 * 7);
+	}
+
+	.u-col-8 {
+		width: calc(100%/12 * 8);
+	}
+
+	.u-col-9 {
+		width: calc(100%/12 * 9);
+	}
+
+	.u-col-10 {
+		width: calc(100%/12 * 10);
+	}
+
+	.u-col-11 {
+		width: calc(100%/12 * 11);
+	}
+
+	.u-col-12 {
+		width: calc(100%/12 * 12);
+	}
+
+	/* #endif */
+</style>

+ 31 - 0
uni_modules/uview-plus/components/u-collapse-item/collapseItem.js

@@ -0,0 +1,31 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:56:42
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/collapseItem.js
+ */
+export default {
+    // collapseItem 组件
+    collapseItem: {
+        title: '',
+        value: '',
+        label: '',
+        disabled: false,
+        isLink: true,
+        clickable: true,
+        border: true,
+        align: 'left',
+        name: '',
+        icon: '',
+        duration: 300,
+        showRight: true,
+        titleStyle: {},
+        iconStyle: {},
+		rightIconStyle: {},
+        cellCustomStyle: {},
+        cellCustomClass: ''
+    }
+}

+ 97 - 0
uni_modules/uview-plus/components/u-collapse-item/props.js

@@ -0,0 +1,97 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 标题
+        title: {
+            type: String,
+            default: () => defProps.collapseItem.title
+        },
+        // 标题的样式
+        titleStyle: {
+            type: [Object, String],
+			default: () => {
+				return defProps.collapseItem.titleStyle
+			}
+        },
+        // 标题右侧内容
+        value: {
+            type: String,
+            default: () => defProps.collapseItem.value
+        },
+        // 标题下方的描述信息
+        label: {
+            type: String,
+            default: () => defProps.collapseItem.label
+        },
+        // 是否禁用折叠面板
+        disabled: {
+            type: Boolean,
+            default: () => defProps.collapseItem.disabled
+        },
+        // 是否展示右侧箭头并开启点击反馈
+        isLink: {
+            type: Boolean,
+            default: () => defProps.collapseItem.isLink
+        },
+        // 是否开启点击反馈
+        clickable: {
+            type: Boolean,
+            default: () => defProps.collapseItem.clickable
+        },
+        // 是否显示内边框
+        border: {
+            type: Boolean,
+            default: () => defProps.collapseItem.border
+        },
+        // 标题的对齐方式
+        align: {
+            type: String,
+            default: () => defProps.collapseItem.align
+        },
+        // 唯一标识符
+        name: {
+            type: [String, Number],
+            default: () => defProps.collapseItem.name
+        },
+        // 标题左侧图片,可为绝对路径的图片或内置图标
+        icon: {
+            type: String,
+            default: () => defProps.collapseItem.icon
+        },
+        // 面板展开收起的过渡时间,单位ms
+        duration: {
+            type: Number,
+            default: () => defProps.collapseItem.duration
+        },
+        // 显示右侧图标
+        showRight: {
+            type: Boolean,
+            default: () => defProps.collapseItem.showRight
+        },
+        // 左侧图标样式
+        iconStyle: {
+            type: [Object, String],
+            default: () => {
+				return defProps.collapseItem.iconStyle
+			}
+        },
+        // 右侧箭头图标的样式
+        rightIconStyle: {
+            type: [Object, String],
+            default: () => {
+				return defProps.collapseItem.rightIconStyle
+			}
+        },
+        cellCustomStyle: {
+            type: [Object, String],
+            default: () => {
+				return defProps.collapseItem.cellCustomStyle
+			}
+        },
+        cellCustomClass: {
+            type: String,
+            default: () => defProps.collapseItem.cellCustomClass
+        }
+    }
+})

+ 243 - 0
uni_modules/uview-plus/components/u-collapse-item/u-collapse-item.vue

@@ -0,0 +1,243 @@
+<template>
+	<view class="u-collapse-item">
+		<u-cell
+			:title="$slots.title ? '' : title"
+			:value="value"
+			:label="label"
+			:icon="icon"
+			:isLink="isLink"
+			:clickable="clickable"
+			:border="parentData.border && showBorder"
+			@click="clickHandler"
+			:arrowDirection="expanded ? 'up' : 'down'"
+			:disabled="disabled"
+			:customClass="cellCustomClass"
+			:customStyle="cellCustomStyle"
+		>
+			<!-- 微信小程序不支持,因为微信中不支持 <slot name="title" #title />的写法 -->
+			<template #title>
+				<slot name="title">
+					<text v-if="!$slots.title && title">
+						{{title}}
+					</text>
+				</slot>
+			</template>
+			<template #icon>
+				<slot name="icon">
+					<u-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></u-icon>
+				</slot>
+			</template>
+			<template #value>
+				<slot name="value">
+					<text v-if="!$slots.value && value">
+						{{value}}
+					</text>
+				</slot>
+			</template>
+			<template #right-icon>
+				<template v-if="showRight">
+					<u-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></u-icon>
+					<slot name="right-icon">
+					</slot>
+				</template>
+			</template>
+		</u-cell>
+		<view
+			class="u-collapse-item__content"
+			:animation="animationData"
+			ref="animation"
+		>
+			<view
+				class="u-collapse-item__content__text content-class"
+				:id="elId"
+				:ref="elId"
+			><slot /></view>
+		</view>
+		<u-line v-if="parentData.border"></u-line>
+	</view>
+</template>
+
+<script>
+	import { props } from './props.js';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { nextTick } from 'vue';
+	import { guid, sleep, error } from '../../libs/function/index';
+	import test from '../../libs/function/test';
+	// #ifdef APP-NVUE
+	const animation = uni.requireNativePlugin('animation')
+	const dom = uni.requireNativePlugin('dom')
+	// #endif
+	/**
+	 * collapseItem 折叠面板Item
+	 * @description 通过折叠面板收纳内容区域(搭配u-collapse使用)
+	 * @tutorial https://ijry.github.io/uview-plus/components/collapse.html
+	 * @property {String}			title 		标题
+	 * @property {String}			value 		标题右侧内容
+	 * @property {String}			label 		标题下方的描述信息
+	 * @property {Boolean}			disbled 	是否禁用折叠面板 ( 默认 false )
+	 * @property {Boolean}			isLink 		是否展示右侧箭头并开启点击反馈 ( 默认 true )
+	 * @property {Boolean}			clickable	是否开启点击反馈 ( 默认 true )
+	 * @property {Boolean}			border		是否显示内边框 ( 默认 true )
+	 * @property {String}			align		标题的对齐方式 ( 默认 'left' )
+	 * @property {String | Number}	name		唯一标识符
+	 * @property {String}			icon		标题左侧图片,可为绝对路径的图片或内置图标
+	 * @event {Function}			change 			某个item被打开或者收起时触发
+	 * @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item>
+	 */
+	export default {
+		name: "u-collapse-item",
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				elId: guid(),
+				// uni.createAnimation的导出数据
+				animationData: {},
+				// 是否展开状态
+				expanded: false,
+				// 根据expanded确定是否显示border,为了控制展开时,cell的下划线更好的显示效果,进行一定时间的延时
+				showBorder: false,
+				// 是否动画中,如果是则不允许继续触发点击
+				animating: false,
+				// 父组件u-collapse的参数
+				parentData: {
+					accordion: false,
+					border: false
+				}
+			};
+		},
+		watch: {
+			expanded(n) {
+				clearTimeout(this.timer)
+				this.timer = null
+				// 这里根据expanded的值来进行一定的延时,是为了cell的下划线更好的显示效果
+				this.timer = setTimeout(() => {
+					this.showBorder = n
+				}, n ? 10 : 290)
+			}
+		},
+		mounted() {
+			this.init()
+			// console.log('$slots', this.$slots)
+		},
+		methods: {
+			// 异步获取内容,或者动态修改了内容时,需要重新初始化
+			async init() {
+				// 初始化数据
+				this.updateParentData()
+				if (!this.parent) {
+					return error('u-collapse-item必须要搭配u-collapse组件使用')
+				}
+				const {
+					value,
+					accordion,
+					children = []
+				} = this.parent
+
+				if (accordion) {
+					if (test.array(value)) {
+						return error('手风琴模式下,u-collapse组件的value参数不能为数组')
+					}
+					this.expanded = this.name == value
+				} else {
+					if (!test.array(value) && value !== null) {
+						return error('非手风琴模式下,u-collapse组件的value参数必须为数组')
+					}
+					this.expanded = (value || []).some(item => item == this.name)
+				}
+				// 设置组件的展开或收起状态
+				await nextTick()
+				this.setContentAnimate()
+			},
+			updateParentData() {
+				// 此方法在mixin中
+				this.getParentData('u-collapse')
+			},
+			async setContentAnimate() {
+				// 每次面板打开或者收起时,都查询元素尺寸
+				// 好处是,父组件从服务端获取内容后,变更折叠面板后可以获得最新的高度
+				const rect = await this.queryRect()
+				const height = this.expanded ? rect.height : 0
+				this.animating = true
+				// #ifdef APP-NVUE
+				const ref = this.$refs['animation'].ref
+				animation.transition(ref, {
+					styles: {
+						height: height + 'px'
+					},
+					duration: this.duration,
+					// 必须设置为true,否则会到面板收起或展开时,页面其他元素不会随之调整它们的布局
+					needLayout: true,
+					timingFunction: 'ease-in-out',
+				}, () => {
+					this.animating = false
+				})
+				// #endif
+
+				// #ifndef APP-NVUE
+				const animation = uni.createAnimation({
+					timingFunction: 'ease-in-out',
+				});
+				animation
+					.height(height)
+					.step({
+						duration: this.duration,
+					})
+					.step()
+				// 导出动画数据给面板的animationData值
+				this.animationData = animation.export()
+				// 标识动画结束
+				sleep(this.duration).then(() => {
+					this.animating = false
+				})
+				// #endif
+			},
+			// 点击collapsehead头部
+			clickHandler() {
+				if (this.disabled && this.animating) return
+				// 设置本组件为相反的状态
+				this.parent && this.parent.onChange(this)
+			},
+			// 查询内容高度
+			queryRect() {
+				// #ifndef APP-NVUE
+				// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://ijry.github.io/uview-plus/js/getRect.html
+				// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
+				return new Promise(resolve => {
+					this.$uGetRect(`#${this.elId}`).then(size => {
+						resolve(size)
+					})
+				})
+				// #endif
+
+				// #ifdef APP-NVUE
+				// nvue下,使用dom模块查询元素高度
+				// 返回一个promise,让调用此方法的主体能使用then回调
+				return new Promise(resolve => {
+					dom.getComponentRect(this.$refs[this.elId], res => {
+						resolve(res.size)
+					})
+				})
+				// #endif
+			}
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+
+	.u-collapse-item {
+
+		&__content {
+			overflow: hidden;
+			height: 0;
+
+			&__text {
+				padding: 12px 15px;
+				color: $u-content-color;
+				font-size: 14px;
+				line-height: 18px;
+			}
+		}
+	}
+</style>

+ 17 - 0
uni_modules/uview-plus/components/u-collapse/collapse.js

@@ -0,0 +1,17 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:56:30
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/collapse.js
+ */
+export default {
+    // collapse 组件
+    collapse: {
+        value: null,
+        accordion: false,
+        border: true
+    }
+}

+ 21 - 0
uni_modules/uview-plus/components/u-collapse/props.js

@@ -0,0 +1,21 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number
+        value: {
+            type: [String, Number, Array, null],
+            default: () => defProps.collapse.value
+        },
+        // 是否手风琴模式
+        accordion: {
+            type: Boolean,
+            default: () => defProps.collapse.accordion
+        },
+        // 是否显示外边框
+        border: {
+            type: Boolean,
+            default: () => defProps.collapse.border
+        }
+    }
+})

+ 90 - 0
uni_modules/uview-plus/components/u-collapse/u-collapse.vue

@@ -0,0 +1,90 @@
+<template>
+	<view class="u-collapse">
+		<u-line v-if="border"></u-line>
+		<slot />
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	/**
+	 * collapse 折叠面板 
+	 * @description 通过折叠面板收纳内容区域
+	 * @tutorial https://ijry.github.io/uview-plus/components/collapse.html
+	 * @property {String | Number | Array}	value		当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number
+	 * @property {Boolean}					accordion	是否手风琴模式( 默认 false )
+	 * @property {Boolean}					border		是否显示外边框 ( 默认 true )
+	 * @event {Function}	change 		当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array)
+	 * @example <u-collapse></u-collapse>
+	 */
+	export default {
+		name: "u-collapse",
+		mixins: [mpMixin, mixin,props],
+		watch: {
+			needInit() {
+				this.init()
+			},
+			// 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+			parentData() {
+				if (this.children.length) {
+					this.children.map(child => {
+						// 判断子组件(u-checkbox)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+						typeof(child.updateParentData) === 'function' && child.updateParentData()
+					})
+				}
+			}
+		},
+		created() {
+			this.children = []
+		},
+		computed: {
+			needInit() {
+				// 通过computed,同时监听accordion和value值的变化
+				// 再通过watch去执行init()方法,进行再一次的初始化
+				return [this.accordion, this.value]
+			}
+		},
+		emits: ["open", "close", "change"],
+		methods: {
+			// 重新初始化一次内部的所有子元素
+			init() {
+				this.children.map(child => {
+					child.init()
+				})
+			},
+			/**
+			 * collapse-item被点击时触发,由collapse统一处理各子组件的状态
+			 * @param {Object} target 被操作的面板的实例
+			 */
+			onChange(target) {
+				let changeArr = []
+				this.children.map((child, index) => {
+					// 如果是手风琴模式,将其他的折叠面板收起来
+					if (this.accordion) {
+						child.expanded = child === target ? !target.expanded : false
+						child.setContentAnimate()
+					} else {
+						if(child === target) {
+							child.expanded = !child.expanded
+							child.setContentAnimate()
+						}
+					}
+					// 拼接change事件中,数组元素的状态
+					changeArr.push({
+						// 如果没有定义name属性,则默认返回组件的index索引
+						name: child.name || index,
+						status: child.expanded ? 'open' : 'close'
+					})
+				})
+
+				this.$emit('change', changeArr)
+				this.$emit(target.expanded ? 'open' : 'close', target.name)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 25 - 0
uni_modules/uview-plus/components/u-column-notice/columnNotice.js

@@ -0,0 +1,25 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:57:16
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/columnNotice.js
+ */
+export default {
+    // columnNotice 组件
+    columnNotice: {
+        text: '',
+        icon: 'volume',
+        mode: '',
+        color: '#f9ae3d',
+        bgColor: '#fdf6ec',
+        fontSize: 14,
+        speed: 80,
+        step: false,
+        duration: 1500,
+        disableTouch: true,
+		justifyContent: 'flex-start'
+    }
+}

+ 61 - 0
uni_modules/uview-plus/components/u-column-notice/props.js

@@ -0,0 +1,61 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 显示的内容,字符串
+        text: {
+            type: [Array],
+            default: () => defProps.columnNotice.text
+        },
+        // 是否显示左侧的音量图标
+        icon: {
+            type: String,
+            default: () => defProps.columnNotice.icon
+        },
+        // 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+        mode: {
+            type: String,
+            default: () => defProps.columnNotice.mode
+        },
+        // 文字颜色,各图标也会使用文字颜色
+        color: {
+            type: String,
+            default: () => defProps.columnNotice.color
+        },
+        // 背景颜色
+        bgColor: {
+            type: String,
+            default: () => defProps.columnNotice.bgColor
+        },
+        // 字体大小,单位px
+        fontSize: {
+            type: [String, Number],
+            default: () => defProps.columnNotice.fontSize
+        },
+        // 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度
+        speed: {
+            type: [String, Number],
+            default: () => defProps.columnNotice.speed
+        },
+        // direction = row时,是否使用步进形式滚动
+        step: {
+            type: Boolean,
+            default: () => defProps.columnNotice.step
+        },
+        // 滚动一个周期的时间长,单位ms
+        duration: {
+            type: [String, Number],
+            default: () => defProps.columnNotice.duration
+        },
+        // 是否禁止用手滑动切换
+        // 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序
+        disableTouch: {
+            type: Boolean,
+            default: () => defProps.columnNotice.disableTouch
+        },
+		justifyContent: {
+			type: String,
+			default: () => defProps.columnNotice.justifyContent
+		}
+    }
+})

+ 165 - 0
uni_modules/uview-plus/components/u-column-notice/u-column-notice.vue

@@ -0,0 +1,165 @@
+<template>
+	<view
+		class="u-notice"
+		@tap="clickHandler"
+	>
+		<slot name="icon">
+			<view
+				class="u-notice__left-icon"
+				v-if="icon"
+			>
+				<u-icon
+					:name="icon"
+					:color="color"
+					size="19"
+				></u-icon>
+			</view>
+		</slot>
+		<swiper
+			:disable-touch="disableTouch"
+			:vertical="step ? false : true"
+			circular
+			:interval="duration"
+			:autoplay="true"
+			class="u-notice__swiper"
+			@change="noticeChange"
+		>
+			<swiper-item
+				v-for="(item, index) in text"
+				:key="index"
+				class="u-notice__swiper__item"
+				:style="{'justifyContent': justifyContent}"
+			>
+				<text
+					class="u-notice__swiper__item__text u-line-1"
+					:style="[textStyle]"
+				>{{ item }}</text>
+			</swiper-item>
+		</swiper>
+		<view
+			class="u-notice__right-icon"
+			v-if="['link', 'closable'].includes(mode)"
+		>
+			<u-icon
+				v-if="mode === 'link'"
+				name="arrow-right"
+				:size="17"
+				:color="color"
+			></u-icon>
+			<u-icon
+				v-if="mode === 'closable'"
+				name="close"
+				:size="16"
+				:color="color"
+				@click="close"
+			></u-icon>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import { addUnit, error } from '../../libs/function/index';
+	import test from '../../libs/function/test';
+	/**
+	 * ColumnNotice 滚动通知中的垂直滚动 内部组件
+	 * @description 该组件用于滚动通告场景,是其中的垂直滚动方式
+	 * @tutorial https://ijry.github.io/uview-plus/components/noticeBar.html
+	 * @property {Array}			text 			显示的内容,字符串
+	 * @property {String}			icon 			是否显示左侧的音量图标 ( 默认 'volume' )
+	 * @property {String}			mode 			通告模式,link-显示右箭头,closable-显示右侧关闭图标
+	 * @property {String}			color 			文字颜色,各图标也会使用文字颜色 ( 默认 '#f9ae3d' )
+	 * @property {String}			bgColor 		背景颜色 ( 默认 '#fdf6ec' )
+	 * @property {String | Number}	fontSize		字体大小,单位px  ( 默认 14 )
+	 * @property {String | Number}	speed			水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 ( 默认 80 )
+	 * @property {Boolean}			step			direction = row时,是否使用步进形式滚动 ( 默认 false )
+	 * @property {String | Number}	duration		滚动一个周期的时间长,单位ms ( 默认 1500 )
+	 * @property {Boolean}			disableTouch	是否禁止用手滑动切换   目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序 ( 默认 true )
+	 * @example 
+	 */
+	export default {
+		mixins: [mpMixin, mixin, props],
+		watch: {
+			text: {
+				immediate: true,
+				handler(newValue, oldValue) {
+					if(!test.array(newValue)) {
+						error('noticebar组件direction为column时,要求text参数为数组形式')
+					}
+				}
+			}
+		},
+		computed: {
+			// 文字内容的样式
+			textStyle() {
+				let style = {}
+				style.color = this.color
+				style.fontSize = addUnit(this.fontSize)
+				return style
+			},
+			// 垂直或者水平滚动
+			vertical() {
+				if (this.mode == 'horizontal') return false
+				else return true
+			},
+		},
+		data() {
+			return {
+				index:0
+			}
+		},
+		emits: ["click", "close"],
+		methods: {
+			noticeChange(e){
+				this.index = e.detail.current
+			},
+			// 点击通告栏
+			clickHandler() {
+				this.$emit('click', this.index)
+			},
+			// 点击关闭按钮
+			close() {
+				this.$emit('close')
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+
+	.u-notice {
+		@include flex;
+		align-items: center;
+		justify-content: space-between;
+
+		&__left-icon {
+			align-items: center;
+			margin-right: 5px;
+		}
+
+		&__right-icon {
+			margin-left: 5px;
+			align-items: center;
+		}
+
+		&__swiper {
+			height: 16px;
+			@include flex;
+			align-items: center;
+			flex: 1;
+
+			&__item {
+				@include flex;
+				align-items: center;
+				overflow: hidden;
+
+				&__text {
+					font-size: 14px;
+					color: $u-warning;
+				}
+			}
+		}
+	}
+</style>

+ 70 - 0
uni_modules/uview-plus/components/u-copy/u-copy.vue

@@ -0,0 +1,70 @@
+<template>
+	<view @click="handleClick">
+        <slot>复制</slot>
+    </view>
+</template>
+<script>
+export default {
+    name: "up-copy",
+    props: {
+        content: {
+            type: String,
+            default: ''
+        },
+		alertStyle: {
+			type: String,
+			default: 'toast'
+		},
+		notice: {
+			type: String,
+			default: '复制成功'
+		}
+    },
+	emits: ['success'],
+    methods: {
+        handleClick() {
+            let content = this.content;
+			if (!content) {
+				uni.showToast({
+				    title: '暂无',
+				    icon: 'none',
+				    duration: 2000,
+				});
+				return false;
+			}
+            content = typeof content === 'string' ? content : content.toString() // 复制内容,必须字符串,数字需要转换为字符串
+            /**
+			* 小程序端 和 app端的复制逻辑
+			*/
+			let that = this;
+            uni.setClipboardData({
+                data: content,
+                success: function() {
+					if (that.alertStyle == 'modal') {
+						uni.showModal({
+							title: '提示',
+							content: that.notice
+						});
+					} else {
+						uni.showToast({
+						    title: that.notice,
+						    icon: 'none'
+						});
+					}
+					that.$emit('success');
+                },
+                fail:function(){
+                    uni.showToast({
+                        title: '复制失败',
+                        icon: 'none',
+                        duration:3000,
+                    });
+                }
+            });
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 18 - 0
uni_modules/uview-plus/components/u-count-down/countDown.js

@@ -0,0 +1,18 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 17:11:29
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/countDown.js
+ */
+export default {
+    // u-count-down 计时器组件
+    countDown: {
+        time: 0,
+        format: 'HH:mm:ss',
+        autoStart: true,
+        millisecond: false
+    }
+}

+ 26 - 0
uni_modules/uview-plus/components/u-count-down/props.js

@@ -0,0 +1,26 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 倒计时时长,单位ms
+        time: {
+            type: [String, Number],
+            default: () => defProps.countDown.time
+        },
+        // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒
+        format: {
+            type: String,
+            default: () => defProps.countDown.format
+        },
+        // 是否自动开始倒计时
+        autoStart: {
+            type: Boolean,
+            default: () => defProps.countDown.autoStart
+        },
+        // 是否展示毫秒倒计时
+        millisecond: {
+            type: Boolean,
+            default: () => defProps.countDown.millisecond
+        }
+    }
+})

+ 167 - 0
uni_modules/uview-plus/components/u-count-down/u-count-down.vue

@@ -0,0 +1,167 @@
+<template>
+	<view class="u-count-down">
+		<slot :days="timeData.days" :hours="timeData.hours"
+			:minutes="timeData.minutes" :seconds="timeData.seconds">
+			<text class="u-count-down__text">{{ formattedTime }}</text>
+		</slot>
+	</view>
+</template>
+
+<script>
+	import { props } from './props';
+	import { mpMixin } from '../../libs/mixin/mpMixin';
+	import { mixin } from '../../libs/mixin/mixin';
+	import {
+		isSameSecond,
+		parseFormat,
+		parseTimeData
+	} from './utils';
+	/**
+	 * u-count-down 倒计时
+	 * @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
+	 * @tutorial https://uview-plus.jiangruyi.com/components/countDown.html
+	 * @property {String | Number}	time		倒计时时长,单位ms (默认 0 )
+	 * @property {String}			format		时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒  (默认 'HH:mm:ss' )
+	 * @property {Boolean}			autoStart	是否自动开始倒计时 (默认 true )
+	 * @property {Boolean}			millisecond	是否展示毫秒倒计时 (默认 false )
+	 * @event {Function} finish 倒计时结束时触发 
+	 * @event {Function} change 倒计时变化时触发 
+	 * @event {Function} start	开始倒计时
+	 * @event {Function} pause	暂停倒计时 
+	 * @event {Function} reset	重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时 
+	 * @example <u-count-down :time="time"></u-count-down>
+	 */
+	export default {
+		name: 'u-count-down',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				timer: null,
+				// 各单位(天,时,分等)剩余时间
+				timeData: parseTimeData(0),
+				// 格式化后的时间,如"03:23:21"
+				formattedTime: '0',
+				// 倒计时是否正在进行中
+				runing: false,
+				endTime: 0, // 结束的毫秒时间戳
+				remainTime: 0, // 剩余的毫秒时间
+			}
+		},
+		watch: {
+			time(n) {
+				this.reset()
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		emits: ["change", "finish"],
+		methods: {
+			init() {
+				this.reset()
+			},
+			// 开始倒计时
+			start() {
+				if (this.runing) return
+				// 标识为进行中
+				this.runing = true
+				// 结束时间戳 = 此刻时间戳 + 剩余的时间
+				this.endTime = Date.now() + this.remainTime
+				this.toTick()
+			},
+			// 根据是否展示毫秒,执行不同操作函数
+			toTick() {
+				if (this.millisecond) {
+					this.microTick()
+				} else {
+					this.macroTick()
+				}
+			},
+			macroTick() {
+				this.clearTimeout()
+				// 每隔一定时间,更新一遍定时器的值
+				// 同时此定时器的作用也能带来毫秒级的更新
+				this.timer = setTimeout(() => {
+					// 获取剩余时间
+					const remain = this.getRemainTime()
+					// 重设剩余时间
+					if (!isSameSecond(remain, this.remainTime) || remain === 0) {
+						this.setRemainTime(remain)
+					}
+					// 如果剩余时间不为0,则继续检查更新倒计时
+					if (this.remainTime !== 0) {
+						this.macroTick()
+					}
+				}, 30)
+			},
+			microTick() {
+				this.clearTimeout()
+				this.timer = setTimeout(() => {
+					this.setRemainTime(this.getRemainTime())
+					if (this.remainTime !== 0) {
+						this.microTick()
+					}
+				}, 50)
+			},
+			// 获取剩余的时间
+			getRemainTime() {
+				// 取最大值,防止出现小于0的剩余时间值
+				return Math.max(this.endTime - Date.now(), 0)
+			},
+			// 设置剩余的时间
+			setRemainTime(remain) {
+				this.remainTime = remain
+				// 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象
+				const timeData = parseTimeData(remain)
+				this.timeData = timeData;
+				this.$emit('change', timeData)
+				// 得出格式化后的时间
+				this.formattedTime = parseFormat(this.format, timeData)
+				// 如果时间已到,停止倒计时
+				if (remain <= 0) {
+					this.pause()
+					this.$emit('finish')
+				}
+			},
+			// 重置倒计时
+			reset() {
+				this.pause()
+				this.remainTime = this.time
+				this.setRemainTime(this.remainTime)
+				if (this.autoStart) {
+					this.start()
+				}
+			},
+			// 暂停倒计时
+			pause() {
+				this.runing = false;
+				this.clearTimeout()
+			},
+			// 清空定时器
+			clearTimeout() {
+				clearTimeout(this.timer)
+				this.timer = null
+			}
+		},
+		beforeUnmount() {
+			this.clearTimeout()
+		}
+	}
+</script>
+
+<style
+	lang="scss"
+	scoped
+>
+	$u-count-down-text-color:$u-content-color !default;
+	$u-count-down-text-font-size:15px !default;
+	$u-count-down-text-line-height:22px !default;
+
+	.u-count-down {
+		&__text {
+			color: $u-count-down-text-color;
+			font-size: $u-count-down-text-font-size;
+			line-height: $u-count-down-text-line-height;
+		}
+	}
+</style>

+ 62 - 0
uni_modules/uview-plus/components/u-count-down/utils.js

@@ -0,0 +1,62 @@
+// 补0,如1 -> 01
+function padZero(num, targetLength = 2) {
+    let str = `${num}`
+    while (str.length < targetLength) {
+        str = `0${str}`
+    }
+    return str
+}
+const SECOND = 1000
+const MINUTE = 60 * SECOND
+const HOUR = 60 * MINUTE
+const DAY = 24 * HOUR
+export function parseTimeData(time) {
+    const days = Math.floor(time / DAY)
+    const hours = Math.floor((time % DAY) / HOUR)
+    const minutes = Math.floor((time % HOUR) / MINUTE)
+    const seconds = Math.floor((time % MINUTE) / SECOND)
+    const milliseconds = Math.floor(time % SECOND)
+    return {
+        days,
+        hours,
+        minutes,
+        seconds,
+        milliseconds
+    }
+}
+export function parseFormat(format, timeData) {
+    let {
+        days,
+        hours,
+        minutes,
+        seconds,
+        milliseconds
+    } = timeData
+    // 如果格式化字符串中不存在DD(天),则将天的时间转为小时中去
+    if (format.indexOf('DD') === -1) {
+        hours += days * 24
+    } else {
+        // 对天补0
+        format = format.replace('DD', padZero(days))
+    }
+    // 其他同理于DD的格式化处理方式
+    if (format.indexOf('HH') === -1) {
+        minutes += hours * 60
+    } else {
+        format = format.replace('HH', padZero(hours))
+    }
+    if (format.indexOf('mm') === -1) {
+        seconds += minutes * 60
+    } else {
+        format = format.replace('mm', padZero(minutes))
+    }
+    if (format.indexOf('ss') === -1) {
+        milliseconds += seconds * 1000
+    } else {
+        format = format.replace('ss', padZero(seconds))
+    }
+    return format.replace('SSS', padZero(milliseconds, 3))
+}
+export function isSameSecond(time1, time2) {
+    return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
+}

+ 25 - 0
uni_modules/uview-plus/components/u-count-to/countTo.js

@@ -0,0 +1,25 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-20 16:44:21
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-20 16:57:32
+ * @FilePath     : /u-view2.0/uview-ui/libs/config/props/countTo.js
+ */
+export default {
+    // countTo 组件
+    countTo: {
+        startVal: 0,
+        endVal: 0,
+        duration: 2000,
+        autoplay: true,
+        decimals: 0,
+        useEasing: true,
+        decimal: '.',
+        color: '#606266',
+        fontSize: 22,
+        bold: false,
+        separator: ''
+    }
+}

+ 61 - 0
uni_modules/uview-plus/components/u-count-to/props.js

@@ -0,0 +1,61 @@
+import { defineMixin } from '../../libs/vue'
+import defProps from '../../libs/config/props.js'
+export const props = defineMixin({
+    props: {
+        // 开始的数值,默认从0增长到某一个数
+        startVal: {
+            type: [String, Number],
+            default: () => defProps.countTo.startVal
+        },
+        // 要滚动的目标数值,必须
+        endVal: {
+            type: [String, Number],
+            default: () => defProps.countTo.endVal
+        },
+        // 滚动到目标数值的动画持续时间,单位为毫秒(ms)
+        duration: {
+            type: [String, Number],
+            default: () => defProps.countTo.duration
+        },
+        // 设置数值后是否自动开始滚动
+        autoplay: {
+            type: Boolean,
+            default: () => defProps.countTo.autoplay
+        },
+        // 要显示的小数位数
+        decimals: {
+            type: [String, Number],
+            default: () => defProps.countTo.decimals
+        },
+        // 是否在即将到达目标数值的时候,使用缓慢滚动的效果
+        useEasing: {
+            type: Boolean,
+            default: () => defProps.countTo.useEasing
+        },
+        // 十进制分割
+        decimal: {
+            type: [String, Number],
+            default: () => defProps.countTo.decimal
+        },
+        // 字体颜色
+        color: {
+            type: String,
+            default: () => defProps.countTo.color
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: () => defProps.countTo.fontSize
+        },
+        // 是否加粗字体
+        bold: {
+            type: Boolean,
+            default: () => defProps.countTo.bold
+        },
+        // 千位分隔符,类似金额的分割(¥23,321.05中的",")
+        separator: {
+            type: String,
+            default: () => defProps.countTo.separator
+        }
+    }
+})

Деякі файли не було показано, через те що забагато файлів було змінено