tangning 9 ay önce
ebeveyn
işleme
65febcdfac
100 değiştirilmiş dosya ile 17972 ekleme ve 0 silme
  1. 11 0
      .editorconfig
  2. 4 0
      .eslintignore
  3. 27 0
      .eslintrc.js
  4. 25 0
      .gitee/ISSUE_TEMPLATE.zh-CN.md
  5. 8 0
      .github/ISSUE_TEMPLATE/config.yml
  6. 10 0
      .gitignore
  7. 20 0
      App.vue
  8. 21 0
      LICENSE
  9. 7 0
      common/api.js
  10. 3 0
      common/config.js
  11. 50 0
      common/demo.scss
  12. 7 0
      common/mixin.js
  13. 2 0
      common/props.js
  14. 39 0
      components/echars/china.json
  15. 1 0
      components/echars/echarts.min.js
  16. 112 0
      components/echars/map.vue
  17. 228 0
      components/echars/maps.vue
  18. 113 0
      components/page-nav/page-nav.vue
  19. 146 0
      components/qiyan-qrcode/qiyan-qrcode.vue
  20. 1242 0
      components/qiyan-qrcode/qrcode.js
  21. 145 0
      components/qiyan-qrcode/utils.js
  22. 373 0
      components/tabbar/index.vue
  23. 37 0
      main.js
  24. 152 0
      manifest.json
  25. 1337 0
      package-lock.json
  26. 21 0
      package.json
  27. 37 0
      pages.json
  28. 57 0
      pages/home/code.vue
  29. 366 0
      pages/home/home.vue
  30. 71 0
      pages/home/index.vue
  31. 153 0
      pages/home/map.vue
  32. 206 0
      pages/home/roam.vue
  33. 0 0
      pages/home/scene.vue
  34. 52 0
      pages/home/webview.vue
  35. 225 0
      pages/my/index.vue
  36. 92 0
      pages/prayers/DomData.js
  37. 393 0
      pages/prayers/list.vue
  38. 381 0
      pages/prayers/save.vue
  39. 725 0
      pages/prayers/showlist.vue
  40. 277 0
      pages/prayers/webview.vue
  41. 93 0
      pages/prayers/wx2canvas.js
  42. 2307 0
      pnpm-lock.yaml
  43. 63 0
      store/index.js
  44. 42 0
      template.h5.html
  45. 69 0
      tree.md
  46. 6 0
      uni.scss
  47. 200 0
      uni_modules/lime-echart/changelog.md
  48. 393 0
      uni_modules/lime-echart/components/l-echart/canvas.js
  49. 252 0
      uni_modules/lime-echart/components/l-echart/l-echart.uvue
  50. 514 0
      uni_modules/lime-echart/components/l-echart/l-echart.vue
  51. 51 0
      uni_modules/lime-echart/components/l-echart/nvue.js
  52. 145 0
      uni_modules/lime-echart/components/l-echart/utils.js
  53. 133 0
      uni_modules/lime-echart/components/l-echart/uvue.uts
  54. 159 0
      uni_modules/lime-echart/components/lime-echart/lime-echart.nvue
  55. 160 0
      uni_modules/lime-echart/components/lime-echart/lime-echart.uvue
  56. 226 0
      uni_modules/lime-echart/components/lime-echart/lime-echart.vue
  57. 90 0
      uni_modules/lime-echart/package.json
  58. 406 0
      uni_modules/lime-echart/readme.md
  59. 1 0
      uni_modules/lime-echart/static/ecStat.min.js
  60. 1 0
      uni_modules/lime-echart/static/echarts.min.js
  61. 61 0
      uni_modules/lime-echart/static/echarts.min1.js
  62. 1 0
      uni_modules/lime-echart/static/uni.webview.1.5.5.js
  63. 173 0
      uni_modules/lime-echart/static/uvue.html
  64. 21 0
      uni_modules/uview-ui/LICENSE
  65. 66 0
      uni_modules/uview-ui/README.md
  66. 374 0
      uni_modules/uview-ui/changelog.md
  67. 92 0
      uni_modules/uview-ui/components/u--form/u--form.vue
  68. 73 0
      uni_modules/uview-ui/components/u--image/u--image.vue
  69. 115 0
      uni_modules/uview-ui/components/u--input/u--input.vue
  70. 72 0
      uni_modules/uview-ui/components/u--text/u--text.vue
  71. 85 0
      uni_modules/uview-ui/components/u--textarea/u--textarea.vue
  72. 54 0
      uni_modules/uview-ui/components/u-action-sheet/props.js
  73. 278 0
      uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue
  74. 59 0
      uni_modules/uview-ui/components/u-album/props.js
  75. 259 0
      uni_modules/uview-ui/components/u-album/u-album.vue
  76. 44 0
      uni_modules/uview-ui/components/u-alert/props.js
  77. 243 0
      uni_modules/uview-ui/components/u-alert/u-alert.vue
  78. 52 0
      uni_modules/uview-ui/components/u-avatar-group/props.js
  79. 103 0
      uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue
  80. 78 0
      uni_modules/uview-ui/components/u-avatar/props.js
  81. 173 0
      uni_modules/uview-ui/components/u-avatar/u-avatar.vue
  82. 54 0
      uni_modules/uview-ui/components/u-back-top/props.js
  83. 129 0
      uni_modules/uview-ui/components/u-back-top/u-back-top.vue
  84. 72 0
      uni_modules/uview-ui/components/u-badge/props.js
  85. 171 0
      uni_modules/uview-ui/components/u-badge/u-badge.vue
  86. 46 0
      uni_modules/uview-ui/components/u-button/nvue.scss
  87. 161 0
      uni_modules/uview-ui/components/u-button/props.js
  88. 495 0
      uni_modules/uview-ui/components/u-button/u-button.vue
  89. 80 0
      uni_modules/uview-ui/components/u-button/vue.scss
  90. 99 0
      uni_modules/uview-ui/components/u-calendar/header.vue
  91. 579 0
      uni_modules/uview-ui/components/u-calendar/month.vue
  92. 144 0
      uni_modules/uview-ui/components/u-calendar/props.js
  93. 384 0
      uni_modules/uview-ui/components/u-calendar/u-calendar.vue
  94. 85 0
      uni_modules/uview-ui/components/u-calendar/util.js
  95. 14 0
      uni_modules/uview-ui/components/u-car-keyboard/props.js
  96. 311 0
      uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue
  97. 14 0
      uni_modules/uview-ui/components/u-cell-group/props.js
  98. 61 0
      uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue
  99. 110 0
      uni_modules/uview-ui/components/u-cell/props.js
  100. 0 0
      uni_modules/uview-ui/components/u-cell/u-cell.vue

+ 11 - 0
.editorconfig

@@ -0,0 +1,11 @@
+[*]
+#缩进风格:空格
+indent_style = tab
+#缩进大小2
+indent_size = 4
+#换行符lf
+end_of_line = lf
+#字符集utf-8
+charset = utf-8
+
+

+ 4 - 0
.eslintignore

@@ -0,0 +1,4 @@
+unpackage
+node_modules
+coverage
+static/common/*

+ 27 - 0
.eslintrc.js

@@ -0,0 +1,27 @@
+module.exports = {
+    extends: ['airbnb'],
+    rules: {
+        // 关闭严格模式的提示
+        strict: 0,
+        // 强制4个空格缩进
+        indent: ['error', 4],
+        // 不允许出现分号
+        semi: ['error', 'always'],
+        // 是否检查连续等号赋值
+        'no-multi-assign': 0,
+        // 要求 require() 出现在顶层模块作用域中,禁止它
+        'global-require': 0,
+        // 允许console,覆盖来自airbnb的规则
+        'no-console': 'off',
+        'no-underscore-dangle': 'off',
+        'prefer-rest-params': 'off',
+        // 禁用对象最后一个属性的逗号
+        'comma-dangle': ['error', 'never'],
+        // 可以使用未定义的变量,因为会全局引用uni对象
+        'no-undef': 'off',
+        // 允许lf和crlf文件行尾
+        'linebreak-style': 'off',
+        // 关闭function的jsdoc校验
+        'jsdoc/check-tag-names': 'off'
+    }
+}

+ 25 - 0
.gitee/ISSUE_TEMPLATE.zh-CN.md

@@ -0,0 +1,25 @@
+## bug所属区域(vue页面,nvue页面,文档)
+
+
+## 简单描述
+
+
+## 问题截图
+
+
+## 代码示例
+
+
+## uView版本号
+
+
+## 测试平台(h5,android,ios,微信小程序,其他小程序)
+
+
+## 备注
+
+
+
+或者使用下面的链接创建 issue 以帮助我们更快的排查问题,不规范的 issue 会被关闭,感谢配合。
+
+https://new-issue.uviewui.com/

+ 8 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+    - name: 创建一个新issue
+      url: https://new-issue.uviewui.com/
+      about: 请使用接下来的链接创建新issue。
+    - name: Create new issue
+      url: https://new-issue.uviewui.com/?lang=en
+      about: Please use the following link to create a new issue.

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+/unpackage/dist/build/*
+/unpackage/dist/dev/*
+/unpackage/cache/*
+/unpackage/release/*
+/node_modules/*
+/.idea/*
+deploy.sh
+/.hbuilderx/
+/.vscode/
+deploy

+ 20 - 0
App.vue

@@ -0,0 +1,20 @@
+<script>
+	export default {
+		onLaunch: function() {
+		},
+		onShow: function() {
+			
+		},
+		onHide: function() {
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import '@/uni_modules/windi-css-uniapp/index.scss';
+	@import "@/uni_modules/uview-ui/index.scss";
+	@import "common/demo.scss";
+	
+</style>

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 www.uviewui.com
+
+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.

+ 7 - 0
common/api.js

@@ -0,0 +1,7 @@
+const { http } = uni.$u
+// 获取菜单
+export const wxLogin = (params, config = {}) => http.get('api/show/wx/login', {params}, config)
+export const userInfo = (params, config = {}) => http.get('api/wx/userInfo', {params}, config)
+export const logout = (params, config = {}) => http.get('api/wx/logout', {params}, config)
+export const updateWxUser = (params, config = {}) => http.post('api/wx/updateWxUser', params, config)
+export const fetchMenu = (params, config = {}) => http.post('/ebapi/public_api/index', params, config)

+ 3 - 0
common/config.js

@@ -0,0 +1,3 @@
+module.exports = {
+    baseUrl: 'https://sit-nanhuacs.4dage.com/'
+}

+ 50 - 0
common/demo.scss

@@ -0,0 +1,50 @@
+.u-block{
+	padding: 14px;
+	&__section{
+		margin-bottom:10px;
+	}
+	&__title {
+		margin-top:10px;
+		font-size: 15px;
+		color: $u-content-color;
+		margin-bottom:10px;
+	}
+	&__flex{
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+	}
+}
+
+// 使用了cell组件的icon图片样式
+.u-cell-icon {
+	width: 36rpx;
+	height: 36rpx;
+	margin-right: 8rpx;
+}
+.mypopup {
+	
+}
+.w-full {
+	width: 100%;
+}
+.u-page {
+	padding: 15px 15px 40px 15px;
+}
+
+.u-demo-block {
+	flex: 1;
+	margin-bottom: 23px;
+	
+	&__content {
+		@include flex(column);
+	}
+	
+	&__title {
+		font-size: 14px;
+		color: rgb(143, 156, 162);
+		margin-bottom: 8px;
+		@include flex;
+	}
+}
+

+ 7 - 0
common/mixin.js

@@ -0,0 +1,7 @@
+export default {
+    data() {
+        return {
+
+        }
+    }
+}

+ 2 - 0
common/props.js

@@ -0,0 +1,2 @@
+uni.$u.props.gap.bgColor = '#f3f4f6'
+uni.$u.props.gap.height = '10'

Dosya farkı çok büyük olduğundan ihmal edildi
+ 39 - 0
components/echars/china.json


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
components/echars/echarts.min.js


+ 112 - 0
components/echars/map.vue

@@ -0,0 +1,112 @@
+<template>
+	<view style="height: 750rpx">
+		<l-echart ref="chart"></l-echart>
+	</view>
+</template>
+
+<script>
+	import * as echarts from "./echarts.min.js";
+	export default {
+		data() {
+			return {
+			}
+		},
+		mounted() {
+			this.$refs.chart.init(echarts,async chart => {
+				chart.showLoading()
+				const data = await this.getData()
+				chart.hideLoading()
+				echarts.registerMap('HK', data);
+				const option = {
+					title: {
+						text: 'Population Density of Hong Kong (2011)',
+						sublink:
+						'http://zh.wikipedia.org/wiki/%E9%A6%99%E6%B8%AF%E8%A1%8C%E6%94%BF%E5%8D%80%E5%8A%83#cite_note-12'
+					},
+					tooltip: {
+						trigger: 'item',
+						formatter: '{b}<br/>{c} (p / km2)'
+					},
+					visualMap: {
+						min: 800,
+						max: 50000,
+						text: ['High', 'Low'],
+						realtime: false,
+						calculable: true,
+						inRange: {
+						color: ['lightskyblue', 'yellow', 'orangered']
+						}
+					},
+					series: [
+						{
+						name: '香港18区人口密度',
+						type: 'map',
+						map: 'HK',
+						label: {
+							show: true
+						},
+						data: [
+							{ name: '中西区', value: 20057.34 },
+							{ name: '湾仔', value: 15477.48 },
+							{ name: '东区', value: 31686.1 },
+							{ name: '南区', value: 6992.6 },
+							{ name: '油尖旺', value: 44045.49 },
+							{ name: '深水埗', value: 40689.64 },
+							{ name: '九龙城', value: 37659.78 },
+							{ name: '黄大仙', value: 45180.97 },
+							{ name: '观塘', value: 55204.26 },
+							{ name: '葵青', value: 21900.9 },
+							{ name: '荃湾', value: 4918.26 },
+							{ name: '屯门', value: 5881.84 },
+							{ name: '元朗', value: 4178.01 },
+							{ name: '北区', value: 2227.92 },
+							{ name: '大埔', value: 2180.98 },
+							{ name: '沙田', value: 9172.94 },
+							{ name: '西贡', value: 3368 },
+							{ name: '离岛', value: 806.98 }
+						],
+						// 自定义名称映射
+						nameMap: {
+							'Central and Western': '中西区',
+							Eastern: '东区',
+							Islands: '离岛',
+							'Kowloon City': '九龙城',
+							'Kwai Tsing': '葵青',
+							'Kwun Tong': '观塘',
+							North: '北区',
+							'Sai Kung': '西贡',
+							'Sha Tin': '沙田',
+							'Sham Shui Po': '深水埗',
+							Southern: '南区',
+							'Tai Po': '大埔',
+							'Tsuen Wan': '荃湾',
+							'Tuen Mun': '屯门',
+							'Wan Chai': '湾仔',
+							'Wong Tai Sin': '黄大仙',
+							'Yau Tsim Mong': '油尖旺',
+							'Yuen Long': '元朗'
+						}
+						}
+					]
+				}
+				
+				chart.setOption(option);
+			})
+		},
+		methods: {
+			getData() {
+				return new Promise(resolve => {
+					uni.request({
+						url: 'https://fastly.jsdelivr.net/gh/apache/echarts-website@asf-site/examples/data/asset/geo/HK.json',
+						success(res) {
+							setTimeout(() => {
+								resolve(res.data);
+							}, 2000)
+							
+						}
+					});
+				});
+			}
+		}
+	}
+</script>

+ 228 - 0
components/echars/maps.vue

@@ -0,0 +1,228 @@
+<template>
+  <view
+    style="
+      width: 500px;
+      height: 310px;
+      position: relative;
+      top: 170px;
+      left: -30px;
+    "
+    class="map"
+  >
+    <!--  -->
+    <l-echart style="width: 100%; height: 100%" ref="chart"></l-echart>
+  </view>
+</template>
+
+<script>
+import * as echarts from "./echarts.min.js";
+import china from "./china.json"; // 引入中国地图数据
+export default {
+  data() {
+    return {
+      mapData: [
+        {
+          name: "新疆维吾尔自治区",
+          value: 0,
+        },
+        {
+          name: "西藏自治区",
+          value: 0,
+        },
+        {
+          name: "内蒙古自治区",
+          value: 0,
+        },
+        {
+          name: "青海省",
+          value: 0,
+        },
+        {
+          name: "四川省",
+          value: 0,
+        },
+        {
+          name: "黑龙江省",
+          value: 0,
+        },
+        {
+          name: "甘肃省",
+          value: 0,
+        },
+        {
+          name: "云南省",
+          value: 4,
+        },
+        {
+          name: "广西壮族自治区",
+          value: 40,
+        },
+        {
+          name: "湖南省",
+          value: 89,
+        },
+        {
+          name: "陕西省",
+          value: 40,
+        },
+        {
+          name: "广东省",
+          value: 54,
+        },
+        {
+          name: "吉林省",
+          value: 15,
+        },
+        {
+          name: "河北省",
+          value: 81,
+        },
+        {
+          name: "湖北省",
+          value: 82,
+        },
+        {
+          name: "贵州省",
+          value: 32,
+        },
+        {
+          name: "山东省",
+          value: 11,
+        },
+        {
+          name: "江西省",
+          value: 64,
+        },
+        {
+          name: "河南省",
+          value: 38,
+        },
+        {
+          name: "辽宁省",
+          value: 82,
+        },
+        {
+          name: "山西省",
+          value: 50,
+        },
+        {
+          name: "安徽省",
+          value: 45,
+        },
+        {
+          name: "福建省",
+          value: 76,
+        },
+        {
+          name: "浙江省",
+          value: 30,
+        },
+        {
+          name: "江苏省",
+          value: 9,
+        },
+        {
+          name: "重庆市",
+          value: 91,
+        },
+        {
+          name: "宁夏回族自治区",
+          value: 17,
+        },
+        {
+          name: "海南省",
+          value: 27,
+        },
+        {
+          name: "台湾省",
+          value: 79,
+        },
+        {
+          name: "北京市",
+          value: 40,
+        },
+        {
+          name: "天津市",
+          value: 33,
+        },
+        {
+          name: "上海市",
+          value: 69,
+        },
+        {
+          name: "香港特别行政区",
+          value: 23,
+        },
+        {
+          name: "澳门特别行政区",
+          value: 37,
+        },
+      ],
+    };
+  },
+  mounted() {
+    this.$refs.chart.init(echarts, async (chart) => {
+      chart.showLoading();
+      // const data = await this.getData();
+      chart.hideLoading();
+      console.log("china", china);
+      echarts.registerMap("China", china); // 注册中国地图
+      // echarts.registerMap('HK', data);
+      const option = this.getChartOption();
+      chart.setOption(option);
+    });
+  },
+  methods: {
+    getChartOption() {
+      const option = {
+        title: {
+          text: "",
+        },
+        tooltip: {
+          trigger: "item",
+          // formatter: "{b}<br/>{c} (p / km2)",
+        },
+        visualMap: {
+          min: 0,
+          left: 100,
+          max: 300,
+          show: false,
+          text: ["High", "Low"],
+          realtime: false,
+          calculable: true,
+          pieces: [
+            {
+              gt: 20,
+              label: "疑似",
+              color: "yellow",
+            },
+            {
+              lt: 20,
+              color: "transparent",
+            },
+          ],
+          inRange: {
+            color: ["transparent", "yellow", "yellow"],
+          },
+        },
+        series: [
+          {
+            name: "祈愿人数分布",
+            type: "map",
+            map: "China",
+            // selectedMode: "multiple",
+            data: this.mapData,
+            itemStyle: {
+              normal: {
+                borderColor: "transparent",
+              },
+            },
+          },
+        ],
+      };
+      console.log("option", option);
+      return option;
+    },
+  },
+};
+</script>

+ 113 - 0
components/page-nav/page-nav.vue

@@ -0,0 +1,113 @@
+<template>
+	<view class="nav-wrap">
+		<view class="nav-title">
+			<u--image :showLoading="true" src="https://cdn.uviewui.com/uview/common/logo.png" width="70px"
+				height="70px" />
+			<view class="nav-info">
+				<view class="nav-info__title" @tap="jumpToWx">
+					<text class="nav-info__title__text">uView {{version}}</text>
+					<!-- #ifdef MP-WEIXIN -->
+					<!-- uni-app不支持text内部的text组件的tap事件,所以放到外部响应tap -->
+					<text class="nav-info__title__jump">查看1.x演示</text>
+					<!-- #endif -->
+				</view>
+				<text class="nav-slogan">多平台快速开发的UI框架</text>
+			</view>
+		</view>
+		<text class="nav-desc">{{desc}}</text>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			desc: String,
+			title: String,
+		},
+		data() {
+			return {
+				version: uni.$u.config.v
+			}
+		},
+		methods: {
+			jumpToWx() {
+				// #ifdef MP-WEIXIN
+				uni.navigateToMiniProgram({
+					appId: 'wxc6bdb80f216e918c'
+				})
+				// #endif
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.nav-wrap {
+		padding: 15px;
+		position: relative;
+	}
+
+	.lang {
+		position: absolute;
+		top: 15px;
+		right: 15px;
+	}
+
+	.nav-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: flex-start;
+	}
+
+	.nav-info {
+		margin-left: 15px;
+		
+		&__title {
+			/* #ifndef APP-NVUE */
+			display: flex;
+			/* #endif */
+			flex-direction: row;
+			align-items: center;
+			
+			&__text {
+				/* #ifndef APP-NVUE */
+				display: flex;
+				/* #endif */
+				color: $u-main-color;
+				font-size: 25px;
+				font-weight: bold;
+				text-align: left;
+			}
+			
+			&__jump {
+				font-size: 12px;
+				color: $u-primary;
+				font-weight: normal;
+				margin-left: 20px;
+			}
+		}
+	}
+
+	.logo {
+		width: 70px;
+		height: 70px;
+		/* #ifndef APP-NVUE */
+		height: auto;
+		/* #endif */
+	}
+
+	.nav-slogan {
+		color: $u-tips-color;
+		font-size: 14px;
+	}
+
+	.nav-desc {
+		margin-top: 10px;
+		font-size: 14px;
+		color: $u-content-color;
+		line-height: 20px;
+	}
+</style>

+ 146 - 0
components/qiyan-qrcode/qiyan-qrcode.vue

@@ -0,0 +1,146 @@
+<template>
+	<canvas @click="onClick()" :style="{ width: `${size}px`, height: `${size}px` }" :canvas-id="id" :id="id" type="2d" :hidpi="false"></canvas>
+</template>
+
+<script>
+/**
+ * Qrcode 显示二维码
+ * @description 二维码生成的组件
+ * @property {String} id 多个时需设置不同id
+ * @property {String} text 二维码内容
+ * @property {Number} size 二维码大小
+ * @property {String} background 背景颜色
+ * @property {String} foreground 二维码颜色
+ * @property {String} logo 中间logo资源
+ * @property {String} logoSize logo大小
+ * @property {Boolean} logoRound logo圆角
+ * 
+ * @event {Function} click 点击 Qrcode 触发事件
+ * @example <QiyanQrcode text="321"></QiyanQrcode>
+ */
+import drawQrcode from "./utils.js";
+let canvasObj = {};
+export default {
+	name: 'QiyanQrcode',
+	emits: ['click'],
+	props: {
+		id: {
+			type: String,
+			default: "qrCanvas",
+		},
+		text: {
+			type: String,
+			default: "",
+		},
+		size: {
+			type: Number,
+			default: 200,
+		},
+		background: {
+			type: String,
+			default: "#ffffff",
+		},
+		foreground: {
+			type: String,
+			default: "#000000",
+		},
+		logo: {
+			type: String,
+			default: "",
+		},
+		logoSize: {
+			type: Number,
+			default: 60,
+		},
+		logoRound: {
+			type: Boolean,
+			default: false,
+		},
+	},
+	watch: {
+		text(nVal) {
+			if (nVal) {
+				this.draw();
+			} else {
+				console.error('qrcode text is null');
+			}
+		}
+	},
+	mounted() {
+		this.$nextTick(() => {
+			let _this = this;
+			// #ifdef H5
+			const canvas = document.getElementById(this.id).childNodes[0]
+			canvasObj[this.id] = canvas;
+			_this.draw();
+			// #endif
+			// #ifndef H5
+			const query = uni.createSelectorQuery().in(this);
+			query.select("#" + this.id).fields({
+						node: true,
+						size: true,
+					},
+					(data) => {
+						canvasObj[this.id] = data.node;
+						_this.draw();
+					}
+				)
+				.exec();
+			// #endif
+		})
+	},
+	beforeUnmount() {
+		delete canvasObj[this.id];
+	},
+	methods: {
+		draw() {
+			if (this.text && canvasObj[this.id]) {
+				let canvas = canvasObj[this.id];
+				const drawText = (imageResource) => {
+					let image = {};
+					if (imageResource) {
+						image = {
+							imageResource,
+							width: this.logoSize,
+							height: this.logoSize,
+							round: this.logoRound
+						}
+					}
+					drawQrcode({
+						canvas,
+						canvasId: this.id,
+						width: this.size,
+						height: this.size,
+						padding: 0,
+						background: this.background,
+						foreground: this.foreground,
+						text: this.text,
+						image
+					});
+				}
+
+				setTimeout(() => {
+					if (this.logo) {
+						let img;
+						// #ifdef H5
+						img = new Image();
+						// #endif
+						// #ifndef H5
+						img = canvas.createImage();
+						// #endif
+						img.src = this.logo;
+						img.onload = () => {
+							drawText(img);
+						}
+					} else {
+						drawText();
+					}
+				}, 100);
+			}
+		},
+		onClick() {
+			this.$emit('click');
+		},
+	}
+}
+</script>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1242 - 0
components/qiyan-qrcode/qrcode.js


+ 145 - 0
components/qiyan-qrcode/utils.js

@@ -0,0 +1,145 @@
+// import extend from 'extend'
+// import { extend } from "@/utils/lodash";
+import {
+  QRCode,
+  QRErrorCorrectLevel
+} from './qrcode.js'
+
+// support Chinese
+function utf16to8(str) {
+  var out, i, len, c
+  out = ''
+  len = str.length
+  for (i = 0; i < len; i++) {
+    c = str.charCodeAt(i)
+    if ((c >= 0x0001) && (c <= 0x007F)) {
+      out += str.charAt(i)
+    } else if (c > 0x07FF) {
+      out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F))
+      out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F))
+      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))
+    } else {
+      out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F))
+      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))
+    }
+  }
+  return out
+}
+
+function drawQrcode(options, debug) {
+  options = options || {}
+  options = Object.assign(true, {
+    canvasId: 'myQrcode',
+    // canvas: canvas,
+    text: '爱一个人就要勇敢说出来',
+    width: 260,
+    height: 260,
+    padding: 20,
+    // paddingColor: '#ffffff', // 默认与background一致
+    typeNumber: -1,
+    correctLevel: QRErrorCorrectLevel.H,
+    background: '#ffffff',
+    foreground: '#000000',
+    image: {
+      imageResource: '',
+      width: 80,
+      height: 80,
+      round: true
+    }
+  }, options)
+  if (!options.canvasId && !options.canvas) {
+    console.warn('please set canvasId or canvas!')
+    return
+  }
+
+  if (!options.paddingColor) options.paddingColor = options.background
+
+  if (debug) {
+    var qrcode = new QRCode(options.typeNumber, options.correctLevel)
+    qrcode.addData(utf16to8(options.text))
+    qrcode.make()
+
+    return new Promise(function (resolve, reject) {
+      resolve(qrcode);
+    })
+
+  } else {
+
+    return new Promise(function (resolve, reject) {
+      return resolve(createCanvas());
+    })
+
+  }
+
+  function createCanvas() {
+    // create the qrcode itself
+    var qrcode = new QRCode(options.typeNumber, options.correctLevel)
+    qrcode.addData(utf16to8(options.text))
+    qrcode.make()
+
+    var dpr = 1
+    // #ifdef MP-WEIXIN
+    dpr = wx.getSystemInfoSync().pixelRatio
+    // #endif
+
+    var canvas = options.canvas
+    const ctx = canvas.getContext('2d')
+    canvas.width = options.width * dpr
+    canvas.height = options.width * dpr
+    const width = canvas.width
+
+    // 背景色
+    ctx.fillStyle = options.paddingColor
+    ctx.fillRect(0, 0, width + options.padding * 2, width + options.padding * 2);
+
+    var tileW = (width - options.padding * 2) / qrcode.getModuleCount()
+    var tileH = (width - options.padding * 2) / qrcode.getModuleCount()
+
+    // 开始画二维码
+    for (var row = 0; row < qrcode.getModuleCount(); row++) {
+      for (var col = 0; col < qrcode.getModuleCount(); col++) {
+        ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background
+        var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW))
+        var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW))
+        ctx.fillRect(Math.round(col * tileW) + options.padding, Math.round(row * tileH) + options.padding, w, h);
+      }
+    }
+
+    if (options.image.imageResource) {
+      const imgW = options.image.width * dpr
+      const imgH = options.image.height * dpr
+      const dx = (width - imgW) / 2
+      const dy = (width - imgH) / 2
+      if (options.image.round) {
+        // Logo边框
+        const imgW2 = options.image.width * dpr + 5
+        const dx2 = (width - imgW2) / 2
+        const r2 = imgW2 / 2
+        const cx2 = dx2 + r2;
+        ctx.beginPath();
+        ctx.arc(cx2, cx2, r2, 0, 2 * Math.PI);
+        ctx.fillStyle = '#ffffff'
+        ctx.fill();
+        ctx.restore();
+
+        // 画Logo
+        const r = imgW / 2
+        const cx = dx + r;
+        const cy = dy + r;
+        ctx.beginPath();
+        ctx.arc(cx, cy, r, 0, 2 * Math.PI);
+        ctx.clip();
+
+        ctx.drawImage(options.image.imageResource, dx, dy, imgW, imgW);
+        ctx.restore();
+      } else {
+        ctx.drawImage(options.image.imageResource, dx, dy, imgW, imgH)
+        ctx.restore();
+      }
+    }
+
+    return ctx
+  }
+}
+
+export default drawQrcode

+ 373 - 0
components/tabbar/index.vue

@@ -0,0 +1,373 @@
+<template>
+  <div>
+    <view class="tabbar">
+      <div class="tabbarbg">
+        <u--image
+          src="/static/img/menu_light@2x.png"
+          width="100vw"
+          height="81px"
+        ></u--image>
+      </div>
+      <view
+        class="tabbarItem"
+        @click="handleItem(item, index)"
+        :class="{ activeMenu: active == index }"
+        v-for="(item, index) in list"
+        :key="index"
+      >
+        <div :class="{ aotoImg: index == 2 }">
+          <u--image
+            :src="active == index ? item.active : item.light"
+            :width="index == 2 ? '50px' : '48px'"
+            :height="index == 2 ? '50px' : '48px'"
+          ></u--image>
+        </div>
+        <div class="hightImg" v-show="active == index">
+          <u--image
+            src="https://4dscene.4dage.com/new4dkk/deng/static/img/icon_active@2x.png"
+            width="68px"
+            height="81px"
+          ></u--image>
+        </div>
+        <view class="tabbarItemText" :class="{ active: active == index }">{{
+          item.name
+        }}</view>
+      </view>
+    </view>
+    <u-popup
+      closeIconPos="bottom-right"
+      :show="show"
+      mode="center"
+      round="10"
+      bgColor="transparent"
+      @close="close"
+      @open="open"
+    >
+      <view class="codeData" :style="{backgroundImage: `url(${codeImg})`}">
+        <div class="close" @click="close">
+          <u--image
+            width="42px"
+            height="42px"
+            src="https://4dscene.4dage.com/new4dkk/deng/static/img/icon_cancel@2x.png"
+          ></u--image>
+        </div>
+        <!-- <view class="text">
+          <u--image
+            width="100%"
+            height="185px"
+            src="https://4dscene.4dage.com/new4dkk/v2/lang/images/solutions/government/survey1.png"
+          ></u--image>
+          <div class="text-center pt-20 pb-8 text-2xl">一花五叶</div>
+          <u--image
+            width="100%"
+            height="10px"
+            src="https://4dscene.4dage.com/new4dkk/deng/static/img/line@2x.png"
+          ></u--image>
+          <div class="codeDataText">
+            此灯组以禅宗“一花五叶”为主题,传承禅宗经典。您可欣赏花开五叶的意象,感受南宗禅衍化出临济、曹洞、法眼、沩仰和云门五家,发展壮大的辉煌历史,领悟慧能大师法脉的深邃智慧。
+          </div>
+          <div class="codeImg">
+            <QiyanQrcode
+              @click="title1 = '1.现在时间戳:' + Date.now()"
+              :text="title1"
+            ></QiyanQrcode>
+            <up-qrcode
+              :size="72"
+              val="uview-plus"
+              icon="https://uview-plus.jiangruyi.com/uview-plus/common/logo.png"
+            ></up-qrcode>
+          </div>
+        </view> -->
+        <view class="codebutList">
+          <!-- <div class="downText" v-if="false">
+            <div class="img flex justify-center items-center">
+              <div style="width: 36px; height: 36px">
+                <u--image
+                  width="36px"
+                  height="36px"
+                  src="https://4dscene.4dage.com/new4dkk/deng/static/img/icon_download@2x.png"
+                ></u--image>
+              </div>
+              <div class="pl-10">下载图片</div>
+            </div>
+            <div class="tips">完成6处打卡,即可点灯祈愿</div>
+          </div> -->
+          <div class="downButtom flex justify-center">
+            <div class="downButtomItem">下载图片</div>
+            <div class="downButtomItem" @click="handleqiyan">去祈愿</div>
+          </div>
+        </view>
+      </view>
+    </u-popup>
+  </div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import QiyanQrcode from "@/components/qiyan-qrcode/qiyan-qrcode.vue";
+// import uButton from "uview-ui/components/u-button/u-button.vue";
+export default {
+  components: {
+    QiyanQrcode,
+  },
+  props: {
+    type: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      show: false,
+      title1: "1.现在时间戳:" + Date.now(),
+      codeImg: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+      codeList:{
+        code1: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code2: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code3: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code4: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code5: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code6: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code7: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/02.png',
+        code8: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/01.png',
+        code9: 'https://4dscene.4dage.com/new4dkk/deng/static/img/code/12.png',
+      },
+      list: [
+        {
+          active: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_scan_active@2x.png",
+          dark: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_scan_dark@2x.png",
+          light: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_scan_light@2x.png",
+          name: "扫码打卡",
+        },
+        {
+          active: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_lotus_active@2x.png",
+          dark: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_lotus_dark@2x.png",
+          light: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_lotus_light@2x.png",
+          name: "点亮",
+          url: "https://sit-nanhuacs.4dage.com/web/index.html#/prayers/list",
+        },
+        {
+          active: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_map_active@2x.png",
+          dark: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_map_dark@2x.png",
+          light: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_map_light@2x.png",
+          name: "地图",
+          path: "/pages/home/map",
+          url: "https://sit-nanhuacs.4dage.com/web/index.html#/map",
+        },
+        {
+          active: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_ai_active@2x.png",
+          dark: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_ai_dark@2x.png",
+          light: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_ai_light@2x.png",
+          name: "妙笔生花",
+          url: "https://sit-nanhuacs.4dage.com/web/index.html#/composite",
+        },
+        {
+          active: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_user_active@2x.png",
+          dark: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_user_dark@2x.png",
+          light: "https://4dscene.4dage.com/new4dkk/deng/static/img/icon_user_light@2x.png",
+          name: "我的",
+        },
+      ],
+      title: "Hello",
+    };
+  },
+  computed: {
+    ...mapState(["active"]),
+  },
+  onLoad() {},
+  methods: {
+    handleItem(item, index) {
+      let obj = { 
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=1':this.codeList.code1,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=2':this.codeList.code2,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=3':this.codeList.code3,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=4':this.codeList.code4,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=5':this.codeList.code5,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=6':this.codeList.code6,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=7':this.codeList.code7,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=8':this.codeList.code8,
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=9':this.codeList.code9,
+       };
+       let objclockIn = { 
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=1':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=2':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=3':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=4':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=5':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=6':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=7':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=8':'SG-PHP2F5mD35e',
+        'https://houseoss.4dkankan.com/project/nanHua/index.html#/code?m=9':'SG-PHP2F5mD35e',
+       };
+      let that = this;
+      if (item.name == "扫码打卡") {
+        // 只允许通过相机扫码
+        uni.scanCode({
+          onlyFromCamera: true,
+          success: function (res) {
+            console.log("条码类型:" + res.scanType);
+            console.log("条码内容:" + res.result);
+            that.show = true;
+            that.codeImg = obj[res.result];
+            that.$store.commit("changeActive", index);
+            that.getRequest('/api/wx/clockIn', {code: objclockIn[res.result]})
+          },
+          fail: function (e) {
+            console.log("扫码失败", e);
+            that.$store.commit("changeActive", 4);
+            that.show = true;
+          },
+        });
+        return;
+      } else if (item.name == "我的") {
+      }
+      this.$store.commit("changeActive", index);
+      if (item.url){
+        uni.$u.route("/pages/home/webview", {
+          url: item.url,
+          index: index,
+        });
+      }
+    },
+    handleHome() {
+      console.log("开启云上观灯", uni);
+      uni.$u.route("/pages/home/home");
+    },
+    handleqiyan() {
+        uni.$u.route("/pages/home/webview", {
+          url: 'https://sit-nanhuacs.4dage.com/web/index.html#/prayers/list',
+        });
+    },
+    close() {
+      this.show = false;
+      // console.log('close');
+    },
+    open() {},
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.tabbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  position: fixed;
+  bottom: 0;
+  width: 100vw;
+  z-index: 1000;
+  height: 81px;
+  left: 0;
+  padding: -2px 0 16px 0;
+  .tabbarbg {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+  .tabbarItem {
+    text-align: center;
+    width: 50px;
+    font-weight: 400;
+    font-size: 11px;
+    color: #303030;
+    line-height: 13px;
+    text-align: center;
+    position: relative;
+    z-index: 1;
+    .hightImg {
+      position: absolute;
+      z-index: 2;
+      top: 0;
+      left: -10px;
+    }
+    .aotoImg {
+      position: relative;
+      top: -5px;
+    }
+  }
+}
+.codeData {
+  // background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/pop2@2x.png) no-repeat cover;
+  background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/code1.png)
+    100% 100% no-repeat;
+  background-size: 100% auto;
+  width: calc(100vw - 108px);
+  height: 490px;
+  padding: 48px 52px 30px 36px;
+  position: relative;
+  .close {
+    position: absolute;
+    right: -0px;
+    top: -0px;
+    width: 42px;
+    height: 42px;
+  }
+  .text {
+    font-weight: 400;
+    font-size: 24px;
+    color: #d86332;
+    line-height: 29px;
+    position: relative;
+    padding-bottom: 72px;
+    .codeDataText {
+      padding: 8px 0 16px 0;
+      font-weight: 400;
+      font-size: 14px;
+      color: #b1967b;
+      line-height: 18px;
+      text-align: left;
+    }
+    .codeImg {
+      text-align: right;
+      width: 72px;
+      height: 72px;
+      float: right;
+    }
+  }
+  .codebutList {
+    text-align: center;
+    text-align: center;
+    position: absolute;
+    bottom: -42px;
+    width: 100%;
+    left: 0;
+    margin: 0 auto;
+    .downText {
+      .img {
+        margin-right: 10px;
+        font-weight: 400;
+        font-size: 16px;
+        color: #ffffff;
+        line-height: 19px;
+        text-align: center;
+      }
+      .tips {
+        padding-top: 10px;
+        font-weight: 400;
+        font-size: 16px;
+        color: #a49d94;
+        line-height: 19px;
+        text-align: center;
+      }
+    }
+    .downButtom {
+      .downButtomItem {
+        width: 140px;
+        height: 42px;
+        background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/btn01@2x.png)
+          100% 100% no-repeat;
+        background-size: cover;
+        font-weight: 400;
+        font-size: 16px;
+        color: #ffffff;
+        line-height: 19px;
+        font-style: normal;
+        margin: 0 5px;
+        line-height: 42px;
+        text-align: center;
+      }
+    }
+  }
+}
+</style>

+ 37 - 0
main.js

@@ -0,0 +1,37 @@
+import Vue from 'vue'
+import App from './App'
+
+// vuex
+import store from './store'
+
+// 引入全局uView
+import uView from '@/uni_modules/uview-ui'
+
+import mixin from './common/mixin'
+
+Vue.prototype.$store = store
+Vue.prototype.$root = 'https://4dscene.4dage.com/new4dkk/'
+
+
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+Vue.use(uView)
+
+// #ifdef MP
+// 引入uView对小程序分享的mixin封装
+const mpShare = require('@/uni_modules/uview-ui/libs/mixin/mpShare.js')
+Vue.mixin(mpShare)
+// #endif
+
+Vue.mixin(mixin)
+
+const app = new Vue({
+    store,
+    ...App
+})
+
+// 引入请求封装
+require('./util/request/index')(app)
+
+app.$mount()

+ 152 - 0
manifest.json

@@ -0,0 +1,152 @@
+{
+    "name" : "南华灯会",
+    "appid" : "__UNI__A145573",
+    "description" : "多平台快速开发的UI框架",
+    "versionName" : "2.0.37",
+    "versionCode" : 1,
+    "transformPx" : false,
+    "app-plus" : {
+        // APP-VUE分包,可提升APP启动速度,2.7.12开始支持,兼容微信小程序分包方案,默认关闭
+        "optimization" : {
+            "subPackages" : true
+        },
+        "safearea" : {
+            "bottom" : {
+                "offset" : "none"
+            }
+        },
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "modules" : {
+            "Webview-x5" : {}
+        },
+        "distribute" : {
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a" ]
+            },
+            "ios" : {
+                "idfa" : false
+            },
+            "sdkConfigs" : {
+                "ad" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    "quickapp" : {},
+    "mp-weixin" : {
+        "appid" : "wxc6bdb80f216e918c",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : false,
+            "minified" : false,
+            "postcss" : false
+        },
+        "optimization" : {
+            "subPackages" : true
+        },
+        "usingComponents" : true,
+        "mergeVirtualHostAttributes" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true,
+        "component2" : true
+    },
+    "mp-qq" : {
+        "optimization" : {
+            "subPackages" : true
+        },
+        "appid" : "15646153"
+    },
+    "mp-baidu" : {
+        "usingComponents" : true,
+        "appid" : "17597421"
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true,
+        "appid" : "tt2bc55d78b4ff50bf"
+    },
+    "h5" : {
+        "template" : "template.h5.html",
+        "router" : {
+            "mode" : "history",
+            "base" : ""
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : false
+            }
+        },
+        "title" : "uView UI",
+        "sdkConfigs" : {
+            "maps" : {
+                "qqmap" : {
+                    "key" : ""
+                }
+            }
+        },
+        "domain" : ""
+    }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1337 - 0
package-lock.json


+ 21 - 0
package.json

@@ -0,0 +1,21 @@
+{
+    "id": "qiyan-qrcode",
+    "name": "qrcode",
+    "displayName": "极简的qrcode(二维码)生成",
+    "version": "1.1.0",
+    "description": "qrcode(二维码)显示生成,体积小,不依赖任何库",
+    "keywords": [
+        "qrcode",
+        "二维码"
+    ],
+    "dcloudext": {
+        "category": [
+            "前端组件",
+            "通用组件"
+        ]
+    },
+    "dependencies": {
+        "compression-webpack-plugin": "^11.1.0",
+        "uniapp-qrcode": "^1.0.2"
+    }
+}

+ 37 - 0
pages.json

@@ -0,0 +1,37 @@
+{
+	// "condition": { //模式配置,仅开发期间生效
+	// 	"current": 0, //当前激活的模式(list 的索引项)
+	// 	"list": [{
+	// 		"name": "test", //模式名称
+	// 		"path": "pages/componentsA/test/test", //启动页面,必选
+	// 		"query": "" //启动参数,在页面的onLoad函数里面得到
+	// 	}]
+	// },
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/home/index",
+			"style": {
+				"navigationStyle": "custom",
+				"navigationBarTitleText": "南华灯会"
+			}
+		},{
+			"path": "pages/home/webview",
+			"style": {
+				"navigationStyle": "custom",
+				"navigationBarTitleText": "南华灯会"
+			}
+		},{
+			"path": "pages/my/index",
+			"style": {
+				"navigationStyle": "custom",
+				"navigationBarTitleText": "南华灯会"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uView",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"backgroundColor": "#FFFFFF"
+	}
+}

+ 57 - 0
pages/home/code.vue

@@ -0,0 +1,57 @@
+<template>
+	<view class="content">
+    <u--image :showLoading="true" src="/static/img/img_cover@2x.jpg" width="100vw" height="100vh"></u--image>
+		<view class="fixed bottom-48 w-full text-center">
+      <view  class="absolute" style="left: calc(50% - 112px);bottom: 100px">
+        <u--image src="/static/img/btn_enter@2x.png" width="224px" height="55px"></u--image>
+      </view>
+			<view class="butText text-center" @click="handleHome">开始云游</view>
+		</view>
+	</view>
+</template>
+
+<script>
+// import uButton from "uview-ui/components/u-button/u-button.vue";
+	export default {
+		components: {
+			// uButton
+		},
+		data() {
+			return {
+				title: 'Hello'
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+      handleHome() {
+        console.log('开启云上观灯', uni)
+        uni.$u.route('/pages/home/home')
+		}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		min-height: 100vh;
+	}
+
+	.butText{
+    font-weight: 400;
+    font-size: 24px;
+    color: #FFFFFF;
+    line-height: 36px;
+    text-shadow: 0px 0px 8px #FFF9B1, 0px 0px 27px #FFF9B1, 0px 0px 4px #D86332;
+    font-style: normal;
+    text-transform: none;
+    position: relative;
+    top: -110px;
+    z-index: 10;
+  }
+</style>

+ 366 - 0
pages/home/home.vue

@@ -0,0 +1,366 @@
+<template>
+  <view class="content">
+    <view class="home-img">
+      <div class="homeBg">
+        <scroll-view :scroll-x="true" class="scrollview-box">
+          <div class="img">
+            <u--image
+              height="calc(100vh - 71px)"
+              width="700px"
+              mode="heightFix"
+              src="/static/img/homeBg.jpg"
+            ></u--image>
+          </div>
+          <div class="list">
+            <view
+              class="itme"
+              :class="{ active: item.id == active }"
+              v-for="item in list"
+              :key="item.id"
+              @click.stop="handleItem(item)"
+            >
+              <img
+                class="bg"
+                v-if="item.id == active"
+                src="/static/img/tab_active@2x.png"
+                alt=""
+              />
+              <img
+                class="bg"
+                v-else
+                src="/static/img/tab_normal@2x.png"
+                alt=""
+              />
+              <div class="itmeText">{{ item.title }}</div>
+              <div class="mypopup">
+                <div class="popupImg">
+                  <div class="but">可打卡</div>
+                  <u--image
+                    width="209px"
+                    height="131px"
+                    radius="10"
+                    src="https://4dkk.4dage.com/head/15915816041/head_1722399456050.png"
+                  ></u--image>
+                  <div class="title">{{ item.title }}</div>
+                </div>
+                <div
+                  class="flex justify-evenly butList content-center"
+                  style="line-height: 24px; color: #fff"
+                >
+                  <div class="flex justify-around content-center" @click="handleRoam">
+                    <img src="/static/img/icon_dollhouse_normal@2x.png" />
+                    漫游
+                  </div>
+                  <div class="border"></div>
+                  <div class="flex justify-around content-center">
+                    <img src="/static/img/icon_dollhouse_normal@2x.png" />
+                    导航
+                  </div>
+                </div>
+              </div>
+            </view>
+          </div>
+        </scroll-view>
+        <!-- <div >
+        </div> -->
+      </div>
+    </view>
+    <!-- <view class="fixed bottom-40 flex w-full justify-around" style="">
+      <view class="text-center">
+        点亮祈福
+        <u-icon
+          class="text-center flex-col-reverse"
+          name="scan"
+          color="#2979ff"
+          size="28"
+        ></u-icon>
+      </view>
+
+      <view class="text-center">
+        扫码打卡
+        <u-icon
+          class="text-center flex-col-reverse"
+          name="scan"
+          color="#2979ff"
+          size="28"
+        ></u-icon>
+      </view>
+    </view> -->
+    <tabbar></tabbar>
+    <u-popup
+      closeIconPos="bottom-right"
+      :show="show"
+      mode="center"
+      round="10"
+      :closeable="true"
+      @close="close"
+      @open="open"
+    >
+      <view>
+        <view class="text">
+          <u--image
+            src="https://4dscene.4dage.com/new4dkk/v2/lang/images/solutions/government/survey1.png"
+          ></u--image>
+          <text mt-10>出淤泥而不染,濯清涟而不妖</text>
+        </view>
+        <view class="butList flex justify-around my-10">
+          <u-button
+            @click="handleRoam"
+            style="width: 60px"
+            type="primary"
+            plain
+            icon="car"
+            size="small"
+            shape="circle"
+            text="漫游"
+          ></u-button>
+          <u-button
+            @click="handleMap"
+            style="width: 60px"
+            type="primary"
+            plain
+            icon="map"
+            size="small"
+            shape="circle"
+            text="祈愿地图"
+          ></u-button>
+          <u-button
+            style="width: 60px"
+            type="primary"
+            plain
+            icon="map"
+            size="small"
+            shape="circle"
+            text="导航"
+          ></u-button>
+        </view>
+      </view>
+    </u-popup>
+  </view>
+</template>
+
+<script>
+import tabbar from "components/tabbar/index.vue";
+export default {
+  components: {
+    tabbar,
+  },
+  data() {
+    return {
+      title: "Hello",
+      show: false,
+      active: null,
+      list: [
+        {
+          id: 1,
+          title: "大雄宝殿",
+          style: {},
+        },
+      ],
+    };
+  },
+  onLoad() {},
+  methods: {
+    handleMap() {
+      uni.$u.route("/pages/home/map");
+    },
+    handleRoam() {
+      uni.$u.route("/pages/home/roam");
+    },
+    handleItem(item) {
+      this.active = item.id;
+    },
+    close() {
+      this.show = false;
+      // console.log('close');
+    },
+    open() {},
+  },
+};
+</script>
+
+<style lang="scss">
+.content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 100vh;
+  .right {
+    position: fixed;
+    left: 20px;
+    top: 20px;
+  }
+}
+
+.logo {
+  height: 200rpx;
+  width: 200rpx;
+  margin-top: 200rpx;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: 50rpx;
+}
+
+.text-area {
+  display: flex;
+  justify-content: center;
+  padding-bottom: 50%;
+}
+
+.title {
+  font-size: 36rpx;
+  color: #8f8f94;
+}
+.home-img {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  .homeBg {
+    .scrollview-box {
+      white-space: nowrap; // 滚动必须加的属性
+      width: 100%;
+    }
+
+    .list {
+      .itme {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        border-radius: 50%;
+        text-align: center;
+        line-height: 30px;
+        width: 27px;
+        height: 108px;
+        font-weight: 400;
+        font-size: 14px;
+        color: #fff;
+        // color: #5B472E;
+        line-height: 17px;
+        text-align: center;
+        // letter-spacing: 10px;
+        // vertical-align: middle;
+        .bg {
+          width: 100%;
+          height: 100%;
+          position: absolute;
+          z-index: -1;
+        }
+        .itmeText {
+          writing-mode: vertical-rl;
+          position: relative;
+          left: 18px;
+          top: 22px;
+          // letter-spacing: normal;
+          // display: table-cell;
+          // vertical-align: middle;
+        }
+        .mypopup {
+          display: none;
+          width: 230px;
+          height: 162px;
+          position: absolute;
+          bottom: 108px;
+          background: linear-gradient(
+            158deg,
+            rgba(193, 149, 97, 0.85) 0%,
+            #5b472e 100%
+          );
+          border: 1px solid;
+          left: -95px;
+          border-radius: 15px;
+          .popupImg {
+            letter-spacing: 0;
+            width: 209px;
+            height: 131px;
+            padding: 0 9px;
+            position: relative;
+            top: -10px;
+            border-radius: 10px;
+            overflow: hidden;
+            .title {
+              font-weight: bold;
+              font-size: 14px;
+              color: #ffffff;
+              line-height: 16px;
+              text-align: center;
+              position: absolute;
+              bottom: 10px;
+              left: 20px;
+            }
+            .but {
+              width: 63px;
+              height: 25px;
+              position: absolute;
+              right: 10px;
+              top: 10px;
+              border-radius: 20px 20px 20px 20px;
+              border: 1px solid #fff9b6;
+              text-align: center;
+              color: #fff9b6;
+              line-height: 25px;
+              font-weight: 400;
+              font-size: 12px;
+              color: #fff9b6;
+              z-index: 1;
+            }
+          }
+          .butList {
+            .border {
+              width: 1px;
+              height: 26px;
+              border-radius: 0px 0px 0px 0px;
+              border-left: 1px solid;
+              border-image: linear-gradient(
+                  180deg,
+                  rgba(255, 255, 255, 0),
+                  rgba(255, 255, 255, 1),
+                  rgba(255, 255, 255, 0)
+                )
+                1 1;
+            }
+            img {
+              width: 24px;
+              height: 24px;
+            }
+          }
+        }
+      }
+      .active {
+        color: #5b472e;
+        .mypopup {
+          display: block;
+        }
+      }
+    }
+    .img {
+      height: calc(100vh - 81px);
+      position: relative;
+      z-index: -1;
+      overflow-x: auto;
+      width: 700px;
+      img {
+        height: 100%;
+        position: relative;
+        z-index: -1;
+      }
+    }
+  }
+}
+.u-popup__content__close {
+  right: calc(50% - 4.5px) !important;
+  bottom: -40px !important;
+  z-index: 10;
+  border: 1px solid #fff;
+  border-radius: 20px;
+  padding: 5px;
+  color: #fff;
+  .u-icon__icon {
+    color: #fff !important;
+  }
+}
+</style>

+ 71 - 0
pages/home/index.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="content">
+    <u--image :showLoading="true" src="https://4dscene.4dage.com/new4dkk/deng/static/img/img_cover@2x.jpg" width="100vw" height="100vh"></u--image>
+		<view class="fixed bottom-48 w-full text-center">
+      <view  class="absolute" style="left: calc(50% - 112px);bottom: 100px">
+        <u--image src="https://4dscene.4dage.com/new4dkk/deng/static/img/btn_enter@2x.png" width="224px" height="55px"></u--image>
+      </view>
+			<view class="butText text-center" @click="handleHome">开始云游</view>
+		</view>
+	</view>
+</template>
+
+<script>
+// import uButton from "uview-ui/components/u-button/u-button.vue";
+import { wxLogin } from "@/common/api.js";
+	export default {
+		components: {
+			// uButton
+		},
+		data() {
+			return {
+				title: 'Hello'
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+      handleHome() {
+        console.log('开启云上观灯', uni)
+				this.$store.commit("wxLogininfo", wxLogin);
+			// 	uni.login({
+			// 		provider: 'weixin', //使用微信登录
+			// 		success: function ({code}) {
+			// 			console.log('loginRes', code);
+			// 			wxLogin({
+			// 				code: code
+			// 			}).then(res => {
+      //           console.log('wxLoginRes', res, code);
+			// 					uni.$u.toast("登录成功");
+			// 					uni.setStorageSync('token', res.token);
+			// 		})
+			// 	}
+			// });
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		min-height: 100vh;
+	}
+
+	.butText{
+    font-weight: 400;
+    font-size: 24px;
+    color: #FFFFFF;
+    line-height: 36px;
+    text-shadow: 0px 0px 8px #FFF9B1, 0px 0px 27px #FFF9B1, 0px 0px 4px #D86332;
+    font-style: normal;
+    text-transform: none;
+    position: relative;
+    top: -110px;
+    z-index: 10;
+  }
+</style>

+ 153 - 0
pages/home/map.vue

@@ -0,0 +1,153 @@
+<template>
+  <div class="homeMap">
+    <view class="map">
+      <scroll-view :scroll-x="true" class="scrollview-box">
+        <div class="chinaMap" v-if="show">
+          <echarsMap></echarsMap>
+        </div>
+        <div class="allMap" v-else>
+          <div class="goChina" @click="show = !show"></div>
+        </div>
+      </scroll-view>
+    </view>
+    <div class="people">
+      <u--image
+        height="30px"
+        width="30px"
+        mode="heightFix"
+        src="/static/img/icon_lotus_dark@2x.png"
+      ></u--image>
+      <text class="peopleNum">祈愿人数</text>
+      <text class="peopleNum">{{ people }}</text>
+    </div>
+    <div class="bottom flex justify-between items-end">
+      <div class="list">
+        <div class="listItem" v-for="item in 6" :key="item">
+          用户名
+          <text style="color: #7EE3DE">【河北】</text>
+          打卡!
+        </div>
+      </div>
+      <div class="qiyuan">
+        <u--image
+              height="70px"
+              width="70px"
+              mode="heightFix"
+              src="/static/img/btn_wish@2x.png"
+            ></u--image>
+      </div>
+    </div>
+    <tabbar></tabbar>
+  </div>
+</template>
+
+<script>
+import tabbar from "components/tabbar/index.vue";
+import echarsMap from "components/echars/maps.vue";
+export default {
+  components: {
+    tabbar,
+    echarsMap,
+  },
+  data() {
+    return {
+      show: true,
+      people: 15124,
+    };
+  },
+  onReady() {},
+  methods: {
+    handleHome() {
+      console.log("开启云上观灯", uni);
+      uni.$u.route("/pages/home/home");
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.homeMap {
+  width: 100%;
+  height: 100vh;
+  position: relative;
+  background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/bg03@2x.png)
+    100% 100% no-repeat;
+  background-size: cover;
+  .bottom {
+    width: calc(100% - 47px);
+    position: absolute;
+    bottom: 105px;
+    left: 0;
+    height: 25vh;
+    padding: 0 27px 0 20px;
+    .list{
+      .listItem{
+        padding: 6px 10px;
+        background: rgba(0,0,0,0.3);
+        border-radius: 50px;
+        margin-top: 10px;
+        font-weight: 400;
+        font-size: 12px;
+        color: #FFFFFF;
+        line-height: 14px;
+        text-align: center;
+        font-style: normal;
+      }
+    }
+  }
+  .people {
+    position: absolute;
+    height: 35px;
+    padding: 0 5px;
+    background: rgba(0,0,0,0.5);
+    right: 0;
+    top: 125px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-radius: 20px 0 0 20px;
+    .peopleNum {
+      font-size: 24rpx;
+      color: #fff;
+      margin-right: 8px;
+    }
+  }
+  .map {
+    width: 100%;
+    height: 100%;
+    .scrollview-box {
+      white-space: nowrap; // 滚动必须加的属性
+      width: 100%;
+    }
+    .chinaMap {
+      overflow: hidden;
+      width: 100vw;
+      height: 100vh;
+      background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/map@2x.png)
+        100% 100% no-repeat;
+        // background-position: -1281px -397px;
+        background-position: -1262px -383px;
+        background-size: 1950px 1334px;
+    }
+    .allMap {
+      width: 384px;
+      height: calc(100vh - 358px);
+      background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/map@2x.png)
+        100% 55% no-repeat;
+      background-position-y: 47px;
+      background-position-x: -231px;
+      background-size: 100vh 1097px;
+      background-size: cover;
+      position: relative;
+      // background-position: -1758px -600px;
+      .goChina {
+        width: 100px;
+        height: 100px;
+        position: absolute;
+        left: 240px;
+        top: 236px;
+      }
+    }
+  }
+}
+</style>

+ 206 - 0
pages/home/roam.vue

@@ -0,0 +1,206 @@
+<template>
+  <view class="content">
+    <view class="home-img">
+      <!-- <web-view class="relative z-0" :src="weburl" ></web-view> -->
+    </view>
+    <u-popup :show="show" mode="center" round="10" @close="close" @open="open">
+        <view class="mySharepopup">
+          <div class="close" @click="close">
+            <u--image width="42px" height="42px" src="/static/img/icon_cancel@2x.png"></u--image>
+          </div>
+          <view class="text">
+              <u--image width="295px" height="450px" src="/static/img/img_cover@2x.jpg"></u--image>
+            <div class="flex justify-between shareImg">
+              <div class="code">
+                <u--image width="66px" height="66px" src="/static/img/icon_scan_active@2x.png"></u--image>
+              </div>
+              <div class="zz">
+                <u--image width="122px" height="113px" src="/static/img/insignia@2x.png"></u--image>
+              </div>
+            </div>
+          </view>
+          <view class="bottom">
+            <view class="text" style="width: 100%">              
+                <view mt-10>第<text >5462</text>位游客完成打卡</view>
+            </view>
+          </view>
+          <view class="butList flex justify-around my-10">
+            <!-- <div class="share">
+              <u-button :customStyle="{opacity: 0,width: '50px', height: '50px',}" type="primary" icon="share" plain text=" " open-type="share"></u-button>
+              <u-icon name="share" color="#2979ff" size="28"></u-icon>
+            </div> -->
+            <u-icon name="download" color="#2979ff" size="28"></u-icon>
+          </view>
+        </view>
+		</u-popup>
+  </view>
+</template>
+
+<script>
+// import uButton from "uview-ui/components/u-button/u-button.vue";
+export default {
+  components: {
+    // uButton
+  },
+  data() {
+    return {
+      title: "Hello",
+      show: true,
+      weburl: "https://test.4dkankan.com/spg.html?m=KK-t-fs8TbMny2Zb&lang=zh"
+    };
+  },
+  onLoad() {},
+  methods: {
+    handleHome() {
+      console.log("开启云上观灯", uni);
+      uni.$u.route("/pages/home/home");
+    },
+    close() {
+      this.show = false
+      // console.log('close');
+    },
+    open() {
+
+    },
+    handleShare() {
+      uni.share({
+        provider: "weixin",
+        scene: "WXSceneTimeline",
+        type: 1,
+        summary: "我正在使用HBuilderX开发uni-app,赶紧跟我一起来体验!",
+        success: function (res) {
+          console.log("success:" + JSON.stringify(res));
+        },
+        fail: function (err) {
+          console.log("fail:" + JSON.stringify(err));
+        }
+      });
+    }
+  },
+};
+</script>
+
+<style lang="scss">
+.mySharepopup{
+  // width: 330px;
+  // height: 537px;
+  padding: 19px 17px 0px 17px;
+  .close{
+    position: absolute;
+    right: -20px;
+    top: -20px;
+    width: 42px;
+    height: 42px;
+  }
+  .butList{
+    position: absolute;
+    bottom: -80px;
+    left: calc(50% - 14px);
+    .share{
+      position: relative;
+      .u-icon{
+        position: absolute;
+        left: calc(50% - 14px);
+        top: calc(50% - 14px);
+      }
+    }
+  }
+  .text{
+    position: relative;
+    .shareImg{
+      width: 100%;
+      position: absolute;
+      bottom: -30px;
+    }
+  }
+  .bottom{
+    font-weight: 400;
+    height: 50px;
+    font-size: 16px;
+    color: #000000;
+    line-height: 19px;
+    text-align: center;
+    font-style: normal;
+    text-transform: none;
+    position: relative;
+    bottom: -30px;
+    text {
+      font-size: 20px;
+      color: #B1967B;
+    }
+  }
+}
+.content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 100vh;
+  width: 100%;
+  .urlContent{
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+  }
+  .right{
+    position: fixed;
+    left: 20px;
+    top: 20px;
+  }
+}
+
+.logo {
+  height: 200rpx;
+  width: 200rpx;
+  margin-top: 200rpx;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: 50rpx;
+}
+
+.text-area {
+  display: flex;
+  justify-content: center;
+  padding-bottom: 50%;
+}
+
+.title {
+  font-size: 36rpx;
+  color: #8f8f94;
+}
+.home-img{
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  .share{
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 30px;
+    height: 30px;
+    border-radius: 50%;
+    text-align: center;
+    line-height: 30px;
+    border: 1px solid #2979ff;
+
+  }
+}
+.u-popup__content__close{
+  right: calc(50% - 4.5px) !important;
+  bottom: -40px !important;
+  z-index: 10;
+  border: 1px solid #fff;
+  border-radius: 20px;
+  padding: 5px;
+  color: #fff;
+  .u-icon__icon {
+     color: #fff !important;
+  }
+}
+</style>

+ 0 - 0
pages/home/scene.vue


+ 52 - 0
pages/home/webview.vue

@@ -0,0 +1,52 @@
+<template>
+  <view class="webview-box">
+    <web-view
+      v-if="url"
+      ref="webview"
+      class="webview"
+      :src="url"
+      @onPostMessage="PostMessage"
+      @message="PostMessage"
+    ></web-view>
+  </view>
+</template>  
+<script>
+export default {
+  data() {
+    return {
+      url: "https://sit-nanhuacs.4dage.com/web/index.html#/home",
+      token: uni.getStorageSync('token')
+    };
+  },
+  onLoad(data) {
+    console.log("postMessage1: ", data);
+      this.url = data.url + "?token=" + this.token;
+    console.log("postMessage1: ", this.url);
+  },
+  methods: {
+    // 接收h5页面发来的键值判断需要执行的操作
+    PostMessage(evt) {
+      console.log("2222postMessage1: ", evt);
+    },
+    // 获取到对应webview(关键)通过evalJs(注意大小写,如果不知道evalJ是什么,可自行百度) 执行网页的函数,可对其进行传参,完成与网页的通讯
+    handlePostMessage(res) {
+      console.log("212", res);
+    },
+  },
+};
+</script>  
+
+<style>
+.webview-box {
+  position: absolute;
+  left: 0px;
+  right: 0px;
+  top: 0px;
+  bottom: 0px;
+}
+
+.webview {
+  flex: 1;
+  height: 300rpx;
+}
+</style>

+ 225 - 0
pages/my/index.vue

@@ -0,0 +1,225 @@
+<template>
+	<div class="content">
+    <u-navbar
+        title="南华禅寺灯会云游"
+        @rightClick="rightClick"
+        :autoBack="true" >
+        </u-navbar>
+    <div class="avatarUrl">
+      <button class="but" type="balanced" open-type="chooseAvatar" @chooseavatar="onChooseavatar">
+        <img :src="userInfo.avatarUrl" class="refreshIcon"></img>
+      </button>
+    </div>
+    <div class="userName">
+      <text>昵称:</text>
+      <input style="    text-align: left;width: 100px" :clearable="false" type="nickname" class="weui-input" :value="userInfo.nickName" @blur="bindblur"
+        placeholder="请输入昵称" @input="bindinput" />
+    </div>		<view class="fixed bottom-48 w-full text-center">
+      <view  class="absolute" style="left: calc(50% - 112px);bottom: 100px">
+        <u--image src="https://4dscene.4dage.com/new4dkk/deng/static/img/btn_enter@2x.png" width="224px" height="55px"></u--image>
+      </view>
+			<view class="butText text-center" @click="handleHome">退出登录</view>
+		</view>
+    <tabbar ref="tabbar" />
+	</div>
+</template>
+
+<script>
+// import uButton from "uview-ui/components/u-button/u-button.vue";
+import tabbar from "components/tabbar/index.vue";
+import { userInfo, updateWxUser, logout } from "@/common/api.js";
+// import uNavbar from "uview-ui/components/u-navbar/u-navbar.vue";
+import { mapState } from "vuex";
+export default {
+  components: {
+    tabbar,
+    // uButton
+    // uNavbar
+  },
+  data() {
+    return {
+      title: "Hello World",
+      avatarUrl: "",
+      userName: "",
+      show: false,
+    };
+  },
+  computed: {
+    ...mapState(["userInfo"]),
+  },
+  onLoad(e) {
+    console.log(e, "onChooseAvatar");
+    let that = this;
+    this.$store.commit("changeUseinf", userInfo);
+    userInfo({}).then((res) => {
+      // uni.$u.toast("登录成功");
+      // state.token = res.token;
+    });
+    if (e.scanCode) {
+      //进入扫码
+      that.$refs.tabbar.handleItem({name: "扫码打卡"}, 4)
+      // uni.scanCode({
+      //   onlyFromCamera: true,
+      //   success: function (res) {
+      //     console.log("条码类型:" + res.scanType);
+      //     console.log("条码内容:" + res.result, that.$refs.tabbar);
+      //     that.$refs.tabbar.show = true;
+      //     // that.$store.commit("changeActive", index);
+      //   },
+      //   fail: function (e) {
+      //     console.log("扫码失败", e, that.$refs.tabbar.show);
+      //     // this.$store.commit("changeActive", 4);
+      //     that.$refs.tabbar.show = true;
+      //   },
+      // });
+    }
+  },
+  // // 在小程序的JS文件中定义接口
+  // wx.miniProgram.onMessageToH5 = function (event) {
+  //   console.log('收到来自H5页面的消息:', event.data);
+  //   // 在这里处理接收到的消息
+  // },
+  methods: {
+    onChooseavatar(e) {
+      console.log(e, "onChooseAvatar");
+      this.avatarUrl = e.detail.avatarUrl;
+      this.upload_file(e.detail.avatarUrl);
+    },
+    upload_file(e) {
+      wx.showLoading({
+        title: "上传中",
+      });
+
+      let self = this;
+      wx.uploadFile({
+        url: "https://sit-nanhuacs.4dage.com/api/wx/upload",
+        filePath: e, //图片路径
+        name: "file",
+        header: {
+          "Content-Type": "multipart/form-data",
+          token: uni.getStorageSync("token"),
+        },
+        formData: {
+          type: "img",
+        },
+        success: function (a) {
+          let res = a.data;
+          res = JSON.parse(res);
+            console.log(res, "data");
+          if (res.code == 0) {
+            const { data } = res;
+            let avatarUrl = 'https://sit-nanhuacs.4dage.com' + data.filePath;
+            self.avatar = 'https://sit-nanhuacs.4dage.com' + data.filePath;
+            self.cdnUrl = 'https://sit-nanhuacs.4dage.com' + data.filePath;
+            updateWxUser({ ...self.userInfo, avatarUrl: avatarUrl }).then((res) => {
+              console.log(res);
+              self.$store.commit("changeUseinf", userInfo);
+              // if (res.code == 0) {
+              //   uni.$u.toast("修改成功");
+              // } else {
+              //   uni.$u.toast("修改失败");
+              // }
+            });
+          }
+
+          wx.hideLoading();
+          wx.showToast({
+            title: "上传成功",
+            icon: "success",
+            duration: 3000,
+          });
+        },
+        fail: function (a) {
+          wx.hideLoading();
+          wx.showToast({
+            title: "上传失败",
+            icon: "none",
+            duration: 3000,
+          });
+        },
+      });
+    },
+    bindblur(e) {
+      console.log(e);
+      let userName = e.detail.value;
+      updateWxUser({ ...this.userInfo,nickName: userName}).then((res) => {
+        this.$store.commit("changeUseinf", userInfo);
+        console.log(res);
+      });
+    },
+    bindinput(e) {
+      console.log(e);
+     },
+    savaInfo() {
+      let that = this;
+      updateWxUser({ ...this.userInfo,nickname: this.userName, avatarUrl: this.avatar }).then((res) => {
+        console.log(res);
+      });
+    },
+    handleHome() {
+      console.log("开启云上观灯", uni);
+      logout().then((res) => {
+        uni.$u.route("/pages/home/index");
+      });
+    },
+    close() {
+      this.show = false;
+      // console.log('close');
+    },
+    open() {
+      // this.show = false;
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.content {
+  width: 100%;
+  height: 100vh;
+  position: relative;
+  background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/bg03@2x.png)
+    100% 100% no-repeat;
+  background-size: cover;
+  .userName {
+    width: 100%;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  .avatarUrl {
+    width: 126px;
+    height: 126px;
+    box-shadow: 0px 4px 9px 0px rgba(0, 0, 0, 0.25);
+    border-radius: 0px 0px 0px 0px;
+    border: 5px solid #ffffff;
+    border-radius: 50%;
+    margin: 146px auto 40px auto;
+    .but {
+      width: 100%;
+      height: 100%;
+      border-radius: 50%;
+      padding: 0;
+      .refreshIcon {
+        width: 100%;
+        border-radius: 50%;
+        height: 100%;
+      }
+    }
+  }
+
+  .butText {
+    font-weight: 400;
+    font-size: 24px;
+    color: #ffffff;
+    line-height: 36px;
+    text-shadow: 0px 0px 8px #fff9b1, 0px 0px 27px #fff9b1, 0px 0px 4px #d86332;
+    font-style: normal;
+    text-transform: none;
+    position: relative;
+    top: -110px;
+    z-index: 10;
+  }
+}
+</style>

+ 92 - 0
pages/prayers/DomData.js

@@ -0,0 +1,92 @@
+/**
+ *
+ *
+ * @param {*} number  第几位
+ * @param {*} src 名片头像
+ * @param {*} name 名片名字
+ * @param {*} qrCodeUrl 小程序codeURL图片
+ */
+const wxml = (name, pic, c1, c2) => `
+<view class="container">
+    <text class="name">wodecesss</text>
+    <text class="content">asdsadad</text>
+    <text class="content">cccccc</text>
+    <view class="bottom">
+        <text class="msg">扫码一起加入学习吧</text>
+    </view>
+</view>
+`
+
+/**
+ *
+ *
+ * @param {*} screenWidth 屏幕宽度
+ * @param {*} canvasWidth  画布宽度
+ * @param {*} canvasHeight  画布高度
+ * @param {*} numberWidth  数字宽度,动态设置
+ * @return {*} 
+ */
+const style = (screenWidth, canvasWidth, canvasHeight) => {
+  return {
+    "container": {
+      width: canvasWidth,
+      height: canvasHeight,
+      position: 'relative',
+      overflow: 'hidden',
+      backgroundColor: '#ffffff',
+    },
+    "name": {
+      fontSize: 20,
+      color: '#333',
+      marginLeft: canvasWidth * 0.08,
+      width: canvasWidth * 0.84,
+      height: screenWidth * 0.18,
+      textAlign: 'center',
+    },
+    "content": {
+      fontSize: 14,
+      color: '#333',
+      width: canvasWidth * 0.84,
+      height: screenWidth * 0.15,
+      marginLeft: canvasWidth * 0.08,
+    },
+    "pic": {
+      width: canvasWidth * 0.3,
+      height: screenWidth * 0.28,
+      marginTop: canvasWidth * 0.1,
+      marginLeft: canvasWidth * 0.35,
+      marginBottom: canvasWidth * 0.05,
+      borderRadius: screenWidth * 0.14,
+      overflow: 'hidden',
+    },
+    "bottom": {
+      width: canvasWidth,
+      height: screenWidth * 0.2,
+      flexDirection: 'row',
+      justifyContent: 'self-start',
+      alignItems: 'center',
+      backgroundColor: '#fafafa',
+      position: 'absolute',
+      bottom: 0,
+      left: 0,
+    },
+    "qr": {
+      width: canvasWidth * 0.14,
+      height: screenWidth * 0.14,
+      marginLeft: canvasWidth * 0.04,
+      marginRight: canvasWidth * 0.04,
+    },
+    "msg": {
+      fontSize: 14,
+      color: '#a1a1a1',
+      width: canvasWidth * 0.74,
+      height: 14,
+      textAlign: 'left'
+    },
+  }
+}
+
+module.exports = {
+  wxml,
+  style
+}

+ 393 - 0
pages/prayers/list.vue

@@ -0,0 +1,393 @@
+<template>
+  <view class="prayers-list" @click="handleclick">
+    <u-navbar
+      title="南华云灯会"
+      leftIconColor="#fff"
+      :titleStyle="{ color: '#fff' }"
+      bgColor="transparent"
+      :autoBack="true"
+    >
+    </u-navbar>
+    <!-- <u-navbar title="剑未配妥,出门已是江湖" @click-left="onClickBack" @click-right="onClickRight"></u-navbar> -->
+    <div class="allList" v-if="false">
+      <div class="list">
+        <swiper
+          :display-multiple-items="3.3"
+          :rebound="false"
+          slidesPerView="auto"
+          style="height: 555px"
+          :height="555"
+          class="swiper"
+          :autoplay="false"
+          @click="handleclick"
+        >
+          <swiper-item
+            style="height: 555px"
+            :height="680"
+            v-for="(item, index) in list"
+            :key="index"
+          >
+            <div class="listitem">
+              <div
+                class="item"
+                :class="{ active: active == element }"
+                v-for="(element, s) in item.list"
+                :key="s"
+              >
+                {{ element }}
+              </div>
+            </div>
+          </swiper-item>
+        </swiper>
+      </div>
+      <div class="tips">请选择祈愿类型</div>
+      <div class="img_signal" v-if="!show"></div>
+    </div>
+    <div class="activeIndex">
+      <div class="indexHome">学业祈愿</div>
+      <div class="textListContent">
+        <div
+          class="textList"
+          v-for="(item, index) in textList"
+          :key="item"
+          :class="'text' + index"
+        >
+          {{ item }}
+        </div>
+      </div>
+      <div class="textTips">请选择祈愿语</div>
+      <div class="selcet">
+        <div>您来自哪个省份?</div>
+        <div class="selcetBut" @click="provinceShow = true">
+          {{province}}
+          <u-icon
+            class="mytest"
+            name="arrow-down-fill"
+            color="#fff"
+            size="10"
+          ></u-icon>
+        </div>
+      </div>
+      <div class="qiyuan">
+        <u--image
+              height="70px"
+              width="70px"
+              mode="heightFix"
+              src="/static/img/btn_wish@2x.png"
+            ></u--image>
+      </div>
+    </div>
+    <u-picker :show="provinceShow" :columns="[provinceList]"
+      @cancel="provinceShow = false"
+			@confirm="confirm"
+			@close="provinceShow = false" closeOnClickOverlay></u-picker>
+  </view>
+</template>
+
+<script>
+// import uNavbar from "uview-ui/components/u-navbar/u-navbar.vue";
+export default {
+  components: {
+    // uNavbar
+  },
+  data() {
+    return {
+      active: "婚姻祈愿1",
+      show: false,
+      province: "请选择",
+      provinceShow: false,
+      provinceList: [
+        "北京",
+        "天津",
+        "河北省",
+        "山西省",
+        "内蒙古自治区",
+        "辽宁省",
+        "吉林省",
+        "黑龙江省",
+        "上海",
+        "江苏省",
+        "浙江省",
+        "安徽省",
+        "福建省",
+        "江西省",
+        "山东省",
+        "河南省",
+        "湖北省",
+        "湖南省",
+        "广东省",
+        "广西壮族自治区",
+        "海南省",
+        "重庆",
+        "四川省",
+        "贵州省",
+        "云南省",
+        "西藏自治区",
+        "陕西省",
+        "甘肃省",
+        "青海省",
+        "宁夏回族自治区",
+        "新疆维吾尔自治区",
+        "台湾",
+        "香港特别行政区",
+        "澳门特别行政区",
+        "海外",
+      ],
+      list: [
+        {
+          list: ["婚姻祈愿1", "婚姻祈愿2"],
+        },
+        {
+          list: ["婚姻祈愿2", "婚姻祈愿2"],
+        },
+        {
+          list: ["婚姻祈愿3", "婚姻祈愿2"],
+        },
+        {
+          list: ["婚姻祈愿4", "婚姻祈愿2"],
+        },
+        {
+          list: ["婚姻祈愿5", "婚姻祈愿2"],
+        },
+        {
+          list: ["婚姻祈愿6", "婚姻祈愿2"],
+        },
+        {
+          list: ["婚姻祈愿7", "婚姻祈愿2"],
+        },
+      ],
+      textList: [
+        "100%纯手工制作",
+        "知识的殿堂中闪耀属于你的光芒。",
+        "愿你学有所",
+      ],
+      // list: [{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // },{
+      //   name: '婚姻祈愿',
+      //   textList: ['100%纯手工制作', '100%愿你学业精进,成绩辉煌,知识的殿堂中闪耀属于你的光芒。', '愿你学有所成,智慧增进,每一次努力都为未来铺就光明之路。']
+      // }]
+    };
+  },
+  onLoad() {},
+  methods: {
+    handleHome() {
+      console.log("开启云上观灯", uni);
+      uni.$u.route("/pages/home/home");
+    },
+    handleclick() {
+      this.show = true;
+    },
+    confirm(val){
+      console.log("confirm", val);
+      this.province = val.value[0];
+      this.provinceShow = false;
+    }
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.prayers-list {
+  background: rgba(32, 21, 10, 0.7);
+  border-radius: 0px 0px 0px 0px;
+  height: 100vh;
+  padding-top: 100px;
+  .img_signal {
+    width: 76px;
+    height: 106px;
+    background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/img_signal@2x.png)
+      100% 100% no-repeat;
+    background-size: cover;
+    position: absolute;
+    right: 50px;
+    bottom: 30px;
+    animation: handleAni 1s linear infinite;
+    transition: all 1s;
+  }
+  .tips {
+    width: 100%;
+    text-align: center;
+    margin-top: 27px;
+    color: #fff;
+    font-size: 14px;
+    line-height: 21px;
+  }
+  .list {
+    height: 555px;
+    .swiper {
+      padding: 0 10px;
+      .listitem {
+        text-align: center;
+        padding: 0 20px;
+        .item {
+          margin-top: 20px;
+          // margin: 18px 20px 0 20px;
+          width: 18px;
+          height: 267px;
+          background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/label@2x.png)
+            100% 100% no-repeat;
+          background-size: cover;
+          writing-mode: tb-rl;
+          text-align: center;
+          padding: 0 25px;
+          position: relative;
+        }
+        .active {
+          &:after {
+            content: "";
+            box-shadow: 0px 2px 18px 11px #fff86c,
+              inset 0px 1px 1px 0px rgba(177, 156, 125, 0.7);
+            position: absolute;
+            height: 71%;
+            width: 80%;
+            top: 32px;
+            left: 8px;
+            border-radius: 25px;
+            z-index: -1;
+          }
+        }
+      }
+    }
+  }
+  .activeIndex {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    .indexHome {
+      height: 500px;
+      width: 28px;
+      background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/label@2x.png)
+        100% 100% no-repeat;
+      background-size: contain;
+      writing-mode: tb-rl;
+      padding: 0 50px;
+      position: absolute;
+      font-weight: 400;
+      font-size: 30px;
+      color: #8c4042;
+      line-height: 45px;
+      font-style: normal;
+      display: flex;
+      writing-mode: vertical-lr;
+      text-align: center;
+      justify-content: center;
+      flex-direction: column;
+      letter-spacing: 18px;
+      left: calc(50% - 64px);
+    }
+    .qiyuan{
+      position: absolute;
+      bottom: 162px;
+      right: 20px;
+    }
+    .selcet {
+      position: absolute;
+      width: 100%;
+      bottom: 20%;
+      text-align: center;
+      color: #fff;
+      font-size: 14px;
+      line-height: 14px;
+      .selcetBut {
+        border: 1px solid #d9d9d9;
+        line-height: 48px;
+        width: 120px;
+        margin: 0 auto;
+        padding: 0 15px;
+        margin-top: 10px;
+        height: 48px;
+        border-radius: 10px;
+        position: relative;
+        .mytest {
+          position: absolute;
+          right: 25px;
+          top: calc(50% - 5px);
+        }
+      }
+    }
+    .textTips {
+      color: #fff;
+      writing-mode: vertical-lr;
+      text-align: center;
+      font-weight: 400;
+      font-size: 14px;
+      line-height: 14px;
+      text-align: center;
+      font-style: normal;
+      text-transform: none;
+      position: absolute;
+      top: calc(50% - 150px);
+      left: calc(50% + 68px);
+    }
+    .textListContent {
+      display: flex;
+    }
+    .textList {
+      height: fit-content;
+      padding: 20px 6px;
+      font-style: normal;
+      display: flex;
+      writing-mode: vertical-lr;
+      text-align: center;
+      justify-content: center;
+      flex-direction: column;
+      background: rgba(255, 255, 255, 0.3);
+      border-radius: 20px 20px 20px 20px;
+      border: 1px solid #fff9b6;
+      position: absolute;
+      font-weight: 400;
+      font-size: 11px;
+      top: 50px;
+      color: #fff9b6;
+    }
+    .text0 {
+      left: 30px;
+    }
+    .text1 {
+      left: 80px;
+      top: 276px;
+    }
+    .text2 {
+      right: 30px;
+    }
+  }
+}
+
+@keyframes handleAni {
+  from {
+    right: 50px;
+  }
+  to {
+    right: 30px;
+  }
+}
+</style>

+ 381 - 0
pages/prayers/save.vue

@@ -0,0 +1,381 @@
+<template>
+  <view class="canvas-box">
+      <!-- 导航栏 -->
+      <view class="nav-box">
+          <view class="title-top" :style="'padding-top:' + statusBarHeight + 'rpx'">
+              <u-icon class="title-icon" name="arrow-left" color="#ffffff" size="36" @click="getBack"></u-icon>
+              <text>海报分享</text>
+          </view>
+      </view>
+      
+      <!-- 开发完成之前,取消 fixed;opacity: 0;-->
+      <canvas style="width: 352px;height: 800px;position: fixed;" class="canvas" canvas-id="canvasID"></canvas>
+      <!-- 完成海报制作后,需要把canvas移到看不见的地方,或者隐藏,把image显示出来 -->
+      <!-- <image :src="imgUrl" mode=""></image> -->
+
+      <view class="footer">
+          <view class="download" @click="saveImage">
+              <!-- 小于符号图标 -->
+              <u-icon name="download" color="#ffffff" size="34"></u-icon>
+              <text>保存到相册</text>
+          </view>
+      </view>
+  </view>
+</template>
+
+<script>
+import wxcode from 'uniapp-qrcode';
+  export default {
+      data() {
+          return {
+              imgUrl: '',
+              statusBarHeight: 0,
+              bgimg: '',
+              textX: 10, //文字x轴坐标
+              avatar: 'https://4dscene.4dage.com/new4dkk/deng/static/img/flower_colorful@2x.png', //头像地址
+              hello: 'https://4dscene.4dage.com/new4dkk/deng/static/img/img_label@2x.png', // hello图标
+              mony: 'https://4dscene.4dage.com/new4dkk/deng/static/img/flower_pink@2x.png' //圆的钱图标
+          }
+      },
+      async created() {
+        wxcode.qrcode('qrcode', '1234567890123456789', 420, 420);
+          // this.$tool.getSystemInfo().then(res => {
+          //     this.statusBarHeight = res
+          // })
+      },
+      async mounted() {
+          let list = ["知识的殿堂中闪耀属于你的光芒。阿斯顿撒大大大大大大啊啊啊啊", "愿你学业精进,成绩辉煌,"]
+          let imgData = await uni.getImageInfo({src: this.hello})
+          this.bgimg = imgData[1].path
+          let ctx = uni.createCanvasContext('canvasID', this);
+          // ctx.setFillStyle("transparent"); //设置canvas背景颜色
+          // ctx.fillRect(0, 0, 346, 500) //设置canvas画布大小
+          this.fillRoundRect(ctx, 0, 0, 352, 1400, 15, 'transparent'); //绘制一个圆角矩形
+          // this.fillRoundRect(ctx, 0, 0, 346, 182, 15, '#333231'); //绘制一个圆角矩形
+          console.log('imgUrlbgimg', this.bgimg)
+          this.drawrectcular(ctx, this.bgimg, 86, 0, 180, 700) //绘制圆形头像
+          this.listdrawText(ctx, list)
+          // this.drawTextVertical(ctx, '绘制武将姓名:陆逊', x, y);
+          // // 绘制势力汉字:吴
+          // that.drawInfluence(ctx, that.data.hero.HERO.INFLUENCE);
+          // // 绘制武将姓名:陆逊
+          // that.drawName(ctx, that.data.hero.HERO.NAME);
+          // // 绘制武将称号:江陵侯
+          // that.drawHorner(ctx, that.data.hero.HERO.HORNER);
+
+          // ctx.setFontSize(18)
+          // ctx.setFillStyle("#ffffff")
+          // ctx.fillText('明天依然是晴天11', 98, 65)
+          // ctx.drawImage(this.hello, 240, 10, 86, 86) //二维码
+          // ctx.font = '20px normal'
+          // ctx.setFillStyle("#09CFB1")
+          // ctx.fillText('我为“贤马”带盐', 30, 122)
+          // ctx.font = '16px normal'
+          // ctx.setFillStyle("#ffffff")
+          // ctx.fillText('“闲么?上贤马做兼职”', 20, 152)
+
+          // // 绘制职位标题,多余文字自动换行
+          // ctx.setFontSize(28)
+          // ctx.setFillStyle("#333333")
+
+          // let str = '店铺实习生ZAra重庆龙湖时代'
+          // // 字符串总长度
+          // let _strLength = str.length
+          // // 总结截取次数
+          // let _strNum = Math.ceil(_strLength / 9)
+
+          // // 每次开始截取字符串的索引
+          // let _strHeight = 0
+          // // 绘制的字体 x,y的初始位置
+          // let _strX = 27,
+          //     _strY = 223
+          // let strIndex = 223
+          // // 开始截取
+          // for (let i = 0; i < _strNum; i++) {
+          //     strIndex = _strY + i * 40
+          //     ctx.fillText(str.substr(_strHeight + i * 9, 9), _strX, _strY + i * 40)
+          // }
+          // strIndex += 30
+          // ctx.setFontSize(14)
+          // ctx.setFillStyle("#1BB99A")
+          // let strtitle = '环境好/结算快/时间短'
+          // ctx.fillText(strtitle, _strX, strIndex)
+
+          // ctx.setFontSize(20)
+          // ctx.setFillStyle("#333231")
+          // ctx.fillText('16元/小时', _strX, 418)
+
+          // this.drawCircular(ctx, this.mony, _strX, 429, 14, 14) //绘制圆形头像
+          // ctx.setFontSize(12)
+          // ctx.setFillStyle("#F7BA65")
+          // ctx.fillText('已预付', _strX + 20, 440)
+
+          // 绘制微信二维码
+          ctx.drawImage(this.hello, 208, 370, 120, 120) //二维码
+
+          ctx.draw(false, () => {
+              // 返回canvas图片信息
+              uni.canvasToTempFilePath({
+                  canvasId: 'canvasID',
+                  success: (res) => {
+                      this.imgUrl = res.tempFilePath
+                      // console.log(res.tempFilePath)
+                  },
+                  fail: function(err) {
+                      console.log(err)
+                  }
+              })
+          })
+      },
+      methods: {
+          getBack() {
+              uni.navigateBack({
+                  delta: 1
+              });
+          },
+          listdrawText(ctx, list){
+            let mylist = []
+            list.map(ele => {
+              if(ele && ele.length > 21){
+                mylist.push(ele.substring(21, ele.length))
+                mylist.push(ele.substring(0, 21))
+              }else {
+                mylist.push(ele)
+              }
+              // mylist.push(ele)
+            })
+            this.textX = 200 - (mylist.length/2)* 40;
+            console.log('mylist', mylist, this.textX)
+            mylist.map(ele => {
+              this.drawTextVertical(ctx, ele, this.textX, 160);
+              this.textX += 30;
+            })
+          },
+          drawTextVertical(context, text, xx, y) {
+            let x = xx || 0;
+            var arrText = text.split('');
+            var arrWidth = arrText.map(function (letter) {
+              return 18;
+            });
+            
+            var align = context.textAlign;
+            var baseline = context.textBaseline;
+            if (align == 'left') {
+              x = x + Math.max.apply(null, arrWidth) / 2;
+            } else if (align == 'right') {
+              x = x - Math.max.apply(null, arrWidth) / 2;
+            }
+            if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') {
+              y = y - arrWidth[0] / 2;
+            } else if (baseline == 'top' || baseline == 'hanging') {
+              y = y + arrWidth[0] / 2;
+            }
+
+            context.textAlign = 'center';
+            context.textBaseline = 'middle';
+            context.font = '18px normal'
+            context.setFillStyle("#303030")
+            // 开始逐字绘制
+            arrText.forEach(function (letter, index) {
+              // 确定下一个字符的纵坐标位置
+              var letterWidth = arrWidth[index];
+              // 是否需要旋转判断
+              var code = letter.charCodeAt(0);
+              console.log('arrWidth', arrWidth, align, index, letter)
+
+              if (code <= 256) {
+                context.translate(x, y);
+                // 英文字符,旋转90°
+                context.rotate(90 * Math.PI / 180);
+                context.translate(-x, -y);
+              } else if (index > 0 && text.charCodeAt(index - 1) < 256) {
+                // y修正
+                y = y + arrWidth[index - 1] / 2;
+              }
+              context.fillText(letter, x, y);
+              // 旋转坐标系还原成初始态
+              context.setTransform(1, 0, 0, 1, 0, 0);
+              // 确定下一个字符的纵坐标位置
+              var letterWidth = arrWidth[index];
+              y = y + letterWidth;
+            });
+            // 水平垂直对齐方式还原
+            context.textAlign = align;
+            context.textBaseline = baseline;
+          },
+          saveImage() { //点击保存
+              var _this = this;
+              uni.saveImageToPhotosAlbum({
+                  filePath: _this.imgUrl,
+                  success() {
+                      uni.showModal({
+                          title: "保存成功",
+                          content: "图片已成功保存到相册,快去分享到您的圈子吧",
+                          showCancel: false
+                      })
+                  }
+              })
+          },
+          // 将网络图片转为临时图片地址
+          async getImageInfo({imgSrc}) {
+              return new Promise((resolve, errs) => {
+                  uni.downloadFile({
+                      src: imgSrc,
+                      success: function(image) {
+                          resolve(image);
+                      },
+                      fail(err) {
+                          errs(err);
+                      }
+                  });
+              });
+          },
+          // 绘制圆形头像
+          drawCircular(ctx, url, x, y, width, height) {
+              //画圆形头像
+              var avatarurl_width = width;
+              var avatarurl_heigth = height;
+              var avatarurl_x = x;
+              var avatarurl_y = y;
+              ctx.save(); //先保存状态,已便于画完园再用
+              ctx.beginPath(); //开始绘制
+              ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math
+                  .PI * 2, false);
+              ctx.setFillStyle("#FFFFFF")
+              ctx.fill() //保证图片无bug填充
+              ctx.clip(); //剪切
+              ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); //推进去图片
+              ctx.restore();
+          },
+          
+          // 绘制矩形图片
+          drawrectcular(ctx, url, x, y, width, height) {
+              //画圆形头像
+              var avatarurl_width = width;
+              var avatarurl_heigth = height;
+              var avatarurl_x = x;
+              var avatarurl_y = y;
+              ctx.save(); //先保存状态,已便于画完园再用
+              ctx.beginPath(); //开始绘制
+              ctx.rect(x, y, width, height);
+              // ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math
+              //     .PI * 2, false);
+              ctx.setFillStyle("#FFFFFF")
+              ctx.fill() //保证图片无bug填充
+              ctx.clip(); //剪切
+              ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); //推进去图片
+              ctx.restore();
+          },
+          // 绘制带圆角的矩形方法
+          fillRoundRect(cxt, x, y, width, height, radius,  fillColor) {
+              //圆的直径必然要小于矩形的宽高
+              if (2 * radius > width || 2 * radius > height) {
+                  return false;
+              }
+
+              cxt.save();
+              cxt.translate(x, y);
+              //绘制圆角矩形的各个边
+              this.drawRoundRectPath(cxt, width, height, radius);
+              cxt.fillStyle = fillColor || '#fff'; //若是给定了值就用给定的值否则给予默认值
+              cxt.fill();
+              cxt.restore();
+          },
+          drawRoundRectPath(cxt, width, height, radius) {
+              cxt.beginPath(0);
+              //从右下角顺时针绘制,弧度从0到1/2PI
+              cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
+
+              //矩形下边线
+              cxt.lineTo(radius, height);
+
+              //左下角圆弧,弧度从1/2PI到PI
+              cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
+
+              //矩形左边线
+              cxt.lineTo(0, radius);
+
+              //左上角圆弧,弧度从PI到3/2PI
+              cxt.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2);
+
+              //上边线
+              cxt.lineTo(width - radius, 0);
+
+              //右上角圆弧
+              cxt.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2);
+
+              //右边线
+              cxt.lineTo(width, height - radius);
+              cxt.closePath();
+          }
+      }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .canvas-box {
+      height: 100vh;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background-color: #1ABC9C;
+
+      /deep/.nav-box {
+          width: 100%;
+          padding: 0 20rpx;
+          position: absolute;
+          z-index: 9999;
+          top: 0;
+          left: 0;
+
+          .title-top {
+              font-size: 36rpx;
+              font-weight: 550;
+              color: #FFFFFF;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              position: relative;
+              margin-bottom: 30rpx;
+
+              .title-icon {
+                  position: absolute;
+                  left: 0;
+              }
+          }
+      }
+
+      image {
+          width: 335px;
+          height: 500px;
+      }
+
+      .footer {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          position: absolute;
+          justify-content: center;
+          padding: 0 40rpx;
+          width: 100%;
+          left: 0;
+          bottom: 10%;
+
+          .download {
+              border: 1rpx solid #ffffff;
+              color: #ffffff;
+              display: flex;
+              align-items: center;
+          }
+
+          view {
+              padding: 0 20rpx;
+              height: 78rpx;
+              text-align: center;
+              line-height: 78rpx;
+              font-size: 30rpx;
+              border-radius: 36rpx;
+          }
+      }
+  }
+</style>

+ 725 - 0
pages/prayers/showlist.vue

@@ -0,0 +1,725 @@
+<template>
+  <view class="prayers-list" @click="handleclick">
+    <u-navbar
+      title="南华云灯会"
+      leftIconColor="#fff"
+      :titleStyle="{ color: '#fff' }"
+      bgColor="transparent"
+      :autoBack="true"
+    >
+    </u-navbar>
+    <!-- <u-navbar title="剑未配妥,出门已是江湖" @click-left="onClickBack" @click-right="onClickRight"></u-navbar> -->
+    <view class="allList">
+      <view class="list">
+        <scroll-view
+          scroll-x="true"
+          class="content-scroll swiper"
+          scroll-with-animation
+          :scroll-left="scrollLeft"
+        >
+          <view
+            v-for="(item, index) in list"
+            :key="index"
+            class="scroll-item list"
+            :class="
+              current == index
+                ? `active index${index} ${curIndex == index ? 'fang' : ''}`
+                : `index${index}`
+            "
+            @click="changeTitle(index)"
+          >
+            <view class="activeItem" v-if="curIndex == index">
+              <view class="activetext" v-for="(items, a) in item.list" :key="a">
+                {{ items }}
+              </view>
+            </view>
+            <view class="item-text item" v-else>{{ item.name }}</view>
+            <view class="source">
+              <text style="color: #9c9590">来源</text>
+              <text>小阿飞</text>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+      <view class="tips">
+        请选择祈愿类型
+        <view v-if="curIndex" class="downButtom flex justify-center">
+          <view class="downButtomItem">返回我的祈愿</view>
+        </view>
+        <view v-else class="downButtom flex justify-center">
+          <view class="downButtomItem">祈愿地图</view>
+          <view class="downButtomItem" @click="handleShare">分享</view>
+        </view>
+      </view>
+      <view class="img_signal" v-if="!show"></view>
+
+      <u-popup
+        :show="shareShow"
+        mode="center"
+        bgColor="transparent"
+        round="10"
+        @close="close"
+      >
+        <view class="mySharepopup planel" ref="addImage" id="capture">
+          <!-- <view class="close" @click="close">
+            <u--image
+              width="42px"
+              height="42px"
+              src="/static/img/icon_cancel@2x.png"
+            ></u--image>
+          </view> -->
+          <view class="shareItem answer_draw_canvas">
+            <view class="activeItem answer_draw_canvas">
+              <view
+                class="activetext answer_draw_canvas"
+                v-for="(items, a) in shareData.list"
+                :key="a"
+              >
+                {{ items }}
+              </view>
+            </view>
+            <!-- <view class="share-page-box" id="box" v-if="shareShow"
+              :style="{ width: '180px', height: '600px' }">
+              <wxml-to-canvas class="widget" :width="canvasWidth" :height="canvasHeight"></wxml-to-canvas>
+            </view> -->
+            <view class="shareRight answer_draw_canvas">
+              <view class="close" @click="close">
+                <u--image
+                  width="42px"
+                  height="42px"
+                  src="/static/img/icon_cancel@2x.png"
+                ></u--image>
+              </view>
+              <view class="downBut" @click="handleDown">
+                <view class="img">
+                  <view>
+                    <u--image
+                      width="36px"
+                      height="36px"
+                      src="https://4dscene.4dage.com/new4dkk/deng/static/img/icon_download@2x.png"
+                    ></u--image>
+                  </view>
+                </view>
+              </view>
+              <view class="downText" @click="handleDown">下载图片</view>
+            </view>
+            <view class="codeImg1 answer_draw_canvas">
+              <QiyanQrcode
+                :size="72"
+                background="#fff"
+                @click="title1 = '1.现在时间戳:' + Date.now()"
+                :text="title1"
+              ></QiyanQrcode>
+            </view>
+            <view class="codeImg2 answer_draw_canvas">
+              <QiyanQrcode
+                :size="72"
+                background="#fff"
+                @click="title1 = '1.现在时间戳:' + Date.now()"
+                :text="title1"
+              ></QiyanQrcode>
+            </view>
+          </view>
+        </view>
+      </u-popup>
+      <canvas
+      canvas-id="answerCanvas"
+      class="answerCanvas"
+      :style="'width:' + canvasWidth + 'px;height:' + canvasHeight + 'px;'"
+    ></canvas>
+    </view>
+  </view>
+</template>
+
+<script>
+// import uNavbar from "uview-ui/components/u-navbar/u-navbar.vue";
+import { wxml, style } from './DomData';
+import QiyanQrcode from "@/components/qiyan-qrcode/qiyan-qrcode.vue";
+import Wxml2Canvas from "wxml2canvas";
+import html2canvas from "html2canvas";
+export default {
+  components: {
+    // uNavbar
+    QiyanQrcode,
+  },
+  data() {
+    return {
+      active: "婚姻祈愿1",
+      title1: "1.现在时间戳:" + Date.now(),
+      show: false,
+      shareShow: false,
+      scrollLeft: 0, // 横向滚动条位置
+      current: 2,
+      curIndex: null,
+      province: "请选择",
+      provinceShow: false,
+      shareData: {},
+      list: [
+        {
+          name: "学业祈愿1",
+          list: ["知识的殿堂中闪耀属于你的光芒。", "愿你学业精进,成绩辉煌,"],
+        },
+        {
+          name: "学业祈愿2",
+          list: ["知识的殿堂中闪耀属于你的光芒。", "愿你学业精进,成绩辉煌,"],
+        },
+        {
+          name: "学业祈愿3",
+          list: ["知识的殿堂中闪耀属于你的光芒。", "愿你学业精进,成绩辉煌,"],
+        },
+        {
+          name: "学业祈愿4",
+          list: ["知识的殿堂中闪耀属于你的光芒。", "愿你学业精进,成绩辉煌,"],
+        },
+        {
+          name: "学业祈愿5",
+          list: ["知识的殿堂中闪耀属于你的光芒。", "愿你学业精进,成绩辉煌,"],
+        },
+        {
+          name: "学业祈愿6",
+          list: ["知识的殿堂中闪耀属于你的光芒。", "愿你学业精进,成绩辉煌,"],
+        },
+      ],
+      canvasWidth: 180, // 默认canvas宽高
+      canvasHeight: 700,
+      screenWidth: null, // 设备宽度
+      screenHeight: null, // 设备高度
+      // name: '',
+      // pic: '',
+      // chapter1: '',
+      // chapter2: '',
+      widget: null,
+      msg: '加载中,请稍等...', // 提示语
+    };
+  },
+  mounted() {
+    // 获取标题区域宽度,和每个子元素节点的宽度
+    this.getScrollW();
+  },
+  onLoad() {
+  },
+  methods: {
+    renderInit(){
+    // 获取设备信息
+      wx.getSystemInfo({
+        success: (res) => {
+          console.log("屏幕", res);
+          this.screenWidth = 180;
+          this.canvasWidth = this.screenWidth;
+          this.canvasHeight = 600;
+          console.log("海报高度:", this.canvasHeight);
+
+          this.show = true;
+          // 数字容器宽度 动态设置
+          setTimeout(() => {
+            wx.showLoading({ title: "海报加载中..." });
+            this.widget = this.selectComponent(".widget");
+            this.renderToCanvas();
+          }, 1000);
+        },
+      });
+    },
+    // 获取标题区域宽度,和每个子元素节点的宽度以及元素距离左边栏的距离
+    getScrollW() {
+      const query = uni.createSelectorQuery().in(this);
+
+      query
+        .select(".content-scroll")
+        .boundingClientRect((data) => {
+          // 拿到 scroll-view 组件宽度
+          this.contentScrollW = data.width;
+        })
+        .exec();
+
+      query
+        .selectAll(".scroll-item")
+        .boundingClientRect((data) => {
+          let dataLen = data.length;
+          for (let i = 0; i < dataLen; i++) {
+            //  scroll-view 子元素组件距离左边栏的距离
+            this.list[i].left = data[i].left;
+            //  scroll-view 子元素组件宽度
+            this.list[i].width = data[i].width;
+          }
+        })
+        .exec();
+    },
+
+    // 选择标题
+    changeTitle(index) {
+      if (index == this.current && this.curIndex != index) {
+        this.curIndex = index;
+      } else {
+        this.curIndex = null;
+      }
+      this.current = index;
+      console.log("changeTitle", index);
+      this.scrollLeft = (index - 1) * 118;
+    },
+    handleHome() {
+      console.log("开启云上观灯", uni);
+      uni.$u.route("/pages/home/home");
+    },
+    handleclick() {
+      this.show = true;
+    },
+    handleShare() {
+      console.log("handleShare");
+      this.shareShow = true;
+      this.shareData = this.list[this.current];
+      setTimeout(()=>{
+        // this.renderInit();
+      },500)
+    },
+    close() {
+      this.shareShow = false;
+      console.log("handleShare", this.shareShow);
+    },
+    changeswiper(e) {
+      // 得加上这个方法!!!
+      this.currentswiper = e.detail.current;
+    },
+    confirm(val) {
+      console.log("confirm", val);
+      this.province = val.value[0];
+      this.provinceShow = false;
+    },
+    handleDown() {
+      // this.renderInit();
+      // this.extraImage()
+      this.draw();
+    },
+    // wxml 转 canvas
+    renderToCanvas() {
+      console.log('canvasStyle.widget', this.shareData)
+      const _wxml = wxml(this.shareData);
+      console.log('this.widget', this.widget)
+      const _style = style(this.screenWidth, this.canvasWidth, this.canvasHeight) //this.canvasHeight
+      const p1 = this.widget.renderToCanvas({ wxml: _wxml, style: _style })
+      console.log('renderToCanvas', p1)
+      p1.then((res) => {
+        console.log('海报生成成功', res);
+        wx.hideLoading()
+      }).catch((err) => {
+        console.log('生成失败', err)
+      })
+    },
+    // 保存到朋友圈
+    extraImage() {
+      if (!this.show) {
+        wx.showToast({ title: '海报生成失败,无法分享到朋友圈', icon: 'none' })
+        return
+      }
+      wx.showLoading({ title: '海报生成中...' })
+      const p2 = this.widget.canvasToTempFilePath({ fileType:'jpg', quality :0.5})
+      let that = this;
+      p2.then(result => {
+        let path = result.tempFilePath
+        wx.getSetting({
+          success: res => {
+            wx.hideLoading()
+            // 非初始化且未授权的情况,需要再次弹窗提示授权
+            if (res.authSetting['scope.writePhotosAlbum'] != undefined && res.authSetting['scope.writePhotosAlbum'] != true) {
+              wx.showModal({
+                title: '是否授权相册权限',
+                content: '需要获取相册权限,请确认授权,否则无法使用相关功能',
+                success: res => {
+                  if (res.confirm) {
+                    wx.openSetting({
+                      success: dataAu => {
+                        if (dataAu.authSetting["scope.writePhotosAlbum"] == true) {
+                          wx.showToast({
+                            title: '授权成功',
+                            icon: 'none',
+                            duration: 1000
+                          });
+                          that.saveIMg(path);
+                        } else {
+                          wx.showToast({
+                            title: '授权失败',
+                            icon: 'success',
+                            duration: 1000
+                          });
+                        }
+                      }
+                    });
+                  }
+                }
+              });
+            } else {
+              // 初始化且未授权,系统默认会弹窗提示授权
+              // 非初始化且已授权,也会进入这里
+              that.saveIMg(path);
+            }
+          }
+        });
+      })
+    },
+    
+    draw() {
+      let that = this;
+      //创建wxml2canvas对象
+      let drawImage = new Wxml2Canvas(
+        {
+          element: "answerCanvas", // canvas节点的id,
+          obj: that, // 在组件中使用时,需要传入当前组件的this
+          width: 180, // 宽 自定义
+          height: 700, // 高 自定义
+          background: "#fff", // 默认背景色 设置背景色
+          progress(percent) {
+            // 绘制进度
+            console.log(percent);
+          },
+          finish(url) {
+            uni.saveImageToPhotosAlbum({
+              filePath: url,
+              success() {
+                uni.hideLoading();
+              },
+              fail() {
+                uni.hideLoading();
+              },
+            });
+          },
+          error(res) {
+            console.log(res);
+            // uni.hideLoading()
+            // 画失败的原因
+          },
+        },
+        this
+      );
+      //传入数据,画制canvas图片
+      let data = {
+        //直接获取wxml数据
+        list: [
+          {
+            type: "wxml",
+            class: ".planel .answer_draw_canvas", // answer_canvas这边为要绘制的wxml元素跟元素类名, answer_draw_canvas要绘制的元素的类名(所有要绘制的元素都要添加该类名)
+            limit: ".mySharepopup", // 这边为要绘制的wxml元素跟元素类名,最外面的元素
+            x: 0,
+            y: 0,
+          },
+        ],
+      };
+      drawImage.draw(data, that);
+    },
+    // 保存到相册
+    async saveIMg(tempFilePath) {
+      wx.saveImageToPhotosAlbum({
+        filePath: tempFilePath,
+        success: async (res) => {
+          wx.showModal({
+            content: '图片已保存,分享给好友吧!',
+            showCancel: false,
+            confirmText: '好的',
+            confirmColor: '#333',
+            success: function (res) {
+              wx.navigateBack({
+                //返回
+                delta: 1
+              });
+            },
+            fail: function (res) {
+              console.log('res', res);
+            }
+          });
+        },
+ 
+        fail: function (res) {
+          wx.showToast({
+            title: '您取消了授权',
+            icon: 'none',
+            duration: 2000
+          })
+        }
+      });
+    },
+  },
+};
+</script>
+// <script lang="renderjs" module="canvasImage">
+// import html2canvas from 'html2canvas'
+// export default {
+//   methods: {
+//     // 生成图片需要调用的方法
+//     handleDown(){
+//       // const query = uni.createSelectorQuery().in(this);
+//       const dom = this.$refs.addImage;//query.select('#capture'); // 需要生成图片内容的 dom 节点
+//       html2canvas(dom).then(canvas => {
+//       this.imgs= canvas.toDataURL()    //base64字符串   放到图片标签上
+//       });
+//     }
+//   }
+// }
+// </script>
+
+<style lang="scss" scoped>
+.prayers-list {
+  background: rgba(32, 21, 10, 0.7);
+  border-radius: 0px 0px 0px 0px;
+  height: 100vh;
+  padding-top: 100px;
+  .img_signal {
+    width: 76px;
+    height: 106px;
+    background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/img_signal@2x.png)
+      100% 100% no-repeat;
+    background-size: cover;
+    position: absolute;
+    right: 50px;
+    bottom: 30px;
+    animation: handleAni 1s linear infinite;
+    transition: all 1s;
+  }
+  .mySharepopup {
+    // width: 330px;
+    // height: 537px;
+    padding: 19px 17px 0px 17px;
+    .close {
+      position: absolute;
+      right: -24px;
+      top: -20px;
+      width: 42px;
+      height: 42px;
+    }
+    .shareItem {
+      width: 150px;
+      height: calc(100% - 512px);
+      padding: 160px 0px;
+      display: inline-block;
+      background-size: 100% 100%;
+      font-size: 12px;
+      color: #303030;
+      line-height: 18px;
+      display: flex;
+      writing-mode: vertical-lr;
+      text-align: left;
+      justify-content: center;
+      flex-direction: column;
+      background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/img_label@2x.png)
+        100% 100% no-repeat;
+      background-size: contain;
+      position: relative;
+      top: 44px;
+      font-weight: 400;
+      font-size: 16px;
+      color: #303030;
+      line-height: 24px;
+      .activetext {
+        margin: 0 9px;
+      }
+    }
+    .shareRight {
+      position: absolute;
+      right: -50px;
+      .img {
+      }
+    }
+    .downBut {
+      position: absolute;
+      right: -20px;
+      bottom: -70px;
+    }
+    .downText {
+      position: absolute;
+      right: -34px;
+      bottom: -90px;
+      font-weight: 400;
+      font-size: 16px;
+      color: #ffffff;
+      line-height: 19px;
+      width: 64px;
+      writing-mode: rl;
+    }
+    .codeImg1 {
+      position: absolute;
+      right: -90px;
+      bottom: 50px;
+      padding: 3px;
+      background: #fff;
+    }
+    .codeImg2 {
+      position: absolute;
+      right: -90px;
+      bottom: 150px;
+      padding: 3px;
+      background: #fff;
+    }
+    .qrcode {
+      position: absolute;
+      right: -50px;
+      .codeImg {
+        width: 100%;
+      }
+    }
+  }
+  .tips {
+    width: 100%;
+    text-align: center;
+    margin-top: 27px;
+    color: #fff;
+    font-size: 14px;
+    line-height: 21px;
+
+    .downButtom {
+      padding-top: 20px;
+      .downButtomItem {
+        width: 140px;
+        height: 42px;
+        background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/btn01@2x.png)
+          100% 100% no-repeat;
+        background-size: cover;
+        font-weight: 400;
+        font-size: 16px;
+        color: #ffffff;
+        line-height: 19px;
+        font-style: normal;
+        margin: 0 5px;
+        line-height: 42px;
+        text-align: center;
+      }
+    }
+  }
+  .list {
+    height: 555px;
+    width: 100%;
+    // margin-top: 50rpx;
+    box-sizing: border-box;
+    .content-scroll {
+      height: 500px;
+      white-space: nowrap;
+      display: flex;
+      text-align: center;
+      justify-content: center;
+      flex-direction: column;
+      .scroll-item {
+        height: 500px;
+        width: 118px;
+        padding: 0 20rpx;
+        display: inline-block;
+        text-align: center;
+        position: relative;
+        transition: all 0.3s;
+        .source {
+          display: none;
+          font-weight: 400;
+          font-size: 12px;
+          color: #fff9b1;
+          line-height: 18px;
+          writing-mode: vertical-lr;
+          position: absolute;
+          bottom: 23%;
+          right: -10px;
+        }
+        .item-text {
+          // transform: rotateY(180deg);
+          font-size: 30rpx;
+          line-height: 100rpx;
+          position: absolute;
+          top: calc(50% - 133px);
+          left: calc(50% - 34px);
+          &.active {
+            color: #1468ff;
+          }
+        }
+        .activeItem {
+        }
+      }
+      .fang {
+        transform: rotateY(180deg);
+        transition: all 0.5s;
+        .item-text {
+          transform: rotateY(0deg);
+        }
+        .source {
+          display: block;
+          transform: rotateY(180deg);
+        }
+      }
+      .active {
+        .item-text {
+          top: calc(50% - 226px);
+          left: calc(50% - 50px);
+        }
+        .item {
+          width: 50px;
+          height: 453px;
+          background-size: 100% 100%;
+          font-size: 27px;
+          color: #8c4042;
+          line-height: 41px;
+        }
+        .activeItem {
+          transform: rotateY(180deg);
+          width: 100px;
+          height: 263px;
+          padding: 95px 0px;
+          display: inline-block;
+          background-size: 100% 100%;
+          font-size: 12px;
+          color: #303030;
+          line-height: 18px;
+          display: flex;
+          writing-mode: vertical-lr;
+          text-align: left;
+          justify-content: center;
+          flex-direction: column;
+          background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/img_label@2x.png)
+            100% 100% no-repeat;
+          background-size: 100% 100%;
+          position: relative;
+          top: 44px;
+        }
+      }
+    }
+    .swiper {
+      padding: 0 10px;
+      // .listitem {
+      //   text-align: center;
+      //   padding: 0 20px;
+      .item {
+        margin: 20px auto;
+        width: 18px;
+        height: 267px;
+        background: url(https://4dscene.4dage.com/new4dkk/deng/static/img/label@2x.png)
+          100% 100% no-repeat;
+        background-size: cover;
+        writing-mode: tb-rl;
+        text-align: center;
+        padding: 0 25px;
+        position: relative;
+        display: flex;
+        writing-mode: vertical-lr;
+        text-align: center;
+        justify-content: center;
+        flex-direction: column;
+      }
+      // .active {
+      //   &:after {
+      //     content: "";
+      //     box-shadow: 0px 2px 18px 11px #fff86c,
+      //       inset 0px 1px 1px 0px rgba(177, 156, 125, 0.7);
+      //     position: absolute;
+      //     height: 71%;
+      //     width: 80%;
+      //     top: 32px;
+      //     left: 8px;
+      //     border-radius: 25px;
+      //     z-index: -1;
+      //   }
+      // }
+      // }
+    }
+  }
+}
+
+@keyframes handleAni {
+  from {
+    right: 50px;
+  }
+  to {
+    right: 30px;
+  }
+}
+</style>

+ 277 - 0
pages/prayers/webview.vue

@@ -0,0 +1,277 @@
+<template>
+  <div>
+    <web-view src="https://blog.csdn.net/m0_56104994/article/details/131311656"></web-view>
+  </div>
+</template>
+
+<script>
+import wxcode from 'uniapp-qrcode';
+  export default {
+      data() {
+          return {
+              imgUrl: '',
+              statusBarHeight: 0,
+              bgimg: '',
+              textX: 10, //文字x轴坐标
+              avatar: 'https://4dscene.4dage.com/new4dkk/deng/static/img/flower_colorful@2x.png', //头像地址
+              hello: 'https://4dscene.4dage.com/new4dkk/deng/static/img/img_label@2x.png', // hello图标
+              mony: 'https://4dscene.4dage.com/new4dkk/deng/static/img/flower_pink@2x.png' //圆的钱图标
+          }
+      },
+      async created() {
+
+      },
+      async mounted() {
+      },
+      methods: {
+          getBack() {
+              uni.navigateBack({
+                  delta: 1
+              });
+          },
+          listdrawText(ctx, list){
+            let mylist = []
+            list.map(ele => {
+              if(ele && ele.length > 21){
+                mylist.push(ele.substring(21, ele.length))
+                mylist.push(ele.substring(0, 21))
+              }else {
+                mylist.push(ele)
+              }
+              // mylist.push(ele)
+            })
+            this.textX = 200 - (mylist.length/2)* 40;
+            console.log('mylist', mylist, this.textX)
+            mylist.map(ele => {
+              this.drawTextVertical(ctx, ele, this.textX, 160);
+              this.textX += 30;
+            })
+          },
+          drawTextVertical(context, text, xx, y) {
+            let x = xx || 0;
+            var arrText = text.split('');
+            var arrWidth = arrText.map(function (letter) {
+              return 18;
+            });
+            
+            var align = context.textAlign;
+            var baseline = context.textBaseline;
+            if (align == 'left') {
+              x = x + Math.max.apply(null, arrWidth) / 2;
+            } else if (align == 'right') {
+              x = x - Math.max.apply(null, arrWidth) / 2;
+            }
+            if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') {
+              y = y - arrWidth[0] / 2;
+            } else if (baseline == 'top' || baseline == 'hanging') {
+              y = y + arrWidth[0] / 2;
+            }
+
+            context.textAlign = 'center';
+            context.textBaseline = 'middle';
+            context.font = '18px normal'
+            context.setFillStyle("#303030")
+            // 开始逐字绘制
+            arrText.forEach(function (letter, index) {
+              // 确定下一个字符的纵坐标位置
+              var letterWidth = arrWidth[index];
+              // 是否需要旋转判断
+              var code = letter.charCodeAt(0);
+              console.log('arrWidth', arrWidth, align, index, letter)
+
+              if (code <= 256) {
+                context.translate(x, y);
+                // 英文字符,旋转90°
+                context.rotate(90 * Math.PI / 180);
+                context.translate(-x, -y);
+              } else if (index > 0 && text.charCodeAt(index - 1) < 256) {
+                // y修正
+                y = y + arrWidth[index - 1] / 2;
+              }
+              context.fillText(letter, x, y);
+              // 旋转坐标系还原成初始态
+              context.setTransform(1, 0, 0, 1, 0, 0);
+              // 确定下一个字符的纵坐标位置
+              var letterWidth = arrWidth[index];
+              y = y + letterWidth;
+            });
+            // 水平垂直对齐方式还原
+            context.textAlign = align;
+            context.textBaseline = baseline;
+          },
+          saveImage() { //点击保存
+              var _this = this;
+              uni.saveImageToPhotosAlbum({
+                  filePath: _this.imgUrl,
+                  success() {
+                      uni.showModal({
+                          title: "保存成功",
+                          content: "图片已成功保存到相册,快去分享到您的圈子吧",
+                          showCancel: false
+                      })
+                  }
+              })
+          },
+          // 将网络图片转为临时图片地址
+          async getImageInfo({imgSrc}) {
+              return new Promise((resolve, errs) => {
+                  uni.downloadFile({
+                      src: imgSrc,
+                      success: function(image) {
+                          resolve(image);
+                      },
+                      fail(err) {
+                          errs(err);
+                      }
+                  });
+              });
+          },
+          // 绘制圆形头像
+          drawCircular(ctx, url, x, y, width, height) {
+              //画圆形头像
+              var avatarurl_width = width;
+              var avatarurl_heigth = height;
+              var avatarurl_x = x;
+              var avatarurl_y = y;
+              ctx.save(); //先保存状态,已便于画完园再用
+              ctx.beginPath(); //开始绘制
+              ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math
+                  .PI * 2, false);
+              ctx.setFillStyle("#FFFFFF")
+              ctx.fill() //保证图片无bug填充
+              ctx.clip(); //剪切
+              ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); //推进去图片
+              ctx.restore();
+          },
+          
+          // 绘制矩形图片
+          drawrectcular(ctx, url, x, y, width, height) {
+              //画圆形头像
+              var avatarurl_width = width;
+              var avatarurl_heigth = height;
+              var avatarurl_x = x;
+              var avatarurl_y = y;
+              ctx.save(); //先保存状态,已便于画完园再用
+              ctx.beginPath(); //开始绘制
+              ctx.rect(x, y, width, height);
+              // ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math
+              //     .PI * 2, false);
+              ctx.setFillStyle("#FFFFFF")
+              ctx.fill() //保证图片无bug填充
+              ctx.clip(); //剪切
+              ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); //推进去图片
+              ctx.restore();
+          },
+          // 绘制带圆角的矩形方法
+          fillRoundRect(cxt, x, y, width, height, radius,  fillColor) {
+              //圆的直径必然要小于矩形的宽高
+              if (2 * radius > width || 2 * radius > height) {
+                  return false;
+              }
+
+              cxt.save();
+              cxt.translate(x, y);
+              //绘制圆角矩形的各个边
+              this.drawRoundRectPath(cxt, width, height, radius);
+              cxt.fillStyle = fillColor || '#fff'; //若是给定了值就用给定的值否则给予默认值
+              cxt.fill();
+              cxt.restore();
+          },
+          drawRoundRectPath(cxt, width, height, radius) {
+              cxt.beginPath(0);
+              //从右下角顺时针绘制,弧度从0到1/2PI
+              cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
+
+              //矩形下边线
+              cxt.lineTo(radius, height);
+
+              //左下角圆弧,弧度从1/2PI到PI
+              cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
+
+              //矩形左边线
+              cxt.lineTo(0, radius);
+
+              //左上角圆弧,弧度从PI到3/2PI
+              cxt.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2);
+
+              //上边线
+              cxt.lineTo(width - radius, 0);
+
+              //右上角圆弧
+              cxt.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2);
+
+              //右边线
+              cxt.lineTo(width, height - radius);
+              cxt.closePath();
+          }
+      }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .canvas-box {
+      height: 100vh;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background-color: #1ABC9C;
+
+      /deep/.nav-box {
+          width: 100%;
+          padding: 0 20rpx;
+          position: absolute;
+          z-index: 9999;
+          top: 0;
+          left: 0;
+
+          .title-top {
+              font-size: 36rpx;
+              font-weight: 550;
+              color: #FFFFFF;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              position: relative;
+              margin-bottom: 30rpx;
+
+              .title-icon {
+                  position: absolute;
+                  left: 0;
+              }
+          }
+      }
+
+      image {
+          width: 335px;
+          height: 500px;
+      }
+
+      .footer {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          position: absolute;
+          justify-content: center;
+          padding: 0 40rpx;
+          width: 100%;
+          left: 0;
+          bottom: 10%;
+
+          .download {
+              border: 1rpx solid #ffffff;
+              color: #ffffff;
+              display: flex;
+              align-items: center;
+          }
+
+          view {
+              padding: 0 20rpx;
+              height: 78rpx;
+              text-align: center;
+              line-height: 78rpx;
+              font-size: 30rpx;
+              border-radius: 36rpx;
+          }
+      }
+  }
+</style>

+ 93 - 0
pages/prayers/wx2canvas.js

@@ -0,0 +1,93 @@
+import Wxml2Canvas from 'wxml2canvas'
+export const startDraw = ()=> {
+  let that = this
+  // 创建wxml2canvas对象
+  let drawMyImage = new Wxml2Canvas({
+    element: 'myCanvas', // canvas的id,
+    obj: that, // 传入当前组件的this
+    width: 200* 2,
+    height: 200 * 2,
+    background: '#141415', // 生成图片的背景色
+    progress(percent) { // 进度
+      console.log(percent);
+    },
+    finish(url) { // 生成的图片
+      wx.hideLoading()
+      savePoster(url)
+    },
+    error(res) { // 失败原因
+      console.log(res);
+      wx.hideLoading()
+    }
+  }, this);
+  let data = {
+      // 获取wxml数据
+      list: [{
+          type: 'wxml',
+          class: '.my_canvas .my_draw_canvas',  // my_canvas要绘制的wxml元素根类名, my_draw_canvas单个元素的类名(所有要绘制的单个元素都要添加该类名)
+          limit: '.my_canvas', // 要绘制的wxml元素根类名
+          x: 0,
+          y: 0
+      }]
+  }
+  // 绘制canvas
+  drawMyImage.draw(data, this);
+}
+export const drawMyCanvas = () => {
+  wx.showLoading()
+  const that = this
+  wx.createSelectorQuery()
+    .select('#my-canvas') // 在 WXML 中填入的 id
+    .fields({ scrollOffset: true, size: true }, () => {
+      startDraw()
+    }).exec(() => {
+      console.log(888)
+    })
+} 
+
+export const savePoster = (url) => {
+  const that = this
+  wx.saveImageToPhotosAlbum({
+    filePath: url,
+    success: function() {
+        wx.showToast({
+            title: '保存成功',
+            icon: 'none',
+            duration: 1500
+        });
+    },
+    fail(err) {
+      if (err.errMsg === "saveImageToPhotosAlbum:fail:auth denied" || err.errMsg === "saveImageToPhotosAlbum:fail auth deny" || err.errMsg === "saveImageToPhotosAlbum:fail authorize no response") {
+        wx.showModal({
+          title: '提示',
+          content: '需要您授权保存相册',
+          showCancel: false,
+          success: modalSuccess => {
+            wx.openSetting({
+              success(settingdata) {
+                if (settingdata.authSetting['scope.writePhotosAlbum']) {
+                    wx.saveImageToPhotosAlbum({
+                        filePath: url,
+                        success: function () {
+                          wx.showToast({
+                            title: '保存成功',
+                            icon: 'success',
+                            duration: 2000
+                          })
+                        },
+                    })
+                } else {
+                    wx.showToast({
+                        title: '授权失败,请稍后重新获取',
+                        icon: 'none',
+                        duration: 1500
+                    });
+                }
+              }
+            })
+          }
+        })
+      }
+    }
+  })
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2307 - 0
pnpm-lock.yaml


+ 63 - 0
store/index.js

@@ -0,0 +1,63 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+Vue.use(Vuex) // vue的插件机制
+let userInfo = uni.getStorageSync('userInfo')
+// Vuex.Store 构造器选项
+const store = new Vuex.Store({
+    // 为了不和页面或组件的data中的造成混淆,state中的变量前面建议加上$符号
+    state: {
+        active: 4,
+        token: uni.getStorageSync('token') || '',
+        // 用户信息
+        userInfo: userInfo && JSON.parse(userInfo) || {}
+    },
+    getters: {
+        // 获取用户信息
+        getActive(state) {
+            return state.active
+        },
+        getToken(state) {
+            return state.token || uni.getStorageSync('token')
+        }
+    },
+    mutations: {
+        // 修改用户信息
+        
+        changeUseinf(state, payload) {
+            payload().then(res => {
+                console.log('wxLoginRes', res, state);
+                state.userInfo = res
+            })
+            // state.userInfo = payload
+        },
+        changeActive(state, payload) {
+            state.active = payload
+        },
+        wxLogininfo(state, payload) {
+            console.log('wxLogin', state, payload)
+            uni.login({
+                provider: 'weixin', //使用微信登录
+                success: function ({ code }) {
+                    console.log('loginRes', code);
+                    payload({
+                        code: code
+                    }).then(res => {
+                        console.log('wxLoginRes', res, code);
+                        uni.$u.toast("登录成功");
+                        state.token = res.token;
+                        state.userInfo = res.user;
+                        uni.setStorageSync('token', res.token);
+                        uni.setStorageSync('userInfo', JSON.stringify(res.user));
+                        uni.$u.route("/pages/home/webview",
+                            {
+                            url: 'https://sit-nanhuacs.4dage.com/web/index.html#/home',
+                            data: 20
+                        });
+                    })
+                }
+            });
+        }
+    }
+})
+
+export default store

+ 42 - 0
template.h5.html

@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<link rel="shortcut icon" type="image/x-icon" href="https://cdn.uviewui.com/uview/common/favicon.webp">
+		<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+		<title>
+			<%= htmlWebpackPlugin.options.title %>
+		</title>
+		<!-- 正式发布的时候使用,开发期间不启用。↓ -->
+        <script src="/static/common/js/touch-emulator.js"></script>
+		<script>
+            TouchEmulator();
+		</script>
+        <style>
+            ::-webkit-scrollbar{
+                display: none;
+            }
+        </style>
+        <!-- 正式发布的时候使用,开发期间不启用。↑ -->
+		<script>
+			document.addEventListener('DOMContentLoaded', function() {
+				document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
+			})
+		</script>
+		<link rel="stylesheet" href="<%= BASE_URL %>static/index.css" />
+	</head>
+	<body>
+		<!-- 该文件为 H5 平台的模板 HTML,并非应用入口。 -->
+		<!-- 请勿在此文件编写页面代码或直接运行此文件。 -->
+		<!-- 详见文档:https://uniapp.dcloud.io/collocation/manifest?id=h5-template -->
+		<noscript>
+			<strong>本站点必须要开启JavaScript才能运行</strong>
+		</noscript>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+		<script>
+			/*BAIDU_STAT*/
+		</script>
+	</body>
+</html>

+ 69 - 0
tree.md

@@ -0,0 +1,69 @@
+```
+.
+├── common					#演示需要的一些文件
+│   ├── api.js
+│   ├── config.js
+│   ├── demo.scss
+│   ├── mixin.js
+│   └── props.js
+├── components					#演示项目封装的组件
+│   └── page-nav
+│       └── page-nav.vue
+├── pages					#页面
+│   ├── componentsA				#分包A
+│   │   ├── ...
+│   │   ├── ...
+│   ├── componentsB				#分包B
+│   │   ├── ...
+│   │   ├── ...
+│   ├── componentsC				#分包C
+│   │   ├── ...
+│   │   ├── ...
+│   └── example					#演示项目首页
+│       ├── components.config.js		#演示页面数据
+│       └── components.nvue			#主演示页面
+├── static					#演示项目需要的一些文件
+│   ├── app-plus
+│   │   └── mp-html
+│   ├── common
+│   │   └── js
+│   └── uview
+│       ├── common
+│       └── example
+├── store
+│   └── index.js
+├── uni_modules
+│   └── uview-ui				#uView2.0主包
+│       ├── LICENSE
+│       ├── README.md
+│       ├── changelog.md
+│       ├── components				#所有的组件
+│       ├── index.js
+│       ├── index.scss
+│       ├── libs
+│       ├── package.json
+│       └── theme.scss
+├── unpackage
+│   └── res
+│       └── icons
+├── util
+│   └── request
+│       ├── index.js
+│       ├── requestInterceptors.js
+│       └── responseInterceptors.js
+├── App.vue
+├── LICENSE
+├── main.js
+├── manifest.json
+├── package-lock.json
+├── pages.json					#页面配置
+├── package.json
+├── README.md
+├── template.h5.html				#h5模板
+├── tree.md
+├── uni.scss
+└── vue.config.js
+
+
+created by beiqiao.
+```

+ 6 - 0
uni.scss

@@ -0,0 +1,6 @@
+/**
+ * 下方引入的为uView UI的集成样式文件,为scss预处理器,其中包含了一些"u-"开头的自定义变量
+ * 使用的时候,请将下面的一行复制到您的uniapp项目根目录的uni.scss中即可
+ * uView自定义的css类名和scss变量,均以"u-"开头,不会造成冲突,请放心使用 
+ */
+@import '@/uni_modules/uview-ui/theme.scss';

+ 200 - 0
uni_modules/lime-echart/changelog.md

@@ -0,0 +1,200 @@
+## 0.9.6(2024-07-23)
+- fix: 修复 uni is not defined
+## 0.9.5(2024-07-19)
+- chore: 鸿蒙`measureText`为异步,异步字体不正常,使用模拟方式。
+## 0.9.4(2024-07-18)
+- chore: 更新文档
+## 0.9.3(2024-07-16)
+- feat: 鸿蒙 canvas 事件缺失,待官方修复,如何在鸿蒙使用请看文档`常见问题 vue3` 
+## 0.9.2(2024-07-12)
+- chore: 删除多余文件
+## 0.9.1(2024-07-12)
+- fix: 修复 安卓5不显示图表问题
+## 0.9.0(2024-06-13)
+- chore: 合并nvue和uvue
+## 0.8.9(2024-05-19)
+- chore: 更新文档
+## 0.8.8(2024-05-13)
+- chore: 更新文档和uvue示例
+## 0.8.7(2024-04-26)
+- fix: uniapp x需要HBX 4.13以上
+## 0.8.6(2024-04-10)
+- feat: 支持 uniapp x ios
+## 0.8.5(2024-04-03)
+- fix: 修复 nvue `reset`传值不生效问题
+- feat: 支持 uniapp x web
+## 0.8.4(2024-01-27)
+- chore: 更新文档
+## 0.8.3(2024-01-21)
+- chore: 更新文档
+## 0.8.2(2024-01-21)
+- feat: 支持 `uvue`
+## 0.8.1(2023-08-24)
+- fix: app 的`touch`事件为`object` 导致无法显示 `tooltip`
+## 0.8.0(2023-08-22)
+- fix: 离屏 报错问题
+- fix: 微信小程序PC无法使用事件
+- chore: 更新文档
+## 0.7.9(2023-07-29)
+- chore: 更新文档
+## 0.7.8(2023-07-29)
+- fix: 离屏 报错问题
+## 0.7.7(2023-07-27)
+- chore: 更新文档
+- chore: lime-echart 里的示例使用自定tooltips
+- feat: 对支持离屏的使用离屏创建(微信、字节、支付宝)
+## 0.7.6(2023-06-30)
+- fix: vue3 报`width`的错
+## 0.7.5(2023-05-25)
+- chore: 更新文档 和 demo, 使用`lime-echart`这个标签即可查看示例
+## 0.7.4(2023-05-22)
+- chore: 增加关于钉钉小程序上传时提示安全问题的说明及修改建议
+## 0.7.3(2023-05-16)
+- chore: 更新 vue3 非微信小程序平台可能缺少`wx`的说明
+## 0.7.2(2023-05-16)
+- chore: 更新 vue3 非微信小程序平台的可以缺少`wx`的说明
+## 0.7.1(2023-04-26)
+- chore: 更新demo,使用`lime-echart`这个标签即可查看示例
+- chore:微信小程序的`tooltip`文字有阴影,怀疑是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
+## 0.7.0(2023-04-24)
+- fix: 修复`setAttribute is not a function`
+## 0.6.9(2023-04-15)
+- chore: 更新文档,vue3请使用echarts esm的包
+## 0.6.8(2023-03-22)
+- feat: mac pc无法使用canvas 2d
+## 0.6.7(2023-03-17)
+- feat: 更新文档
+## 0.6.6(2023-03-17)
+- feat: 微信小程序PC已经支持canvas 2d,故去掉判断PC
+## 0.6.5(2022-11-03)
+- fix: 某些手机touches为对象,导致无法交互。
+## 0.6.4(2022-10-28)
+- fix: 优化点击事件的触发条件
+## 0.6.3(2022-10-26)
+- fix: 修复 dataZoom 拖动问题
+## 0.6.2(2022-10-23)
+- fix: 修复 飞书小程序 尺寸问题
+## 0.6.1(2022-10-19)
+- fix: 修复 PC mousewheel 事件 鼠标位置不准确的BUG,不兼容火狐!
+- feat: showLoading 增加传参
+## 0.6.0(2022-09-16)
+- feat: 增加PC的mousewheel事件
+## 0.5.4(2022-09-16)
+- fix: 修复 nvue 动态数据不显示问题
+## 0.5.3(2022-09-16)
+- feat: 增加enableHover属性, 在PC端时当鼠标进入显示tooltip,不必按下。
+- chore: 更新文档
+## 0.5.2(2022-09-16)
+- feat: 增加enableHover属性, 在PC端时当鼠标进入显示tooltip,不必按下。
+## 0.5.1(2022-09-16)
+- fix: 修复nvue报错
+## 0.5.0(2022-09-15)
+- feat: init(echarts, theme?:string, opts?:{}, callback: function(chart))
+## 0.4.8(2022-09-11)
+- feat: 增加 @finished
+## 0.4.7(2022-08-24)
+- chore: 去掉 stylus
+## 0.4.6(2022-08-24)
+- feat: 增加 beforeDelay
+## 0.4.5(2022-08-12)
+- chore: 更新文档
+## 0.4.4(2022-08-12)
+- fix: 修复 resize 无参数时报错
+## 0.4.3(2022-08-07)
+# 评论有说本插件对新手不友好,让我做不好就不要发出来。 还有的说跟官网一样,发出来做什么,给我整无语了。
+# 所以在此提醒一下准备要下载的你,如果你从未使用过 echarts 请不要下载 或 谨慎下载。
+# 如果你确认要下载,麻烦看完文档。还有请注意插件是让echarts在uniapp能运行,API 配置请自行去官网查阅!
+# 如果你不会echarts 但又需要图表,市场上有个很优秀的图表插件 uchart 你可以去使用这款插件,uchart的作者人很好,也热情。
+# 每个人都有自己的本职工作,如果你能力强可以自行兼容,如果使用了他人的插件也麻烦尊重他人的成果和劳动时间。谢谢。
+# 为了心情愉悦,本人已经使用插件屏蔽差评。
+- chore: 更新文档
+## 0.4.2(2022-07-20)
+- feat: 增加 resize
+## 0.4.1(2022-06-07)
+- fix: 修复 canvasToTempFilePath 不生效问题
+## 0.4.0(2022-06-04)
+- chore 为了词云 增加一个canvas 标签
+- 词云下载地址[echart-wordcloud](https://ext.dcloud.net.cn/plugin?id=8430)
+## 0.3.9(2022-06-02)
+- chore: 更新文档
+- tips: lines 不支持 `trailLength`
+## 0.3.8(2022-05-31)
+- fix: 修复 因mouse事件冲突tooltip跳动问题
+## 0.3.7(2022-05-26)
+- chore: 更新文档
+- chore: 设置默认宽高300px
+- fix: 修复 vue3 微信小程序 拖影BUG
+- chore: 支持PC
+## 0.3.5(2022-04-28)
+- chore: 更新使用方式
+- 🔔 必须使用hbuilderx 3.4.8-alpha以上
+## 0.3.4(2021-08-03)
+- chore: 增加 setOption的参数值
+## 0.3.3(2021-07-22)
+- fix: 修复 径向渐变报错的问题
+## 0.3.2(2021-07-09)
+- chore: 统一命名规范,无须主动引入组件
+## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
+## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.3.1(2021-06-21)
+- fix: 修复 app-nvue ios is-enable 无效的问题
+## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
+## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.3.0(2021-06-14)
+- fix: 修复 头条系小程序 2d 报 JSON.stringify 的问题
+- 目前 头条系小程序 2d 无法在开发工具上预览,划动图表页面无法滚动,axisLabel 字体颜色无法更改,建议使用非2d。
+## 0.2.9(2021-06-06)
+- fix: 修复 头条系小程序 2d 放大的BUG 
+- 头条系小程序 2d 无法在开发工具上预览,也存在划动图表页面无法滚动的问题。
+## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.8(2021-05-19)
+- fix: 修复 微信小程序 PC 显示过大的问题
+## 0.2.7(2021-05-19)
+- fix: 修复 微信小程序 PC 不显示问题
+## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.6(2021-05-14)
+- feat: 支持 `image`
+- feat: props 增加 `ec.clear`,更新时是否先删除图表样式 
+- feat: props 增加 `isDisableScroll` ,触摸图表时是否禁止页面滚动
+- feat: props 增加 `webviewStyles` ,webview 的样式, 仅nvue有效
+## 0.2.5(2021-05-13)
+- docs: 插件用到了css 预编译器 [stylus](https://ext.dcloud.net.cn/plugin?name=compile-stylus) 请安装它
+## 0.2.4(2021-05-12)
+- fix: 修复 百度平台 多个图表ctx 和 渐变色 bug
+- ## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.3(2021-05-10)
+- feat: 增加 `canvasToTempFilePath` 方法,用于生成图片
+```js
+this.$refs.chart.canvasToTempFilePath({success: (res) => {
+	console.log('tempFilePath:', res.tempFilePath)
+}})
+```
+## 0.2.2(2021-05-10)
+- feat: 增加 `dispose` 方法,用于销毁实例
+- feat: 增加 `isClickable` 是否派发点击
+- feat: 实验性的支持 `nvue` 使用要慎重考虑
+- ## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.1(2021-05-06)
+- fix:修复 微信小程序 json 报错
+- chore: `reset` 更改为 `setChart`
+- feat: 增加 `isEnable` 开启初始化 启用这个后 无须再使用`init`方法
+```html
+<l-echart ref="chart" is-enable />
+```
+```js
+// 显示加载
+this.$refs.chart.showLoading()
+// 使用实例回调
+this.$refs.chart.setChart(chart => ...code)
+// 直接设置图表配置
+this.$refs.chart.setOption(data)
+```
+## 0.2.0(2021-05-05)
+- fix:修复 头条 百度 偏移的问题
+- docs: 更新文档
+## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.1.0(2021-05-02)
+- chore:  第一次上传,基本全端兼容,使用方法与官网一致。
+- 已知BUG:非2d 无法使用背景色,已反馈官方
+- 已知BUG:头条 百度 有许些偏移
+- 后期计划:兼容nvue

+ 393 - 0
uni_modules/lime-echart/components/l-echart/canvas.js

@@ -0,0 +1,393 @@
+const cacheChart = {}
+const fontSizeReg = /([\d\.]+)px/;
+class EventEmit {
+	constructor() {
+		this.__events = {};
+	}
+	on(type, listener) {
+		if (!type || !listener) {
+			return;
+		}
+		const events = this.__events[type] || [];
+		events.push(listener);
+		this.__events[type] = events;
+	}
+	emit(type, e) {
+		if (type.constructor === Object) {
+			e = type;
+			type = e && e.type;
+		}
+		if (!type) {
+			return;
+		}
+		const events = this.__events[type];
+		if (!events || !events.length) {
+			return;
+		}
+		events.forEach((listener) => {
+			listener.call(this, e);
+		});
+	}
+	off(type, listener) {
+		const __events = this.__events;
+		const events = __events[type];
+		if (!events || !events.length) {
+			return;
+		}
+		if (!listener) {
+			delete __events[type];
+			return;
+		}
+		for (let i = 0, len = events.length; i < len; i++) {
+			if (events[i] === listener) {
+				events.splice(i, 1);
+				i--;
+			}
+		}
+	}
+}
+class Image {
+	constructor() {
+		this.currentSrc = null
+		this.naturalHeight = 0
+		this.naturalWidth = 0
+		this.width = 0
+		this.height = 0
+		this.tagName = 'IMG'
+	}
+	set src(src) {
+		this.currentSrc = src
+		uni.getImageInfo({
+			src,
+			success: (res) => {
+				this.naturalWidth = this.width = res.width
+				this.naturalHeight = this.height = res.height
+				this.onload()
+			},
+			fail: () => {
+				this.onerror()
+			}
+		})
+	}
+	get src() {
+		return this.currentSrc
+	}
+}
+class OffscreenCanvas {
+	constructor(ctx, com, canvasId) {
+		this.tagName = 'canvas'
+		this.com = com
+		this.canvasId = canvasId
+		this.ctx = ctx
+	}
+	set width(w) {
+		this.com.offscreenWidth = w
+	}
+	set height(h) {
+		this.com.offscreenHeight = h
+	}
+	get width() {
+		return this.com.offscreenWidth || 0
+	}
+	get height() {
+		return this.com.offscreenHeight || 0
+	}
+	getContext(type) {
+		return this.ctx
+	}
+	getImageData() {
+		return new Promise((resolve, reject) => {
+			this.com.$nextTick(() => {
+				uni.canvasGetImageData({
+					x:0,
+					y:0,
+					width: this.com.offscreenWidth,
+					height: this.com.offscreenHeight,
+					canvasId: this.canvasId,
+					success: (res) => {
+						resolve(res)
+					},
+					fail: (err) => {
+						reject(err)
+					},
+				}, this.com)
+			})
+		})
+	}
+}
+export class Canvas {
+	constructor(ctx, com, isNew, canvasNode={}) {
+		cacheChart[com.canvasId] = {ctx}
+		this.canvasId = com.canvasId;
+		this.chart = null;
+		this.isNew = isNew
+		this.tagName = 'canvas'
+		this.canvasNode = canvasNode;
+		this.com = com;
+		if (!isNew) {
+			this._initStyle(ctx)
+		}
+		this._initEvent();
+		this._ee = new EventEmit()
+	}
+	getContext(type) {
+		if (type === '2d') {
+			return this.ctx;
+		}
+	}
+	setAttribute(key, value) {
+		if(key === 'aria-label') {
+			this.com['ariaLabel'] = value
+		}
+	}
+	setChart(chart) {
+		this.chart = chart;
+	}
+	createOffscreenCanvas(param){
+		if(!this.children) {
+			this.com.isOffscreenCanvas = true
+			this.com.offscreenWidth = param.width||300
+			this.com.offscreenHeight = param.height||300
+			const com = this.com
+			const canvasId = this.com.offscreenCanvasId
+			const context = uni.createCanvasContext(canvasId, this.com)
+			this._initStyle(context)
+			this.children = new OffscreenCanvas(context, com, canvasId)
+		} 
+		return this.children
+	}
+	appendChild(child) {
+		console.log('child', child)
+	}
+	dispatchEvent(type, e) {
+		if(typeof type == 'object') {
+			this._ee.emit(type.type, type);
+		} else {
+			this._ee.emit(type, e);
+		}
+		return true
+	}
+	attachEvent() {
+	}
+	detachEvent() {
+	}
+	addEventListener(type, listener) {
+		this._ee.on(type, listener)
+	}
+	removeEventListener(type, listener) {
+		this._ee.off(type, listener)
+	}
+	_initCanvas(zrender, ctx) {
+		// zrender.util.getContext = function() {
+		// 	return ctx;
+		// };
+		// zrender.util.$override('measureText', function(text, font) {
+		// 	ctx.font = font || '12px sans-serif';
+		// 	return ctx.measureText(text, font);
+		// });
+	}
+	_initStyle(ctx, child) {
+		const styles = [
+			'fillStyle',
+			'strokeStyle',
+			'fontSize',
+			'globalAlpha',
+			'opacity',
+			'textAlign',
+			'textBaseline',
+			'shadow',
+			'lineWidth',
+			'lineCap',
+			'lineJoin',
+			'lineDash',
+			'miterLimit',
+			// #ifdef H5
+			'font',
+			// #endif
+		];
+		const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g;
+		styles.forEach(style => {
+			Object.defineProperty(ctx, style, {
+				set: value => {
+					// #ifdef H5
+					if (style === 'font' && fontSizeReg.test(value)) {
+						const match = fontSizeReg.exec(value);
+						ctx.setFontSize(match[1]);
+						return;
+					}
+					// #endif
+					
+					if (style === 'opacity') {
+						ctx.setGlobalAlpha(value)
+						return;
+					}
+					if (style !== 'fillStyle' && style !== 'strokeStyle' || value !== 'none' && value !== null) {
+						// #ifdef H5 || APP-PLUS || MP-BAIDU
+						if(typeof value == 'object') {
+							if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
+								ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+							}
+							return
+						} 
+						// #endif
+						// #ifdef MP-TOUTIAO
+						if(colorReg.test(value)) {
+							value = value.replace(colorReg, '#$1$1$2$2$3$3')
+						}
+						// #endif
+						ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+					}
+				}
+			});
+		});
+		if(!this.isNew && !child) {
+			ctx.uniDrawImage = ctx.drawImage
+			ctx.drawImage = (...a) => {
+				a[0] = a[0].src
+				ctx.uniDrawImage(...a)
+			}
+		}
+		if(!ctx.createRadialGradient) {
+			ctx.createRadialGradient = function() {
+				return ctx.createCircularGradient(...[...arguments].slice(-3))
+			};
+		}
+		// 字节不支持
+		if (!ctx.strokeText) {
+			ctx.strokeText = (...a) => {
+				ctx.fillText(...a)
+			}
+		}
+		
+		// 钉钉不支持 , 鸿蒙是异步
+		if (!ctx.measureText || uni.getSystemInfoSync().osName == 'harmonyos') {
+			ctx._measureText = ctx.measureText
+			const strLen = (str) => {
+				let len = 0;
+				for (let i = 0; i < str.length; i++) {
+					if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
+						len++;
+					} else {
+						len += 2;
+					}
+				}
+				return len;
+			}
+			ctx.measureText = (text, font) => {
+				let fontSize = ctx?.state?.fontSize || 12;
+				if (font) {
+					fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
+				}
+				fontSize /= 2;
+				let isBold = fontSize >= 16;
+				const widthFactor = isBold ? 1.3 : 1;
+				// ctx._measureText(text, (res) => {})
+				return {
+					width: strLen(text) * fontSize * widthFactor
+				};
+			}
+		}
+	}
+
+	_initEvent(e) {
+		this.event = {};
+		const eventNames = [{
+			wxName: 'touchStart',
+			ecName: 'mousedown'
+		}, {
+			wxName: 'touchMove',
+			ecName: 'mousemove'
+		}, {
+			wxName: 'touchEnd',
+			ecName: 'mouseup'
+		}, {
+			wxName: 'touchEnd',
+			ecName: 'click'
+		}];
+
+		eventNames.forEach(name => {
+			this.event[name.wxName] = e => {
+				const touch = e.touches[0];
+				this.chart.getZr().handler.dispatch(name.ecName, {
+					zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
+					zrY: name.wxName === 'tap' ? touch.clientY : touch.y
+				});
+			};
+		});
+	}
+
+	set width(w) {
+		this.canvasNode.width = w
+	}
+	set height(h) {
+		this.canvasNode.height = h
+	}
+
+	get width() {
+		return this.canvasNode.width || 0
+	}
+	get height() {
+		return this.canvasNode.height || 0
+	}
+	get ctx() {
+		return cacheChart[this.canvasId]['ctx'] || null
+	}
+	set chart(chart) {
+		cacheChart[this.canvasId]['chart'] = chart
+	}
+	get chart() {
+		return cacheChart[this.canvasId]['chart'] || null
+	}
+}
+
+export function dispatch(name, {x,y, wheelDelta}) {
+	this.dispatch(name, {
+		zrX: x,
+		zrY: y,
+		zrDelta: wheelDelta,
+		preventDefault: () => {},
+		stopPropagation: () =>{}
+	});
+}
+export function setCanvasCreator(echarts, {canvas, node}) {
+	// echarts.setCanvasCreator(() => canvas);
+	if(echarts && !echarts.registerPreprocessor) {
+		return console.warn('echarts 版本不对或未传入echarts,vue3请使用esm格式')
+	}
+	echarts.registerPreprocessor(option => {
+		if (option && option.series) {
+			if (option.series.length > 0) {
+				option.series.forEach(series => {
+					series.progressive = 0;
+				});
+			} else if (typeof option.series === 'object') {
+				option.series.progressive = 0;
+			}
+		}
+	});
+	function loadImage(src, onload, onerror) {
+		let img = null
+		if(node && node.createImage) {
+			img = node.createImage()
+			img.onload = onload.bind(img);
+			img.onerror = onerror.bind(img);
+			img.src = src;
+			return img
+		} else {
+			img = new Image()
+			img.onload = onload.bind(img)
+			img.onerror = onerror.bind(img);
+			img.src = src
+			return img
+		}
+	}
+	if(echarts.setPlatformAPI) {
+		echarts.setPlatformAPI({
+			loadImage: canvas.setChart ? loadImage : null,
+			createCanvas(){
+				const key = 'createOffscreenCanvas'
+				return uni.canIUse(key) && uni[key] ? uni[key]({type: '2d'}) : canvas
+			}
+		})
+	}
+}

+ 252 - 0
uni_modules/lime-echart/components/l-echart/l-echart.uvue

@@ -0,0 +1,252 @@
+<template>
+	<!-- #ifdef APP -->
+	<web-view class="lime-echart" ref="chartRef" @load="loaded" :style="[customStyle]" 
+		:webview-styles="[webviewStyles]" src="/uni_modules/lime-echart/static/uvue.html?v=10112">
+	</web-view>
+	<!-- #endif -->
+	<!-- #ifdef H5 -->
+	<div class="lime-echart" ref="chartRef"></div>
+	<!-- #endif -->
+</template>
+
+<script lang="uts" setup>
+	// @ts-nocheck
+	import { Echarts } from './uvue';
+	type EchartsResolve = (value : Echarts) => void
+	defineOptions({
+		name: 'l-echart'
+	})
+	const emits = defineEmits(['finished'])
+	const props = defineProps({
+		// #ifdef APP
+		webviewStyles: {
+			type: Object
+		},
+		customStyle: {
+			type: Object
+		},
+		// #endif
+		// #ifndef APP
+		webviewStyles: {
+			type: Object
+		},
+		customStyle: {
+			type: [String, Object]
+		},
+		// #endif
+		isDisableScroll: {
+			type: Boolean,
+			default: false
+		},
+		isClickable: {
+			type: Boolean,
+			default: true
+		},
+		enableHover: {
+			type: Boolean,
+			default: false
+		},
+		beforeDelay: {
+			type: Number,
+			default: 30
+		}
+	})
+
+	const finished = ref(false)
+	const map = [] as EchartsResolve[]
+	const callbackMap = [] as EchartsResolve[]
+	// let context = null as UniWebViewElement | null
+	let chart = null as Echarts | null
+	let chartRef = ref<UniWebViewElement | null>(null)
+	
+	const trigger = () => {
+		// #ifdef APP
+		if (finished.value) {
+			if (chart == null) {
+				chart = new Echarts(chartRef.value!)
+			}
+			while (map.length > 0) {
+				const resolve = map.pop() as EchartsResolve
+				resolve(chart!)
+			}
+		}
+		// #endif
+		// #ifdef H5
+		while (map.length > 0) {
+			if(chart != null){
+				const resolve = map.pop() as EchartsResolve
+				resolve(chart!)
+			}
+		}
+		// #endif
+		
+		if(chart != null){
+			while(callbackMap.length > 0){
+				const callback = callbackMap.pop() as EchartsResolve
+				callback(chart!)
+			}
+		}
+	}
+	
+	// #ifdef APP
+	const loaded = (event : UniWebViewLoadEvent) => {
+		event.stopPropagation()
+		event.preventDefault()
+		finished.value = true
+		trigger()
+		emits('finished')
+	}
+	// #endif
+	
+	
+	const _next = () : boolean => {
+		if (chart == null) {
+			console.warn(`组件还未初始化,请先使用 init`)
+			return true
+		}
+		return false
+	}
+	const setOption = (option : UTSJSONObject) => {
+		if (_next()) return
+		chart!.setOption(option);
+	}
+	const showLoading = () => {
+		if (_next()) return
+		chart!.showLoading();
+	}
+	const hideLoading = () => {
+		if (_next()) return
+		chart!.hideLoading();
+	}
+	const clear = () => {
+		if (_next()) return
+		chart!.clear();
+	}
+	const dispose = () => {
+		if (_next()) return
+		chart!.dispose();
+	}
+	const resize = (size : UTSJSONObject) => {
+		if (_next()) return
+		chart!.resize(size);
+	}
+	const canvasToTempFilePath = (opt : UTSJSONObject) => {
+		if (_next()) return
+		chart!.canvasToTempFilePath(opt);
+	}
+	// function init() : Promise<Echarts> {
+	// 	return new Promise((resolve) => {
+	// 		map.push(resolve)
+	// 		trigger()
+	// 	})
+	// }
+	// #ifdef APP
+	function init(callback : ((chart : Echarts) => void) | null) : Promise<Echarts> {
+		// if (chart !== null && callback != null) {
+		// 	callback(chart!)
+		// } else {
+		// 	console.warn('echarts 未加载完成,您可以延时一下')
+		// }
+		if(callback!=null){
+			callbackMap.push(callback)
+		}
+		return new Promise<Echarts>((resolve) => {
+			map.push(resolve)
+			trigger()
+		})
+	}
+	// #endif
+	// #ifdef H5
+	const touchstart = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		const rect = chart.getZr().dom.getBoundingClientRect()
+		handler.dispatch('mousedown', {
+			zrX: e.touches[0].clientX - rect.left,
+			zrY: e.touches[0].clientY - rect.top
+		})
+		handler.dispatch('click', {
+			zrX: e.touches[0].clientX - rect.left,
+			zrY: e.touches[0].clientY - rect.top
+		})
+	}
+	const touchmove = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		const rect = chart.getZr().dom.getBoundingClientRect()
+		handler.dispatch('mousemove', {
+			zrX: e.touches[0].clientX - rect.left,
+			zrY: e.touches[0].clientY - rect.top
+		})
+	}
+	const mouseup = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		handler.dispatch('mousemove', {
+			zrX: 999999999,
+			zrY: 999999999
+		})
+		handler.dispatch('mouseup', {
+			zrX: 999999999,
+			zrY: 999999999
+		})
+	}
+	function init(echarts: any, ...args: any[]): Promise<Echarts>{
+		if(echarts == null){
+			console.error('请确保已经引入了 ECharts 库');
+			return Promise.reject('请确保已经引入了 ECharts 库');
+		}
+		let theme:string|null=null
+		let opts={}
+		let callback:Function|null=null;
+		
+		args.forEach(item =>{
+			if(typeof item === 'function') {
+				callback = item
+			} else if(['string'].includes(typeof item)){
+				theme = item
+			} else if(typeof item === 'object'){
+				opts = item
+			}
+		})
+		chart = echarts.init(chartRef.value, theme, opts)
+		window.addEventListener('touchstart', touchstart)
+		window.addEventListener('touchmove', touchmove)
+		window.addEventListener('touchend', mouseup)
+		
+		if(callback!=null && typeof callback == 'function'){
+			callbackMap.push(callback)
+		}
+		return new Promise<Echarts>((resolve) => {
+			map.push(resolve)
+			trigger()
+		})
+	}
+	onMounted(()=>{
+		finished.value = true
+		trigger()
+		emits('finished')
+	})
+	onUnmounted(()=>{
+		window.removeEventListener('touchstart', touchstart)
+		window.removeEventListener('touchmove', touchmove)
+		window.removeEventListener('touchend', mouseup)
+	})
+	// #endif
+	
+	defineExpose({
+		init,
+		setOption,
+		showLoading,
+		hideLoading,
+		clear,
+		dispose,
+		resize,
+		canvasToTempFilePath
+	})
+</script>
+<style lang="scss">
+	.lime-echart {
+		flex: 1;
+	}
+</style>

+ 514 - 0
uni_modules/lime-echart/components/l-echart/l-echart.vue

@@ -0,0 +1,514 @@
+<template>
+	<view class="lime-echart" style="width: 100%; height: 100%" v-if="canvasId" ref="limeEchart" :aria-label="ariaLabel">
+		<!-- #ifndef APP-NVUE -->
+		<canvas
+			class="lime-echart__canvas"
+			v-if="use2dCanvas"
+			type="2d"
+			:id="canvasId"
+			style="width: 100%; height: 100%"
+			:disable-scroll="isDisableScroll"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd"
+		/>
+		<canvas
+			class="lime-echart__canvas"
+			v-else
+			:width="nodeWidth"
+			:height="nodeHeight"
+			:style="canvasStyle"
+			:canvas-id="canvasId"
+			:id="canvasId"
+			:disable-scroll="isDisableScroll"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd"
+		/>
+		<view class="lime-echart__mask"
+			v-if="isPC"
+			@mousedown="touchStart"
+			@mousemove="touchMove"
+			@mouseup="touchEnd"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd">
+		</view>
+		<canvas v-if="isOffscreenCanvas" :style="offscreenStyle" :canvas-id="offscreenCanvasId"></canvas>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<web-view
+			class="lime-echart__canvas"
+			:id="canvasId"
+			:style="canvasStyle"
+			:webview-styles="webviewStyles"
+			ref="webview"
+			src="/uni_modules/lime-echart/static/uvue.html?v=1"
+			@pagefinish="finished = true"
+			@onPostMessage="onMessage"
+		></web-view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+// #ifndef APP-NVUE
+import {Canvas, setCanvasCreator, dispatch} from './canvas';
+import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect} from './utils';
+// #endif
+// #ifdef APP-NVUE
+import { base64ToPath, sleep } from './utils';
+import {Echarts} from './nvue'
+// #endif
+const charts = {}
+const echartsObj = {}
+
+
+/**
+ * LimeChart 图表
+ * @description 全端兼容的eCharts
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=4899
+
+ * @property {String} customStyle 自定义样式
+ * @property {String} type 指定 canvas 类型
+ * @value 2d 使用canvas 2d,部分小程序支持
+ * @value '' 使用原生canvas,会有层级问题
+ * @value bottom right	不缩放图片,只显示图片的右下边区域
+ * @property {Boolean} isDisableScroll	 
+ * @property {number} beforeDelay = [30]  延迟初始化 (毫秒)
+ * @property {Boolean} enableHover PC端使用鼠标悬浮
+
+ * @event {Function} finished 加载完成触发
+ */
+export default {
+	name: 'lime-echart',
+	props: {
+		// #ifdef MP-WEIXIN || MP-TOUTIAO
+		type: {
+			type: String,
+			default: '2d'
+		},
+		// #endif
+		// #ifdef APP-NVUE
+		webviewStyles: Object,
+		// hybrid: Boolean,
+		// #endif
+		customStyle: String,
+		isDisableScroll: Boolean,
+		isClickable: {
+			type: Boolean,
+			default: true
+		},
+		enableHover: Boolean,
+		beforeDelay: {
+			type: Number,
+			default: 30
+		}
+	},
+	data() {
+		return {
+			// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+			use2dCanvas: true,
+			// #endif
+			// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+			use2dCanvas: false,
+			// #endif
+			ariaLabel: '图表',
+			width: null,
+			height: null,
+			nodeWidth: null,
+			nodeHeight: null,
+			// canvasNode: null,
+			config: {},
+			inited: false,
+			finished: false,
+			file: '',
+			platform: '',
+			isPC: false,
+			isDown: false,
+			isOffscreenCanvas: false,
+			offscreenWidth: 0,
+			offscreenHeight: 0
+		};
+	},
+	computed: {
+		canvasId() {
+			return `lime-echart${this._ && this._.uid || this._uid}`
+		},
+		offscreenCanvasId() {
+			return `${this.canvasId}_offscreen`
+		},
+		offscreenStyle() {
+			return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
+		},
+		canvasStyle() {
+			return  this.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : ''
+		}
+	},
+	// #ifndef VUE3
+	beforeDestroy() {
+		this.clear()
+		this.dispose()
+		// #ifdef H5
+		if(this.isPC) {
+			document.removeEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+	},
+	// #endif
+	// #ifdef VUE3
+	beforeUnmount() {
+		this.clear()
+		this.dispose()
+		// #ifdef H5
+		if(this.isPC) {
+			document.removeEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+	},
+	// #endif
+	created() {
+		// #ifdef H5
+		if(!('ontouchstart' in window)) {
+			this.isPC = true
+			document.addEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+		// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+		const { platform } = uni.getSystemInfoSync();
+		this.isPC = /windows/i.test(platform)
+		// #endif
+		this.use2dCanvas = this.type === '2d' && canIUseCanvas2d()
+	},
+	mounted() {
+		this.$nextTick(() => {
+			this.$emit('finished')
+		})
+	},
+	methods: {
+		// #ifdef APP-NVUE
+		onMessage(e) {
+			const detail = e?.detail?.data[0] || null;
+			const data = detail?.data
+			const key = detail?.event
+			const options = data?.options
+			const event = data?.event
+			const file = detail?.file
+			if (key == 'log' && data) {
+				console.log(data)
+			}
+			if(event) {
+				this.chart.dispatchAction(event.replace(/"/g,''), options)
+			}
+			if(file) {
+				thie.file = file
+			}
+		},
+		// #endif
+		setChart(callback) {
+			if(!this.chart) {
+				console.warn(`组件还未初始化,请先使用 init`)
+				return
+			}
+			if(typeof callback === 'function' && this.chart) {
+				callback(this.chart);
+			}
+			// #ifdef APP-NVUE
+			if(typeof callback === 'function') {
+				this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.chart.options)})`);
+			}
+			// #endif
+		},
+		setOption() {
+			if (!this.chart || !this.chart.setOption) {
+				console.warn(`组件还未初始化,请先使用 init`)
+				return
+			}
+			this.chart.setOption(...arguments);
+		},
+		showLoading() {
+			if(this.chart) {
+				this.chart.showLoading(...arguments)
+			}
+		},
+		hideLoading() {
+			if(this.chart) {
+				this.chart.hideLoading()
+			}
+		},
+		clear() {
+			if(this.chart) {
+				this.chart.clear()
+			}
+		},
+		dispose() {
+			if(this.chart) {
+				this.chart.dispose()
+			}
+		},
+		resize(size) {
+			if(size && size.width && size.height) {
+				this.height = size.height
+				this.width = size.width
+				if(this.chart) {this.chart.resize(size)}
+			} else {
+				this.$nextTick(() => {
+					uni.createSelectorQuery()
+						.in(this)
+						.select(`.lime-echart`)
+						.boundingClientRect()
+						.exec(res => {
+							if (res) {
+								let { width, height } = res[0];
+								this.width = width = width || 300;
+								this.height = height = height || 300;
+								this.chart.resize({width, height})
+							}
+						});
+				})
+				
+			}
+			
+		},
+		canvasToTempFilePath(args = {}) {
+			// #ifndef APP-NVUE
+			const { use2dCanvas, canvasId } = this;
+			return new Promise((resolve, reject) => {
+				const copyArgs = Object.assign({
+					canvasId,
+					success: resolve,
+					fail: reject
+				}, args);
+				if (use2dCanvas) {
+					delete copyArgs.canvasId;
+					copyArgs.canvas = this.canvasNode;
+				}
+				uni.canvasToTempFilePath(copyArgs, this);
+			});
+			// #endif
+			// #ifdef APP-NVUE
+			this.file = ''
+			this.$refs.webview.evalJs(`canvasToTempFilePath()`);
+			return new Promise((resolve, reject) => {
+				this.$watch('file', async (file) => {
+					if(file) {
+						const tempFilePath = await base64ToPath(file)
+						resolve(args.success({tempFilePath}))
+					} else {
+						reject(args.fail({error: ``}))
+					}
+				})
+			})
+			// #endif
+		},
+		async init(echarts, ...args) {
+			// #ifndef APP-NVUE
+			if(args && args.length == 0 && !echarts) {
+				console.error('缺少参数:init(echarts, theme?:string, opts?: object, callback?: function)')
+				return
+			}
+			// #endif
+			let theme=null,opts={},callback;
+			
+			Array.from(arguments).forEach(item => {
+				if(typeof item === 'function') {
+					callback = item
+				}
+				if(['string'].includes(typeof item)) {
+					theme = item
+				}
+				if(typeof item === 'object') {
+					opts = item
+				}
+			})
+			
+			if(this.beforeDelay) {
+				await sleep(this.beforeDelay)
+			}
+			let config = await this.getContext();
+			// #ifndef APP-NVUE
+			setCanvasCreator(echarts, config)
+			try {
+				this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
+				if(typeof callback === 'function') {
+					callback(this.chart)
+				} else {
+					return this.chart
+				}
+			} catch(e) {
+				console.error(e.messges)
+				return null
+			}
+			// #endif
+			// #ifdef APP-NVUE
+			this.chart = new Echarts(this.$refs.webview)
+			this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
+			if(callback) {
+				callback(this.chart)
+			} else {
+				return this.chart
+			}
+			// #endif
+		},
+		getContext() {
+			// #ifdef APP-NVUE
+			if(this.finished) {
+				return Promise.resolve(this.finished)
+			}
+			return new Promise(resolve => {
+				this.$watch('finished', (val) => {
+					if(val) {
+						resolve(this.finished)
+					}
+				})
+			})
+			// #endif
+			// #ifndef APP-NVUE
+			return getRect(`#${this.canvasId}`, {context: this, type: this.use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
+				if(res) {
+					let dpr = devicePixelRatio
+					let {width, height, node} = res
+					let canvas;
+					this.width = width = width || 300;
+					this.height = height = height || 300;
+					if(node) {
+						const ctx = node.getContext('2d');
+						canvas = new Canvas(ctx, this, true, node);
+						this.canvasNode = node
+					} else {
+						// #ifdef MP-TOUTIAO
+						dpr = !this.isPC ? devicePixelRatio : 1// 1.25
+						// #endif
+						// #ifndef MP-ALIPAY || MP-TOUTIAO
+						dpr = this.isPC ? devicePixelRatio : 1
+						// #endif
+						// #ifdef MP-ALIPAY || MP-LARK
+						dpr = devicePixelRatio
+						// #endif
+						// #ifdef WEB
+						dpr = 1
+						// #endif
+						this.rect = res
+						this.nodeWidth = width * dpr;
+						this.nodeHeight = height * dpr;
+						const ctx = uni.createCanvasContext(this.canvasId, this);
+						canvas =  new Canvas(ctx, this, false);
+					}
+					return { canvas, width, height, devicePixelRatio: dpr, node };
+				} else {
+					return {}
+				}
+			})
+			// #endif
+		},
+		// #ifndef APP-NVUE
+		getRelative(e, touches) {
+			let { clientX, clientY } = e
+			if(!(clientX && clientY) && touches && touches[0]) {
+				clientX = touches[0].clientX
+				clientY = touches[0].clientY
+			}
+			return {x: clientX - this.rect.left, y: clientY - this.rect.top, wheelDelta: e.wheelDelta || 0}
+		},
+		getTouch(e, touches) {
+			const {x} = touches && touches[0] || {}
+			return x ? touches[0] : this.getRelative(e, touches);
+		},
+		touchStart(e) {
+			this.isDown = true
+			const next = () => {
+				const touches = convertTouchesToArray(e.touches)
+				if(this.chart) {
+					const touch = this.getTouch(e, touches)
+					this.startX = touch.x
+					this.startY = touch.y
+					this.startT = new Date()
+					const handler = this.chart.getZr().handler;
+					dispatch.call(handler, 'mousedown', touch)
+					dispatch.call(handler, 'mousemove', touch)
+					handler.processGesture(wrapTouch(e), 'start');
+					clearTimeout(this.endTimer);
+				}
+				
+			}
+			if(this.isPC) {
+				getRect(`#${this.canvasId}`, {context: this}).then(res => {
+					this.rect = res
+					next()
+				})
+				return
+			}
+			next()
+		},
+		touchMove(e) {
+			if(this.isPC && this.enableHover && !this.isDown) {this.isDown = true}
+			const touches = convertTouchesToArray(e.touches)
+			if (this.chart && this.isDown) {
+				const handler = this.chart.getZr().handler;
+				dispatch.call(handler, 'mousemove', this.getTouch(e, touches))
+				handler.processGesture(wrapTouch(e), 'change');
+			}
+			
+		},
+		touchEnd(e) {
+			this.isDown = false
+			if (this.chart) {
+				const touches = convertTouchesToArray(e.changedTouches)
+				const {x} = touches && touches[0] || {}
+				const touch = (x ? touches[0] : this.getRelative(e, touches)) || {};
+				const handler = this.chart.getZr().handler;
+				const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200;
+				dispatch.call(handler, 'mouseup', touch)
+				handler.processGesture(wrapTouch(e), 'end');
+				if(isClick) {
+					dispatch.call(handler, 'click', touch)
+				} else {
+					this.endTimer = setTimeout(() => {
+						dispatch.call(handler, 'mousemove', {x: 999999999,y: 999999999});
+						dispatch.call(handler, 'mouseup', {x: 999999999,y: 999999999});
+					},50)
+				}
+			}
+		},
+		// #endif
+		// #ifdef H5
+		mousewheel(e){
+			if(this.chart) {
+				dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
+			}
+		}
+		// #endif
+	}
+};
+</script>
+<style>	
+.lime-echart {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+}
+.lime-echart__canvas {
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+}
+/* #ifndef APP-NVUE */
+.lime-echart__mask {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	left: 0;
+	top: 0;
+	z-index: 1;
+}
+/* #endif */
+</style>

+ 51 - 0
uni_modules/lime-echart/components/l-echart/nvue.js

@@ -0,0 +1,51 @@
+export class Echarts {
+	eventMap = new Map()
+	constructor(webview) {
+		this.webview = webview
+		this.options = null
+	}
+	setOption() {
+		this.options = arguments
+		this.webview.evalJs(`setOption(${JSON.stringify(arguments)})`);
+	}
+	getOption() {
+		return this.options
+	}
+	showLoading() {
+		this.webview.evalJs(`showLoading(${JSON.stringify(arguments)})`);
+	}
+	hideLoading() {
+		this.webview.evalJs(`hideLoading()`);
+	}
+	clear() {
+		this.webview.evalJs(`clear()`);
+	}
+	dispose() {
+		this.webview.evalJs(`dispose()`);
+	}
+	resize(size) {
+		if(size) {
+			this.webview.evalJs(`resize(${JSON.stringify(size)})`);
+		} else {
+			this.webview.evalJs(`resize()`);
+		}
+	}
+	on(type, ...args) {
+		const query = args[0]
+		const useQuery = query && typeof query != 'function'
+		const param = useQuery ? [type, query] : [type]
+		const key = `${type}${useQuery ? JSON.stringify(query): '' }`
+		const callback = useQuery ? args[1]: args[0]
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.webview.evalJs(`on(${JSON.stringify(param)})`);
+		console.warn('nvue 暂不支持事件')
+	}
+	dispatchAction(type, options){
+		const handler = this.eventMap.get(type)
+		if(handler){
+			handler(options)
+		}
+	}
+}

+ 145 - 0
uni_modules/lime-echart/components/l-echart/utils.js

@@ -0,0 +1,145 @@
+// #ifndef APP-NVUE
+// 计算版本
+export function compareVersion(v1, v2) {
+	v1 = v1.split('.')
+	v2 = v2.split('.')
+	const len = Math.max(v1.length, v2.length)
+	while (v1.length < len) {
+		v1.push('0')
+	}
+	while (v2.length < len) {
+		v2.push('0')
+	}
+	for (let i = 0; i < len; i++) {
+		const num1 = parseInt(v1[i], 10)
+		const num2 = parseInt(v2[i], 10)
+
+		if (num1 > num2) {
+			return 1
+		} else if (num1 < num2) {
+			return -1
+		}
+	}
+	return 0
+}
+const systemInfo = uni.getSystemInfoSync();
+
+function gte(version) {
+	// 截止 2023-03-22 mac pc小程序不支持 canvas 2d
+	let {
+		SDKVersion,
+		platform
+	} = systemInfo;
+	// #ifdef MP-ALIPAY
+	SDKVersion = my.SDKVersion
+	// #endif
+	// #ifdef MP-WEIXIN
+	return platform !== 'mac' && compareVersion(SDKVersion, version) >= 0;
+	// #endif
+	return compareVersion(SDKVersion, version) >= 0;
+}
+
+
+export function canIUseCanvas2d() {
+	// #ifdef MP-WEIXIN
+	return gte('2.9.0');
+	// #endif
+	// #ifdef MP-ALIPAY
+	return gte('2.7.0');
+	// #endif
+	// #ifdef MP-TOUTIAO
+	return gte('1.78.0');
+	// #endif
+	return false
+}
+
+export function convertTouchesToArray(touches) {
+	// 如果 touches 是一个数组,则直接返回它
+	if (Array.isArray(touches)) {
+		return touches;
+	}
+	// 如果touches是一个对象,则转换为数组
+	if (typeof touches === 'object' && touches !== null) {
+		return Object.values(touches);
+	}
+	// 对于其他类型,直接返回它
+	return touches;
+}
+
+export function wrapTouch(event) {
+	for (let i = 0; i < event.touches.length; ++i) {
+		const touch = event.touches[i];
+		touch.offsetX = touch.x;
+		touch.offsetY = touch.y;
+	}
+	return event;
+}
+export const devicePixelRatio = uni.getSystemInfoSync().pixelRatio
+// #endif
+// #ifdef APP-NVUE
+export function base64ToPath(base64) {
+	return new Promise((resolve, reject) => {
+		const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
+		const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
+		bitmap.loadBase64Data(base64, () => {
+			if (!format) {
+				reject(new Error('ERROR_BASE64SRC_PARSE'))
+			}
+			const time = new Date().getTime();
+			const filePath = `_doc/uniapp_temp/${time}.${format}`
+
+			bitmap.save(filePath, {},
+				() => {
+					bitmap.clear()
+					resolve(filePath)
+				},
+				(error) => {
+					bitmap.clear()
+					console.error(`${JSON.stringify(error)}`)
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			console.error(`${JSON.stringify(error)}`)
+			reject(error)
+		})
+	})
+}
+// #endif
+
+
+export function sleep(time) {
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve(true)
+		}, time)
+	})
+}
+
+
+export function getRect(selector, options = {}) {
+	const typeDefault = 'boundingClientRect'
+	const {
+		context,
+		type = typeDefault
+	} = options
+	return new Promise((resolve, reject) => {
+		const dom = uni.createSelectorQuery().in(context).select(selector);
+		const result = (rect) => {
+			if (rect) {
+				resolve(rect)
+			} else {
+				reject()
+			}
+		}
+		if (type == typeDefault) {
+			dom[type](result).exec()
+		} else {
+			dom[type]({
+				node: true,
+				size: true,
+				rect: true
+			}, result).exec()
+		}
+	});
+};

+ 133 - 0
uni_modules/lime-echart/components/l-echart/uvue.uts

@@ -0,0 +1,133 @@
+// @ts-nocheck
+// #ifdef APP
+type EchartsEventHandler = (event: UTSJSONObject)=>void
+// type EchartsTempResolve = (obj : UTSJSONObject) => void
+// type EchartsTempOptions = UTSJSONObject
+export class Echarts {
+	options: UTSJSONObject = {} as UTSJSONObject
+	context: UniWebViewElement
+	eventMap: Map<string, EchartsEventHandler> = new Map()
+	private temp: UTSJSONObject[] = []
+	constructor(context: UniWebViewElement){
+		this.context = context
+		this.init()
+	}
+	init(){
+		this.context.evalJS(`init(null, null, ${JSON.stringify({})})`)
+		
+		this.context.addEventListener('message', (e : UniWebViewMessageEvent) => {
+			// event.stopPropagation()
+			// event.preventDefault()
+			
+			const detail = e.detail.data[0]
+			const file = detail.getString('file')
+			const data = detail.get('data')
+			const key = detail.getString('event')
+			const options = typeof data == 'object' ? (data as UTSJSONObject).getJSON('options'): null
+			const event = typeof data == 'object' ? (data as UTSJSONObject).getString('event'): null
+			if (key == 'log' && data != null) {
+				console.log(data)
+			}
+			if (event != null && options != null) {
+				this.dispatchAction(event.replace(/"/g,''), options)
+			}
+			if(file != null){
+				while (this.temp.length > 0) {
+					const opt = this.temp.pop()
+					const success = opt?.get('success')
+					if(typeof success == 'function'){
+						success as (res: UTSJSONObject) => void
+						success({tempFilePath: file})
+					}
+				}
+			}
+			
+		})
+	}
+	setOption(option: UTSJSONObject){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option])})`)
+	}
+	setOption(option: UTSJSONObject, notMerge: boolean = false, lazyUpdate: boolean = false){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option, notMerge, lazyUpdate])})`)
+	}
+	setOption(option: UTSJSONObject, notMerge: UTSJSONObject){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option, notMerge])})`)
+	}
+	getOption(): UTSJSONObject {
+		return this.options
+	}
+	showLoading(){
+		this.context.evalJS(`showLoading(${JSON.stringify([] as any[])})`);
+	}
+	showLoading(type: string, opts: UTSJSONObject){
+		this.context.evalJS(`showLoading(${JSON.stringify([type, opts])})`);
+	}
+	hideLoading(){
+		this.context.evalJS(`hideLoading()`);
+	}
+	clear(){
+		this.context.evalJS(`clear()`);
+	}
+	dispose(){
+		this.context.evalJS(`dispose()`);
+	}
+	resize(size:UTSJSONObject){
+		setTimeout(()=>{
+			this.context.evalJS(`resize(${JSON.stringify(size)})`);
+		},0)
+	}
+	resize(){
+		setTimeout(()=>{
+			this.context.evalJS(`resize()`);
+		},10)
+		
+	}
+	on(type:string, query: any, callback: EchartsEventHandler) {
+		const key = `${type}${JSON.stringify(query)}`
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.context.evalJS(`on(${JSON.stringify([type, query])})`);
+		console.warn('uvue 暂不支持事件')
+	}
+	on(type:string, callback: EchartsEventHandler) {
+		const key = `${type}`
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.context.evalJS(`on(${JSON.stringify([type])})`);
+		console.warn('uvue 暂不支持事件')
+	}
+	dispatchAction(type:string, options: UTSJSONObject){
+		const handler = this.eventMap.get(type)
+		if(handler!=null){
+			handler(options)
+		}
+	}
+	canvasToTempFilePath(opt: UTSJSONObject){
+		// this.context.evalJS(`on(${JSON.stringify(opt)})`);
+		this.context.evalJS(`canvasToTempFilePath(${JSON.stringify(opt)})`);
+		this.temp.push(opt)
+	}
+}
+
+// #endif
+// #ifndef APP
+export class Echarts {
+	constructor() {}
+	setOption(option: UTSJSONObject): void
+	isDisposed(): boolean;
+	clear(): void;
+	resize(size:UTSJSONObject): void;
+	resize(): void;
+	canvasToTempFilePath(opt : UTSJSONObject): void;
+	dispose(): void;
+	showLoading(cfg?: UTSJSONObject): void;
+	showLoading(name?: string, cfg?: UTSJSONObject): void;
+	hideLoading(): void;
+	getZr(): any
+}
+// #endif

+ 159 - 0
uni_modules/lime-echart/components/lime-echart/lime-echart.nvue

@@ -0,0 +1,159 @@
+<template>
+	<view style="width: 100%; height: 408px;">
+		<l-echart ref="chartRef" @finished="init"></l-echart>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				showTip: false,
+				option: {
+					tooltip: {
+						trigger: 'axis',
+						// shadowBlur: 0,
+						textStyle: {
+							textShadowBlur: 0
+						},
+						renderMode: 'richText',
+					},
+					legend: {
+						data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+					},
+					grid: {
+						left: '3%',
+						right: '4%',
+						bottom: '3%',
+						containLabel: true
+					},
+					xAxis: {
+						type: 'category',
+						boundaryGap: false,
+						data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+					},
+					yAxis: {
+						type: 'value'
+					},
+					series: [
+						{
+							name: '邮件营销',
+							type: 'line',
+							stack: '总量',
+							data: [120, 132, 101, 134, 90, 230, 210]
+						},
+						{
+							name: '联盟广告',
+							type: 'line',
+							stack: '总量',
+							data: [220, 182, 191, 234, 290, 330, 310]
+						},
+						{
+							name: '视频广告',
+							type: 'line',
+							stack: '总量',
+							data: [150, 232, 201, 154, 190, 330, 410]
+						},
+						{
+							name: '直接访问',
+							type: 'line',
+							stack: '总量',
+							data: [320, 332, 301, 334, 390, 330, 320]
+						},
+						{
+							name: '搜索引擎',
+							type: 'line',
+							stack: '总量',
+							data: [820, 932, 901, 934, 1290, 1330, 1320]
+						}
+					]
+				}
+			}
+		},
+		mounted() {
+			console.log('lime echarts nvue')
+		},
+		methods: {
+			init() {
+				const chartRef = this.$refs['chartRef']
+				chartRef.init(chart => {
+					chart.setOption(this.option);
+					
+					
+					setTimeout(()=>{
+						const option = {
+							tooltip: {
+								trigger: 'axis',
+								// shadowBlur: 0,
+								textStyle: {
+									textShadowBlur: 0
+								},
+								renderMode: 'richText',
+							},
+							legend: {
+								data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+							},
+							grid: {
+								left: '3%',
+								right: '4%',
+								bottom: '3%',
+								containLabel: true
+							},
+							xAxis: {
+								type: 'category',
+								boundaryGap: false,
+								data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+							},
+							yAxis: {
+								type: 'value'
+							},
+							series: [
+								{
+									name: '邮件营销',
+									type: 'line',
+									stack: '总量',
+									data: [120, 132, 101, 134, 90, 230, 210]
+								},
+								{
+									name: '联盟广告',
+									type: 'line',
+									stack: '总量',
+									data: [220, 182, 191, 234, 290, 330, 310]
+								},
+								{
+									name: '视频广告',
+									type: 'line',
+									stack: '总量',
+									data: [150, 232, 201, 154, 190, 330, 410]
+								},
+								{
+									name: '直接访问',
+									type: 'line',
+									stack: '总量',
+									data: [320, 332, 301, 334, 390, 330, 320]
+								},
+								{
+									name: '搜索引擎',
+									type: 'line',
+									stack: '总量',
+									data: [820, 932, 901, 934, 1290, 1330, 1320]
+								}
+							]
+						}
+						chart.setOption(option);
+					},1000)
+				})
+			},
+			save() {
+				// this.$refs.chart.canvasToTempFilePath({
+				// 	success(res) {
+				// 		console.log('res::::', res)
+				// 	}
+				// })
+			}
+		}
+	}
+</script>
+<style>
+
+</style>

+ 160 - 0
uni_modules/lime-echart/components/lime-echart/lime-echart.uvue

@@ -0,0 +1,160 @@
+<template>
+	<view style="width: 100%; height: 408px;background-color: aqua;">
+		<l-echart ref="chartRef" @finished="init"></l-echart>
+	</view>
+</template>
+
+<script lang="uts" setup>
+	// @ts-nocheck
+	// #ifdef H5
+	import * as echarts from 'echarts/dist/echarts.esm.js'
+	// #endif
+	const chartRef = ref<LEchartComponentPublicInstance|null>(null)
+	const option = {
+		tooltip: {
+			trigger: 'axis',
+			// shadowBlur: 0,
+			textStyle: {
+				textShadowBlur: 0
+			},
+			renderMode: 'richText',
+		},
+		// formatter: async (params: any) => {
+		// 	console.log('params', params)
+		// 	return 1
+		// },
+		legend: {
+			data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+		},
+		grid: {
+			left: '3%',
+			right: '4%',
+			bottom: '3%',
+			containLabel: true
+		},
+		xAxis: {
+			type: 'category',
+			boundaryGap: false,
+			data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+		},
+		yAxis: {
+			type: 'value'
+		},
+		series: [
+			{
+				name: '邮件营销',
+				type: 'line',
+				stack: '总量',
+				data: [120, 132, 101, 134, 90, 230, 210]
+			},
+			{
+				name: '联盟广告',
+				type: 'line',
+				stack: '总量',
+				data: [220, 182, 191, 234, 290, 330, 310]
+			},
+			{
+				name: '视频广告',
+				type: 'line',
+				stack: '总量',
+				data: [150, 232, 201, 154, 190, 330, 410]
+			},
+			{
+				name: '直接访问',
+				type: 'line',
+				stack: '总量',
+				data: [320, 332, 301, 334, 390, 330, 320]
+			},
+			{
+				name: '搜索引擎',
+				type: 'line',
+				stack: '总量',
+				data: [820, 932, 901, 934, 1290, 1330, 1320]
+			}
+		]
+	}
+
+	const init = async () =>{
+		if(chartRef.value== null) return
+		// #ifdef APP
+		const chart = await chartRef.value!.init(null)
+		// #endif
+		// #ifdef H5
+		const chart = await chartRef.value!.init(echarts, null)
+		// #endif
+		chart.setOption(option)
+		chart.on('mouseover', function (params) {
+		    console.log('params', params);
+		});
+		
+		
+		// setTimeout(()=> {
+		// 	const option1 = {
+		// 		tooltip: {
+		// 			trigger: 'axis',
+		// 			// shadowBlur: 0,
+		// 			textStyle: {
+		// 				textShadowBlur: 0
+		// 			},
+		// 			renderMode: 'richText',
+		// 		},
+		// 		legend: {
+		// 			data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+		// 		},
+		// 		grid: {
+		// 			left: '3%',
+		// 			right: '4%',
+		// 			bottom: '3%',
+		// 			containLabel: true
+		// 		},
+		// 		xAxis: {
+		// 			type: 'category',
+		// 			boundaryGap: false,
+		// 			data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+		// 		},
+		// 		yAxis: {
+		// 			type: 'value'
+		// 		},
+		// 		series: [
+		// 			{
+		// 				name: '邮件营销',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [820, 132, 101, 134, 90, 230, 210]
+		// 			},
+		// 			{
+		// 				name: '联盟广告',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [220, 182, 191, 234, 290, 330, 310]
+		// 			},
+		// 			{
+		// 				name: '视频广告',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [950, 232, 201, 154, 190, 330, 410]
+		// 			},
+		// 			{
+		// 				name: '直接访问',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [320, 332, 301, 334, 390, 330, 320]
+		// 			},
+		// 			{
+		// 				name: '搜索引擎',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [820, 932, 901, 934, 1290, 1330, 1320]
+		// 			}
+		// 		]
+		// 	}
+		// 	chart.setOption(option1)
+		// },1000)
+	}
+	
+	
+	
+</script>
+<style>
+
+</style>

+ 226 - 0
uni_modules/lime-echart/components/lime-echart/lime-echart.vue

@@ -0,0 +1,226 @@
+<template>
+	<view >
+		<view style="height: 750rpx; position: relative">
+			<l-echart ref="chart" @finished="init"></l-echart>
+			<view class="customTooltips" :style="{left: position[0] + 'px',top: position[1] + 'px'}" v-if="params.length && position.length && showTip">
+				<view>这是个自定的tooltips</view>
+				<view>{{params[0]['axisValue']}}</view>
+				<view v-for="item in params">
+					<view>
+						<text>{{item.seriesName}}</text>
+						<text>{{item.value}}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>		
+</template>
+
+<script>
+	// nvue 不需要引入
+	// #ifdef VUE2
+	import * as echarts from '@/uni_modules/lime-echart/static/echarts.min';
+	// #endif
+	// #ifdef VUE3
+	// #ifdef MP
+	// 由于vue3 使用vite 不支持umd格式的包,小程序依然可以使用,但需要使用require
+	const echarts = require('../../static/echarts.min');
+	// #endif
+	// #ifndef MP
+	// 由于 vue3 使用vite 不支持umd格式的包,故引入npm的包
+	import * as echarts from 'echarts/dist/echarts.esm';
+	// #endif
+	// #endif
+	export default {
+		data() {
+			return {
+				showTip: false,
+				position: [],
+				params: [],
+				option:  {
+					tooltip: {
+						trigger: 'axis',
+						// shadowBlur: 0,
+						textStyle: {
+							textShadowBlur : 0
+						},
+						renderMode: 'richText',
+						position: (point, params, dom, rect, size) => {
+							// 假设自定义的tooltips尺寸
+							const box = [170, 170]
+							// 偏移
+							const offsetX = point[0] < size.viewSize[0] / 2 ? 20 : -box[0] - 20;
+							const offsetY = point[1] < size.viewSize[1] / 2 ? 20 : -box[1] - 20;
+							const x = point[0] + offsetX;
+							const y = point[1] + offsetY;
+							
+							this.position = [x, y]
+							this.params = params
+						},
+						formatter: (params, ticket, callback) => {
+							
+						}
+					},
+					legend: {
+						data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+					},
+					grid: {
+						left: '3%',
+						right: '4%',
+						bottom: '3%',
+						containLabel: true
+					},
+					xAxis: {
+						type: 'category',
+						boundaryGap: false,
+						data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+					},
+					yAxis: {
+						type: 'value'
+					},
+					series: [
+						{
+							name: '邮件营销',
+							type: 'line',
+							stack: '总量',
+							data: [120, 132, 101, 134, 90, 230, 210]
+						},
+						{
+							name: '联盟广告',
+							type: 'line',
+							stack: '总量',
+							data: [220, 182, 191, 234, 290, 330, 310]
+						},
+						{
+							name: '视频广告',
+							type: 'line',
+							stack: '总量',
+							data: [150, 232, 201, 154, 190, 330, 410]
+						},
+						{
+							name: '直接访问',
+							type: 'line',
+							stack: '总量',
+							data: [320, 332, 301, 334, 390, 330, 320]
+						},
+						{
+							name: '搜索引擎',
+							type: 'line',
+							stack: '总量',
+							data: [820, 932, 901, 934, 1290, 1330, 1320]
+						}
+					]
+				}
+			}
+		},
+		
+		methods: {
+			init() {
+				// init(echarts, theme?:string, opts?:{}, chart => {})
+				// echarts 必填, 非nvue必填,nvue不用填
+				// theme 可选,应用的主题,目前只支持名称,如:'dark'
+				// opts = { // 可选
+				//	locale?: string  // 从 `5.0.0` 开始支持
+				// }
+				// chart => {} , callback 返回图表实例
+				// setTimeout(()=>{
+				// 	this.$refs.chart.init(echarts, chart => {
+				// 		chart.setOption(this.option);
+				// 	});
+				// },300)
+				this.$refs.chart.init(echarts, chart => {
+					chart.setOption(this.option);
+					
+					// 监听tooltip显示事件
+					chart.on('showTip', (params) => {
+					  this.showTip = true
+					  console.log('showTip::')
+					});
+					chart.on('hideTip', (params) => {
+						setTimeout(() => {
+							 this.showTip = false
+						},300)
+					});
+					
+					setTimeout(()=>{
+						const option = {
+							tooltip: {
+								trigger: 'axis',
+								// shadowBlur: 0,
+								textStyle: {
+									textShadowBlur: 0
+								},
+								renderMode: 'richText',
+							},
+							legend: {
+								data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+							},
+							grid: {
+								left: '3%',
+								right: '4%',
+								bottom: '3%',
+								containLabel: true
+							},
+							xAxis: {
+								type: 'category',
+								boundaryGap: false,
+								data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+							},
+							yAxis: {
+								type: 'value'
+							},
+							series: [
+								{
+									name: '邮件营销',
+									type: 'line',
+									stack: '总量',
+									data: [1120, 132, 101, 134, 90, 230, 210]
+								},
+								{
+									name: '联盟广告',
+									type: 'line',
+									stack: '总量',
+									data: [220, 182, 191, 234, 290, 330, 310]
+								},
+								{
+									name: '视频广告',
+									type: 'line',
+									stack: '总量',
+									data: [150, 632, 201, 154, 190, 330, 410]
+								},
+								{
+									name: '直接访问',
+									type: 'line',
+									stack: '总量',
+									data: [820, 332, 301, 334, 390, 330, 320]
+								},
+								{
+									name: '搜索引擎',
+									type: 'line',
+									stack: '总量',
+									data: [820, 932, 901, 934, 1290, 1330, 1320]
+								}
+							]
+						}
+						chart.setOption(option);
+					},1000)
+					
+				});
+			},
+			save() {
+				this.$refs.chart.canvasToTempFilePath({
+					success(res) {
+						console.log('res::::', res)
+					}
+				})
+			}
+		}
+	}
+</script>
+<style>
+	.customTooltips {
+		position: absolute;
+		background-color: rgba(255, 255, 255, 0.8);
+		padding: 20rpx;
+	}
+</style>

+ 90 - 0
uni_modules/lime-echart/package.json

@@ -0,0 +1,90 @@
+{
+  "id": "lime-echart",
+  "displayName": "echarts",
+  "version": "0.9.6",
+  "description": "echarts 全端兼容,一款使echarts图表能跑在uniapp各端中的插件, 支持uniapp/uniappx(web,ios,安卓)",
+  "keywords": [
+    "echarts",
+    "canvas",
+    "图表",
+    "可视化"
+],
+  "repository": "https://gitee.com/liangei/lime-echart",
+  "engines": {
+    "HBuilderX": "^3.6.4"
+  },
+  "dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y",
+          "app-uvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "u",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "u"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        }
+      }
+    }
+  },
+  "dependencies": {
+    "echarts": "^5.4.1",
+    "zrender": "^5.4.3"
+  }
+}

+ 406 - 0
uni_modules/lime-echart/readme.md

@@ -0,0 +1,406 @@
+# echarts 图表 <span style="font-size:16px;">👑👑👑👑👑 <span style="background:#ff9d00;padding:2px 4px;color:#fff;font-size:10px;border-radius: 3px;">全端</span></span>
+> 一个基于 JavaScript 的开源可视化图表库   [查看更多](https://limeui.qcoon.cn/#/echart) <br>
+> 基于 echarts 做了兼容处理,更多示例请访问  [uni示例](https://limeui.qcoon.cn/#/echart-example) | [官方示例](https://echarts.apache.org/examples/zh/index.html)  <br>
+
+
+## 平台兼容
+
+| H5  | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App  |
+| --- | ---------- | ------------ | ---------- | ---------- | --------- | ---- |
+| √   | √          | √         | √      | √       | √      | √ |
+
+
+## 安装
+- 第一步:在市场导入 [百度图表](https://ext.dcloud.net.cn/plugin?id=4899) 
+- 第二步:选择插件依赖:<br>
+  1、可以选插件内的`echarts`包或自定义包,自定义包[下载地址](https://echarts.apache.org/zh/builder.html)<br>
+  2、或者使用`npm`安装`echarts`	
+
+**注意** 
+* 🔔 echarts 5.3.0及以上
+* 🔔 如果是 `cli` 项目请下载插件到`src`目录下的`uni_modules`,没有这个目录就创建一个
+
+
+## 代码演示
+
+### Vue2
+- 引入依赖,可以是插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html),也可以是`npm`包
+
+```html
+<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef" @finished="init"></l-echart></view>
+```
+
+```js
+// 插件内的 三选一
+import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
+// 自定义的 三选一 下载后放入项目的路径
+import * as echarts from 'xxx/echarts.min'
+// npm包 三选一 需要在控制台 输入命令:npm install echarts
+import * as echarts from 'echarts'
+```
+
+```js
+export default {
+	data() {
+		return {
+			option: {
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow' 
+					},
+					confine: true
+				},
+				legend: {
+					data: ['热度', '正面', '负面']
+				},
+				grid: {
+					left: 20,
+					right: 20,
+					bottom: 15,
+					top: 40,
+					containLabel: true
+				},
+				xAxis: [
+					{
+						type: 'value',
+						axisLine: {
+							lineStyle: {
+								color: '#999999'
+							}
+						},
+						axisLabel: {
+							color: '#666666'
+						}
+					}
+				],
+				yAxis: [
+					{
+						type: 'category',
+						axisTick: { show: false },
+						data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
+						axisLine: {
+							lineStyle: {
+								color: '#999999'
+							}
+						},
+						axisLabel: {
+							color: '#666666'
+						}
+					}
+				],
+				series: [
+					{
+						name: '热度',
+						type: 'bar',
+						label: {
+							normal: {
+								show: true,
+								position: 'inside'
+							}
+						},
+						data: [300, 270, 340, 344, 300, 320, 310],
+					},
+					{
+						name: '正面',
+						type: 'bar',
+						stack: '总量',
+						label: {
+							normal: {
+								show: true
+							}
+						},
+						data: [120, 102, 141, 174, 190, 250, 220]
+					},
+					{
+						name: '负面',
+						type: 'bar',
+						stack: '总量',
+						label: {
+							normal: {
+								show: true,
+								position: 'left'
+							}
+						},
+						data: [-20, -32, -21, -34, -90, -130, -110]
+					}
+				]
+			},
+		};
+	},
+	// 组件能被调用必须是组件的节点已经被渲染到页面上
+	methods: {
+		async init() {
+			// chart 图表实例不能存在data里
+			const chart = await this.$refs.chartRef.init(echarts);
+			chart.setOption(this.option)
+		}
+	}
+}
+```
+
+### Vue3
+- 小程序可以使用`require`引入插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html)
+- `require`仅支持相对路径,不支持路径别名
+- 非小程序使用 `npm` 包
+
+
+```html
+<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef"></l-echart></view>
+```
+
+```js
+// 小程序 二选一 
+// 插件内的 二选一 
+const echarts = require('../../uni_modules/lime-echart/static/echarts.min');
+// 自定义的 二选一 下载后放入项目的路径
+const echarts = require('xxx/xxx/echarts');
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 非小程序 
+// 需要在控制台 输入命令:npm install echarts
+import * as echarts from 'echarts'
+```
+
+```js
+
+const chartRef = ref(null)
+const option = {
+	tooltip: {
+		trigger: 'axis',
+		axisPointer: {
+			type: 'shadow' 
+		},
+		confine: true
+	},
+	legend: {
+		data: ['热度', '正面', '负面']
+	},
+	grid: {
+		left: 20,
+		right: 20,
+		bottom: 15,
+		top: 40,
+		containLabel: true
+	},
+	xAxis: [
+		{
+			type: 'value',
+			axisLine: {
+				lineStyle: {
+					color: '#999999'
+				}
+			},
+			axisLabel: {
+				color: '#666666'
+			}
+		}
+	],
+	yAxis: [
+		{
+			type: 'category',
+			axisTick: { show: false },
+			data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
+			axisLine: {
+				lineStyle: {
+					color: '#999999'
+				}
+			},
+			axisLabel: {
+				color: '#666666'
+			}
+		}
+	],
+	series: [
+		{
+			name: '热度',
+			type: 'bar',
+			label: {
+				normal: {
+					show: true,
+					position: 'inside'
+				}
+			},
+			data: [300, 270, 340, 344, 300, 320, 310],
+		},
+		{
+			name: '正面',
+			type: 'bar',
+			stack: '总量',
+			label: {
+				normal: {
+					show: true
+				}
+			},
+			data: [120, 102, 141, 174, 190, 250, 220]
+		},
+		{
+			name: '负面',
+			type: 'bar',
+			stack: '总量',
+			label: {
+				normal: {
+					show: true,
+					position: 'left'
+				}
+			},
+			data: [-20, -32, -21, -34, -90, -130, -110]
+		}
+	]
+};
+
+
+onMounted( ()=>{
+	// 组件能被调用必须是组件的节点已经被渲染到页面上
+	setTimeout(async()=>{
+		if(!chartRef.value) return
+		const myChart = await chartRef.value.init(echarts)
+		myChart.setOption(option)
+	},300)
+})
+
+```
+
+
+### Uvue
+- Uvue和Nvue不需要引入`echarts`,因为它们的实现方式是`webview`
+- uniapp x需要HBX 4.13以上
+
+```html
+<view style="width: 100%; height: 408px;">
+	<l-echart ref="chartRef" @finished="init"></l-echart>
+</view>
+```
+
+```js
+// @ts-nocheck
+// #ifdef H5
+import * as echarts from 'echarts/dist/echarts.esm.js'
+// #endif
+const chartRef = ref<LEchartComponentPublicInstance|null>(null);
+const init = async () => {
+	if(chartRef.value== null) return
+	// #ifdef APP
+	const chart = await chartRef.value!.init(null)
+	// #endif
+	// #ifdef H5
+	const chart = await chartRef.value!.init(echarts, null)
+	// #endif
+	chart.setOption(option)
+}
+```
+
+
+## 数据更新
+- 1、使用 `ref` 可获取`setOption`设置更新
+- 2、也可以拿到图表实例`chart`设置`myChart.setOption(data)`
+
+```js
+// ref
+this.$refs.chart.setOption(data)
+
+// 图表实例
+myChart.setOption(data)
+```
+
+## 图表大小
+- 在有些场景下,我们希望当容器大小改变时,图表的大小也相应地改变。
+
+```js
+// 默认获取容器尺寸
+this.$refs.chart.resize()
+// 指定尺寸
+this.$refs.chart.resize({width: 375, height: 375})
+```
+
+## 自定义Tooltips
+- uvue\nvue 不支持
+由于除H5之外都不存在dom,但又有tooltips个性化的需求,代码就不贴了,看示例吧
+```
+代码位于/uni_modules/lime-echart/component/lime-echart
+```
+
+
+## 插件标签
+- 默认 l-echart 为 component
+- 默认 lime-echart 为 demo
+```html
+ // 在任意地方使用可查看domo, 代码位于/uni_modules/lime-echart/component/lime-echart
+<lime-echart></lime-echart>
+```
+
+
+## 常见问题
+- 钉钉小程序 由于没有`measureText`,模拟的`measureText`又无法得到当前字体的`fontWeight`,故可能存在估计不精细的问题
+- 微信小程序 `2d` 只支持 真机调试2.0
+- 微信开发工具会出现 `canvas` 不跟随页面的情况,真机不影响
+- 微信开发工具会出现 `canvas` 层级过高的问题,真机一般不受影响,可以先测只有两个元素的页面看是否会有层级问题。
+- toolbox 不支持 `saveImage`
+- echarts 5.3.0 的 lines 不支持 trailLength,故需设置为 `0`
+- dataZoom H5不要设置 `showDetail` 
+- 如果微信小程序的`tooltip`文字有阴影,可能是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
+- 如果钉钉小程序上传时报安全问题`Uint8Clamped`,可以向钉钉反馈是安全代码扫描把Uint8Clamped数组错误识别了,也可以在 echarts 文件修改`Uint8Clamped`
+```js
+// 找到这段代码把代码中`Uint8Clamped`改成`Uint8_Clamped`,再把下划线去掉,不过直接去掉`Uint8Clamped`也是可行的
+// ["Int8","Uint8","Uint8Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e+"Array]"]
+// 改成如下
+["Int8","Uint8","Uint8_Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e.replace('_','')+"Array]"]
+```
+
+### vue3
+如果您是使用 **vite + vue3** 非微信小程序可能会遇到`echarts`文件缺少`wx`判断导致无法使用或缺少`tooltip`<br>
+
+方式一:可以在`echarts.min.js`文件开头增加以下内容,参考插件内的echart.min.js的做法
+
+```js
+let global = null
+let wx = uni
+```
+
+方式二:在`vite.config.js`的`define`设置环境
+
+```js
+//  或者在`vite.config.js`的`define`设置环境
+import { defineConfig } from 'vite';
+import uni from '@dcloudio/vite-plugin-uni';
+
+const define = {}
+if(!["mp-weixin", "h5", "web"].includes(process.env.UNI_PLATFORM)) {
+	define['global'] =  null
+	define['wx'] =  'uni'
+}
+export default defineConfig({
+	plugins: [uni()],
+	define
+});
+```
+
+
+## Props
+
+| 参数             | 说明                                                            | 类型             | 默认值        | 版本 	|
+| ---------------  | --------                                                        | -------         | ------------ | ----- 	|
+| custom-style     | 自定义样式                                                      |   `string`       | -            | -     	|
+| type             | 指定 canvas 类型                                				 |    `string`      | `2d`         |   	    |
+| is-disable-scroll | 触摸图表时是否禁止页面滚动                                       |    `boolean`     | `false`     |   	    |
+| beforeDelay       |  延迟初始化 (毫秒)                       						|    `number`     | `30`     |   	    |
+| enableHover       |  PC端使用鼠标悬浮                       						|    `boolean`     | `false`     |   	    |
+
+## 事件
+
+| 参数                    | 说明                                                                                                             |
+| ---------------        | ---------------                                                                                                  |
+| init(echarts, chart => {})  | 初始化调用函数,第一个参数是传入`echarts`,第二个参数是回调函数,回调函数的参数是 `chart` 实例                                           |  
+| setChart(chart => {})        | 已经初始化后,请使用这个方法,是个回调函数,参数是 `chart` 实例                  |  
+| setOption(data)        | [图表配置项](https://echarts.apache.org/zh/option.html#title),用于更新 ,传递是数据 `option`  |  
+| clear()                | 清空当前实例,会移除实例中所有的组件和图表。  |  
+| dispose()              | 销毁实例  |  
+| showLoading()          | 显示加载  |  
+| hideLoading()          | 隐藏加载  |  
+| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(opt)  | 用于生成图片,与官方使用方法一致,但不需要传`canvasId`  |  
+
+
+## 打赏
+如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。  
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
uni_modules/lime-echart/static/ecStat.min.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
uni_modules/lime-echart/static/echarts.min.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 61 - 0
uni_modules/lime-echart/static/echarts.min1.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
uni_modules/lime-echart/static/uni.webview.1.5.5.js


+ 173 - 0
uni_modules/lime-echart/static/uvue.html

@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport"
+			content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+		<meta http-equiv="X-UA-Compatible" content="ie=edge">
+		<title></title>
+		<style type="text/css">
+			html,
+			body,
+			.canvas {
+				padding: 0;
+				margin: 0;
+				overflow-y: hidden;
+				background-color: transparent;
+				width: 100%;
+				height: 100%;
+			}
+		</style>
+	</head>
+	<body>
+		<div class="canvas" id="limeChart"></div>
+		<script type="text/javascript" src="./uni.webview.1.5.5.js"></script>
+		<script type="text/javascript" src="./echarts.min.js"></script>
+		<script type="text/javascript" src="./ecStat.min.js"></script>
+		<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-liquidfill@latest/dist/echarts-liquidfill.min.js"></script> -->
+		<script>
+			let chart = null;
+			let cache = [];
+			console.log = function() {
+				emit('log', {
+					log: arguments,
+				})
+			}
+
+			function emit(event, data) {
+				postMessage({
+					event,
+					data
+				})
+				cache = []
+			}
+
+			function postMessage(data) {
+				uni.webView.postMessage({
+					data
+				})
+				// window.__uniapp_x_.postMessage(JSON.stringify(data))
+			};
+
+			function stringify(key, value) {
+				if (typeof value === 'object' && value !== null) {
+					if (cache.indexOf(value) !== -1) {
+						return;
+					}
+					cache.push(value);
+				}
+				return value;
+			}
+
+			function parse(name, callback, options) {
+				const optionNameReg = /[\w]+\.setOption\(([\w]+\.)?([\w]+)\)/
+				if (optionNameReg.test(callback)) {
+					const optionNames = callback.match(optionNameReg)
+					if (optionNames[1]) {
+						const _this = optionNames[1].split('.')[0]
+						window[_this] = {}
+						window[_this][optionNames[2]] = options
+						return optionNames[2]
+					} else {
+						return null
+					}
+				}
+				return null
+			}
+
+			function init(callback, options, opts, theme) {
+				if (!chart) {
+					chart = echarts.init(document.getElementById('limeChart'), theme, opts)
+
+					if (options) {
+						chart.setOption(options)
+					}
+				}
+			}
+
+			function on(data) {
+				if (chart && data.length > 0) {
+					const [type, query] = data
+					const key = `${type}${JSON.stringify(query||'')}`
+					if (query) {
+						chart.on(type, query, function(options) {
+							var obj = {};
+							Object.keys(options).forEach(function(key) {
+								if (key != 'event') {
+									obj[key] = options[key];
+								}
+							});
+							emit(key, {
+								event: key,
+								options: obj,
+							});
+						});
+					} else {
+						chart.on(type, function(options) {
+							var obj = {};
+							Object.keys(options).forEach(function(key) {
+								if (key != 'event') {
+									obj[key] = options[key];
+								}
+							});
+							emit(key, {
+								event: key,
+								options: obj,
+							});
+						});
+					}
+				}
+
+			}
+
+			function setChart(callback, options) {
+				if (!callback) return
+				if (chart && callback && options) {
+					var r = null
+					const name = parse('r', callback, options)
+					if (name) this[name] = options
+					eval(`r = ${callback};`)
+					if (r) {
+						r(chart)
+					}
+				}
+			}
+
+			function setOption(data) {
+				if (chart) chart.setOption(data[0], data[1])
+			}
+
+			function showLoading(data) {
+				if (chart) chart.showLoading(data[0], data[1])
+			}
+
+			function hideLoading() {
+				if (chart) chart.hideLoading()
+			}
+
+			function clear() {
+				if (chart) chart.clear()
+
+			}
+
+			function dispose() {
+				if (chart) chart.dispose()
+			}
+
+			function resize(size) {
+				if (chart) chart.resize(size)
+			}
+
+			function canvasToTempFilePath(opt) {
+				if (chart) {
+					delete opt.success
+					const src = chart.getDataURL(opt)
+					postMessage({
+						// event: 'file',
+						file: src
+					})
+				}
+			}
+		</script>
+	</body>
+</html>

+ 21 - 0
uni_modules/uview-ui/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 www.uviewui.com
+
+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.

+ 66 - 0
uni_modules/uview-ui/README.md

@@ -0,0 +1,66 @@
+<p align="center">
+    <img alt="logo" src="https://uviewui.com/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 2.0</h3>
+<h3 align="center">多平台快速开发的UI框架</h3>
+
+[![stars](https://img.shields.io/github/stars/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0)
+[![forks](https://img.shields.io/github/forks/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0)
+[![issues](https://img.shields.io/github/issues/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0/issues)
+[![Website](https://img.shields.io/badge/uView-up-blue?style=flat-square)](https://uviewui.com)
+[![release](https://img.shields.io/github/v/release/umicro/uView2.0?style=flat-square)](https://gitee.com/umicro/uView2.0/releases)
+[![license](https://img.shields.io/github/license/umicro/uView2.0?style=flat-square)](https://en.wikipedia.org/wiki/MIT_License)
+
+## 说明
+
+uView UI,是[uni-app](https://uniapp.dcloud.io/)全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
+
+## [官方文档:https://uviewui.com](https://uviewui.com)
+
+
+## 预览
+
+您可以通过**微信**扫码,查看最佳的演示效果。
+<br>
+<br>
+<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
+
+
+## 链接
+
+- [官方文档](https://www.uviewui.com/)
+- [更新日志](https://www.uviewui.com/components/changelog.html)
+- [升级指南](https://www.uviewui.com/components/changeGuide.html)
+- [关于我们](https://www.uviewui.com/cooperation/about.html)
+
+## 交流反馈
+
+欢迎加入我们的QQ群交流反馈:[点此跳转](https://www.uviewui.com/components/addQQGroup.html)
+
+## 关于PR
+
+> 我们非常乐意接受各位的优质PR,但在此之前我希望您了解uView2.0是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括nvue页面、vue页面。
+> 所以希望在您修复bug并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢!
+
+## 安装
+
+#### **uni-app插件市场链接** —— [https://ext.dcloud.net.cn/plugin?id=1593](https://ext.dcloud.net.cn/plugin?id=1593)
+
+请通过[官网安装文档](https://www.uviewui.com/components/install.html)了解更详细的内容
+
+## 快速上手
+
+请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
+
+## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<u-button text="按钮"></u-button>
+</template>
+```
+
+## 版权信息
+uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。
+

+ 374 - 0
uni_modules/uview-ui/changelog.md

@@ -0,0 +1,374 @@
+## 2.0.37(2024-03-17)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复表单校验`trigger`触发器参数无效问题
+2. 修复`u-input`组件的`password`属性在动态切换为`false`时失效的问题
+3. 添加微信小程序用户同意隐私协议事件回调
+4. 修复支付宝小程序picker样式问题
+5. `u-modal`添加`duration`字段控制动画过度时间
+6. 修复`picker` `lastIndex`异常导致的`column`异常问题
+7. `tabs`增加长按事件支持
+8. 修复`u-avatar` `square`属性在小程序`open-data`下无效问题
+9. 其他一些修复
+## 2.0.36(2023-03-27)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 重构`deepClone` & `deepMerge`方法
+2. 其他优化
+## 2.0.34(2022-09-24)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. `u-input`、`u-textarea`增加`ignoreCompositionEvent`属性
+2. 修复`route`方法调用可能报错的问题
+3. 修复`u-no-network`组件`z-index`无效的问题
+4. 修复`textarea`组件在h5上confirmType=""报错的问题
+5. `u-rate`适配`nvue`
+6. 优化验证手机号码的正则表达式(根据工信部发布的《电信网编号计划(2017年版)》进行修改。)
+7. `form-item`添加`labelPosition`属性
+8. `u-calendar`修复`maxDate`设置为当前日期,并且当前时间大于08:00时无法显示日期列表的问题 (#724)
+9. `u-radio`增加一个默认插槽用于自定义修改label内容 (#680)
+10. 修复`timeFormat`函数在safari重的兼容性问题 (#664)
+## 2.0.33(2022-06-17)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`loadmore`组件`lineColor`类型错误问题
+2. 修复`u-parse`组件`imgtap`、`linktap`不生效问题
+## 2.0.32(2022-06-16)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+1. `u-loadmore`新增自定义颜色、虚/实线
+2. 修复`u-swiper-action`组件部分平台不能上下滑动的问题
+3. 修复`u-list`回弹问题
+4. 修复`notice-bar`组件动画在低端安卓机可能会抖动的问题
+5. `u-loading-page`添加控制图标大小的属性`iconSize`
+6. 修复`u-tooltip`组件`color`参数不生效的问题
+7. 修复`u--input`组件使用`blur`事件输出为`undefined`的bug
+8. `u-code-input`组件新增键盘弹起时,是否自动上推页面参数`adjustPosition`
+9. 修复`image`组件`load`事件无回调对象问题
+10. 修复`button`组件`loadingSize`设置无效问题
+10. 其他修复
+## 2.0.31(2022-04-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`upload`在`vue`页面上传成功后没有成功标志的问题
+2. 解决演示项目中微信小程序模拟上传图片一直出于上传中问题
+3. 修复`u-code-input`组件在`nvue`页面编译到`app`平台上光标异常问题(`app`去除此功能)
+4. 修复`actionSheet`组件标题关闭按钮点击事件名称错误的问题
+5. 其他修复
+## 2.0.30(2022-04-04)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. `u-rate`增加`readonly`属性
+2. `tabs`滑块支持设置背景图片
+3. 修复`u-subsection` `mode`为`subsection`时,滑块样式不正确的问题
+4. `u-code-input`添加光标效果动画
+5. 修复`popup`的`open`事件不触发
+6. 修复`u-flex-column`无效的问题
+7. 修复`u-datetime-picker`索引在特定场合异常问题
+8. 修复`u-datetime-picker`最小时间字符串模板错误问题
+9. `u-swiper`添加`m3u8`验证
+10. `u-swiper`修改判断image和video逻辑
+11. 修复`swiper`无法使用本地图片问题,增加`type`参数
+12. 修复`u-row-notice`格式错误问题
+13. 修复`u-switch`组件当`unit`为`rpx`时,`nodeStyle`消失的问题
+14. 修复`datetime-picker`组件`showToolbar`与`visibleItemCount`属性无效的问题
+15. 修复`upload`组件条件编译位置判断错误,导致`previewImage`属性设置为`false`时,整个组件都会被隐藏的问题
+16. 修复`u-checkbox-group`设置`shape`属性无效的问题
+17. 修复`u-upload`的`capture`传入字符串的时候不生效的问题
+18. 修复`u-action-sheet`组件,关闭事件逻辑错误的问题
+19. 修复`u-list`触顶事件的触发错误的问题
+20. 修复`u-text`只有手机号可拨打的问题
+21. 修复`u-textarea`不能换行的问题
+22. 其他修复
+## 2.0.29(2022-03-13)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`u--text`组件设置`decoration`属性未生效的问题
+2. 修复`u-datetime-picker`使用`formatter`后返回值不正确
+3. 修复`u-datetime-picker` `intercept` 可能为undefined
+4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度
+5. 修复mixin中bem方法生成的类名在支付宝和字节小程序中失效
+6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错,不能选中第一列时间的bug
+7. 修复`u-datetime-picker`使用`formatter`后返回值不正确
+8. 修复`u-image`组件`loading`无效果的问题
+9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题
+10. 修复`u-datetime-picker`组件`itemHeight`无效问题
+11. 其他修复
+## 2.0.28(2022-02-22)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. search组件新增searchIconSize属性
+2. 兼容Safari/Webkit中传入时间格式如2022-02-17 12:00:56
+3. 修复text value.js 判断日期出format错误问题
+4. priceFormat格式化金额出现精度错误
+5. priceFormat在部分情况下出现精度损失问题
+6. 优化表单rules提示
+7. 修复avatar组件src为空时,展示状态不对
+8. 其他修复
+## 2.0.27(2022-01-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1.样式修复
+## 2.0.26(2022-01-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1.样式修复
+## 2.0.25(2022-01-27)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复text组件mode=price时,可能会导致精度错误的问题
+2. 添加$u.setConfig()方法,可设置uView内置的config, props, zIndex, color属性,详见:[修改uView内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+3. 优化form组件在errorType=toast时,如果输入错误页面会有抖动的问题
+4. 修复$u.addUnit()对配置默认单位可能无效的问题
+## 2.0.24(2022-01-25)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复swiper在current指定非0时缩放有误
+2. 修复u-icon添加stop属性的时候报错
+3. 优化遗留的通过正则判断rpx单位的问题
+4. 优化Layout布局 vue使用gutter时,会超出固定区域
+5. 优化search组件高度单位问题(rpx -> px)
+6. 修复u-image slot 加载和错误的图片失去了高度
+7. 修复u-index-list中footer插槽与header插槽存在性判断错误
+8. 修复部分机型下u-popup关闭时会闪烁
+9. 修复u-image在nvue-app下失去宽高
+10. 修复u-popup运行报错
+11. 修复u-tooltip报错
+12. 修复box-sizing在app下的警告
+13. 修复u-navbar在小程序中报运行时错误
+14. 其他修复
+## 2.0.23(2022-01-24)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复image组件在hx3.3.9的nvue下可能会显示异常的问题
+2. 修复col组件gutter参数带rpx单位处理不正确的问题
+3. 修复text组件单行时无法显示省略号的问题
+4. navbar添加titleStyle参数
+5. 升级到hx3.3.9可消除nvue下控制台样式警告的问题
+## 2.0.22(2022-01-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. $u.page()方法优化,避免在特殊场景可能报错的问题
+2. picker组件添加immediateChange参数
+3. 新增$u.pages()方法
+## 2.0.21(2022-01-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化:form组件在用户设置rules的时候提示用户model必传
+2. 优化遗留的通过正则判断rpx单位的问题
+3. 修复微信小程序环境中tabbar组件开启safeAreaInsetBottom属性后,placeholder高度填充不正确
+4. 修复swiper在current指定非0时缩放有误
+5. 修复u-icon添加stop属性的时候报错
+6. 修复upload组件在accept=all的时候没有作用
+7. 修复在text组件mode为phone时call属性无效的问题
+8. 处理u-form clearValidate方法
+9. 其他修复
+## 2.0.20(2022-01-14)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复calendar默认会选择一个日期,如果直接点确定的话,无法取到值的问题
+2. 修复Slider缺少disabled props 还有注释
+3. 修复u-notice-bar点击事件无法拿到index索引值的问题
+4. 修复u-collapse-item在vue文件下,app端自定义插槽不生效的问题
+5. 优化头像为空时显示默认头像 
+6. 修复图片地址赋值后判断加载状态为完成问题
+7. 修复日历滚动到默认日期月份区域
+8. search组件暴露点击左边icon事件
+9. 修复u-form clearValidate方法不生效
+10. upload h5端增加返回文件参数(文件的name参数)
+11. 处理upload选择文件后url为blob类型无法预览的问题
+12. u-code-input 修复输入框没有往左移出一半屏幕
+13. 修复Upload上传 disabled为true时,控制台报hoverClass类型错误
+14. 临时处理ios app下grid点击坍塌问题
+15. 其他修复
+## 2.0.19(2021-12-29)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化微信小程序包体积可在微信中预览,请升级HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码”
+2. 优化微信小程序setData性能,处理某些方法如$u.route()无法在模板中使用的问题
+3. navbar添加autoBack参数
+4. 允许avatar组件的事件冒泡
+5. 修复cell组件报错问题
+6. 其他修复
+## 2.0.18(2021-12-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复app端编译报错问题
+2. 重新处理微信小程序端setData过大的性能问题
+3. 修复边框问题
+4. 修复最大最小月份不大于0则没有数据出现的问题
+5. 修复SwipeAction微信小程序端无法上下滑动问题
+6. 修复input的placeholder在小程序端默认显示为true问题
+7. 修复divider组件click事件无效问题
+8. 修复u-code-input maxlength 属性值为 String 类型时显示异常
+9. 修复当 grid只有 1到2时 在小程序端algin设置无效的问题
+10. 处理form-item的label为top时,取消错误提示的左边距
+11. 其他修复
+## 2.0.17(2021-12-26)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 解决HBuilderX3.3.3.20211225版本导致的样式问题
+2. calendar日历添加monthNum参数
+3. navbar添加center slot
+## 2.0.16(2021-12-25)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 解决微信小程序setData性能问题
+2. 修复count-down组件change事件不触发问题
+## 2.0.15(2021-12-21)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复Cell单元格titleWidth无效
+2. 修复cheakbox组件ischecked不更新
+3. 修复keyboard是否显示"."按键默认值问题
+4. 修复number-keyboard是否显示键盘的"."符号问题
+5. 修复Input输入框 readonly无效
+6. 修复u-avatar 导致打包app、H5时候报错问题
+7. 修复Upload上传deletable无效
+8. 修复upload当设置maxSize时无效的问题
+9. 修复tabs lineWidth传入带单位的字符串的时候偏移量计算错误问题
+10. 修复rate组件在有padding的view内,显示的星星位置和可触摸区域不匹配,无法正常选中星星
+## 2.0.13(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题
+## 2.0.12(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复tabs组件在vue环境下划线消失的问题
+2. 修复upload组件在安卓小程序无法选择视频的问题
+3. 添加uni.$u.config.unit配置,用于配置参数默认单位,详见:[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+4. 修复textarea组件在没绑定v-model时,字符统计不生效问题
+5. 修复nvue下控制是否出现滚动条失效问题
+## 2.0.11(2021-12-13)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. text组件align参数无效的问题
+2. subsection组件添加keyName参数
+3. upload组件无法判断[Object file]类型的问题
+4. 处理notify层级过低问题
+5. codeInput组件添加disabledDot参数
+6. 处理actionSheet组件round参数无效的问题
+7. calendar组件添加round参数用于控制圆角值
+8. 处理swipeAction组件在vue环境下默认被打开的问题
+9. button组件的throttleTime节流参数无效的问题
+10. 解决u-notify手动关闭方法close()无效的问题
+11. input组件readonly不生效问题
+12. tag组件type参数为info不生效问题
+## 2.0.10(2021-12-08)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复button sendMessagePath属性不生效
+2. 修复DatetimePicker选择器title无效
+3. 修复u-toast设置loading=true不生效
+4. 修复u-text金额模式传0报错
+5. 修复u-toast组件的icon属性配置不生效
+6. button的icon在特殊场景下的颜色优化
+7. IndexList优化,增加#
+## 2.0.9(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化swiper的height支持100%值(仅vue有效),修复嵌入视频时click事件无法触发的问题
+2. 优化tabs组件对list值为空的判断,或者动态变化list时重新计算相关尺寸的问题
+3. 优化datetime-picker组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过v-model绑定值才有效
+4. 修复upload内嵌在其他组件中,选择图片可能不会换行的问题
+## 2.0.8(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复toast的position参数无效问题
+2. 处理input在ios nvue上无法获得焦点的问题
+3. avatar-group组件添加extraValue参数,让剩余展示数量可手动控制
+4. tabs组件添加keyName参数用于配置从对象中读取的键名
+5. 处理text组件名字脱敏默认配置无效的问题
+6. 处理picker组件item文本太长换行问题
+## 2.0.7(2021-11-30)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复radio和checkbox动态改变v-model无效的问题。
+2. 优化form规则validator在微信小程序用法
+3. 修复backtop组件mode参数在微信小程序无效的问题
+4. 处理Album的previewFullImage属性无效的问题
+5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题
+## 2.0.6(2021-11-27)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 处理tag组件在vue下边框无效的问题。
+2. 处理popup组件圆角参数可能无效的问题。
+3. 处理tabs组件lineColor参数可能无效的问题。
+4. propgress组件在值很小时,显示异常的问题。
+## 2.0.5(2021-11-25)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. calendar在vue下显示异常问题。 
+2. form组件labelPosition和errorType参数无效的问题
+3. input组件inputAlign无效的问题
+4. 其他一些修复
+## 2.0.4(2021-11-23)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+0. input组件缺失@confirm事件,以及subfix和prefix无效问题
+1. component.scss文件样式在vue下干扰全局布局问题
+2. 修复subsection在vue环境下表现异常的问题
+3. tag组件的bgColor等参数无效的问题
+4. upload组件不换行的问题
+5. 其他的一些修复处理
+## 2.0.3(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 处理modal的confirm回调事件拼写错误问题
+6. 处理input组件@input事件参数错误问题
+7. 其他一些修复
+## 2.0.2(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+6. 优化loading-icon组件的scss写法问题,防止不兼容新版本scss
+## 2.0.0(2020-11-15)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+
+

+ 92 - 0
uni_modules/uview-ui/components/u--form/u--form.vue

@@ -0,0 +1,92 @@
+<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'
+	/**
+	 * Form 表单
+	 * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
+	 * @tutorial https://www.uviewui.com/components/form.html
+	 * @property {Object}						model			当前form的需要验证字段的集合
+	 * @property {Object | Function | Array}	rules			验证规则
+	 * @property {String}						errorType		错误的提示方式,见上方说明 ( 默认 message )
+	 * @property {Boolean}						borderBottom	是否显示表单域的下划线边框   ( 默认 true )
+	 * @property {String}						labelPosition	表单域提示文字的位置,left-左侧,top-上方 ( 默认 'left' )
+	 * @property {String | Number}				labelWidth		提示文字的宽度,单位px  ( 默认 45 )
+	 * @property {String}						labelAlign		lable字体的对齐方式   ( 默认 ‘left' )
+	 * @property {Object}						labelStyle		lable的样式,对象形式
+	 * @example <u--formlabelPosition="left" :model="model1" :rules="rules" ref="form1"></u--form>
+	 */
+	export default {
+		// #ifdef MP-WEIXIN
+		name: 'u-form',
+		// #endif
+		// #ifndef MP-WEIXIN
+		name: 'u--form',
+		// #endif
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvForm
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
+			setRules(rules) {
+				this.$refs.uForm.setRules(rules)
+			},
+			validate() {
+				/**
+				 * 在微信小程序中,通过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()
+			},
+			validateField(value, callback, event) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validateField(value, callback, event)
+			},
+			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>

+ 73 - 0
uni_modules/uview-ui/components/u--image/u--image.vue

@@ -0,0 +1,73 @@
+<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';
+	/**
+	 * Image 图片
+	 * @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
+	 * @tutorial https://uviewui.com/components/image.html
+	 * @property {String}			src 				图片地址
+	 * @property {String}			mode 				裁剪模式,见官网说明 (默认 'aspectFill' )
+	 * @property {String | Number}	width 				宽度,单位任意,如果为数值,则为px单位 (默认 '300' )
+	 * @property {String | Number}	height 				高度,单位任意,如果为数值,则为px单位 (默认 '225' )
+	 * @property {String}			shape 				图片形状,circle-圆形,square-方形 (默认 'square' )
+	 * @property {String | Number}	radius		 		圆角值,单位任意,如果为数值,则为px单位 (默认 0 )
+	 * @property {Boolean}			lazyLoad			是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效 (默认 true )
+	 * @property {Boolean}			showMenuByLongpress	是否开启长按图片显示识别小程序码菜单,仅微信小程序有效 (默认 true )
+	 * @property {String}			loadingIcon 		加载中的图标,或者小图片 (默认 'photo' )
+	 * @property {String}			errorIcon 			加载失败的图标,或者小图片 (默认 'error-circle' )
+	 * @property {Boolean}			showLoading 		是否显示加载中的图标或者自定义的slot (默认 true )
+	 * @property {Boolean}			showError 			是否显示加载错误的图标或者自定义的slot (默认 true )
+	 * @property {Boolean}			fade 				是否需要淡入效果 (默认 true )
+	 * @property {Boolean}			webp 				只支持网络资源,只对微信小程序有效 (默认 false )
+	 * @property {String | Number}	duration 			搭配fade参数的过渡时间,单位ms (默认 500 )
+	 * @property {String}			bgColor 			背景颜色,用于深色页面加载图片时,为了和背景色融合  (默认 '#f3f4f6' )
+	 * @property {Object}			customStyle  		定义需要用到的外部样式
+	 * @event {Function}	click	点击图片时触发
+	 * @event {Function}	error	图片加载失败时触发
+	 * @event {Function} load 图片加载成功时触发
+	 * @example <u--image width="100%" height="300px" :src="src"></u--image>
+	 */
+	export default {
+		name: 'u--image',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvImage
+		},
+	}
+</script>

+ 115 - 0
uni_modules/uview-ui/components/u--input/u--input.vue

@@ -0,0 +1,115 @@
+<template>
+	<uvInput
+		:value="value"
+		: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"
+		@focus="e => $emit('focus', e)"
+		@blur="e => $emit('blur', e)"
+		@keyboardheightchange="e => $emit('keyboardheightchange', e)"
+		@change="e => $emit('change', e)"
+		@input="e => $emit('input', e)"
+		@confirm="e => $emit('confirm', e)"
+		@clear="$emit('clear')"
+		@click="$emit('click')"
+	>
+		<!-- #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'
+	/**
+	 * Input 输入框
+	 * @description  此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件u-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。
+	 * @tutorial https://uviewui.com/components/input.html
+	 * @property {String | Number}	value					输入的值
+	 * @property {String}			type					输入框类型,见上方说明 ( 默认 'text' )
+	 * @property {Boolean}			fixed					如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true,兼容性:微信小程序、百度小程序、字节跳动小程序、QQ小程序 ( 默认 false )
+	 * @property {Boolean}			disabled				是否禁用输入框 ( 默认 false )
+	 * @property {String}			disabledColor			禁用状态时的背景色( 默认 '#f5f7fa' )
+	 * @property {Boolean}			clearable				是否显示清除控件 ( 默认 false )
+	 * @property {Boolean}			password				是否密码类型 ( 默认 false )
+	 * @property {String | Number}	maxlength				最大输入长度,设置为 -1 的时候不限制最大长度 ( 默认 -1 )
+	 * @property {String}			placeholder				输入框为空时的占位符
+	 * @property {String}			placeholderClass		指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' )
+	 * @property {String | Object}	placeholderStyle		指定placeholder的样式,字符串/对象形式,如"color: red;"
+	 * @property {Boolean}			showWordLimit			是否显示输入字数统计,只在 type ="text"或type ="textarea"时有效 ( 默认 false )
+	 * @property {String}			confirmType				设置右下角按钮的文字,兼容性详见uni-app文档 ( 默认 'done' )
+	 * @property {Boolean}			confirmHold				点击键盘右下角按钮时是否保持键盘不收起,H5无效 ( 默认 false )
+	 * @property {Boolean}			holdKeyboard			focus时,点击页面的时候不收起键盘,微信小程序有效 ( 默认 false )
+	 * @property {Boolean}			focus					自动获取焦点,在 H5 平台能否聚焦以及软键盘是否跟随弹出,取决于当前浏览器本身的实现。nvue 页面不支持,需使用组件的 focus()、blur() 方法控制焦点 ( 默认 false )
+	 * @property {Boolean}			autoBlur				键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 ( 默认 false )
+	 * @property {Boolean}			disableDefaultPadding	是否去掉 iOS 下的默认内边距,仅微信小程序,且type=textarea时有效 ( 默认 false )
+	 * @property {String | Number}	cursor					指定focus时光标的位置( 默认 -1 )
+	 * @property {String | Number}	cursorSpacing			输入框聚焦时底部与键盘的距离 ( 默认 30 )
+	 * @property {String | Number}	selectionStart			光标起始位置,自动聚集时有效,需与selection-end搭配使用 ( 默认 -1 )
+	 * @property {String | Number}	selectionEnd			光标结束位置,自动聚集时有效,需与selection-start搭配使用 ( 默认 -1 )
+	 * @property {Boolean}			adjustPosition			键盘弹起时,是否自动上推页面 ( 默认 true )
+	 * @property {String}			inputAlign				输入框内容对齐方式( 默认 'left' )
+	 * @property {String | Number}	fontSize				输入框字体的大小 ( 默认 '15px' )
+	 * @property {String}			color					输入框字体颜色	( 默认 '#303133' )
+	 * @property {Function}			formatter			    内容式化函数
+	 * @property {String}			prefixIcon				输入框前置图标
+	 * @property {String | Object}	prefixIconStyle			前置图标样式,对象或字符串
+	 * @property {String}			suffixIcon				输入框后置图标
+	 * @property {String | Object}	suffixIconStyle			后置图标样式,对象或字符串
+	 * @property {String}			border					边框类型,surround-四周边框,bottom-底部边框,none-无边框 ( 默认 'surround' )
+	 * @property {Boolean}			readonly				是否只读,与disabled不同之处在于disabled会置灰组件,而readonly则不会 ( 默认 false )
+	 * @property {String}			shape					输入框形状,circle-圆形,square-方形 ( 默认 'square' )
+	 * @property {Object}			customStyle				定义需要用到的外部样式
+	 * @property {Boolean}			ignoreCompositionEvent	是否忽略组件内对文本合成系统事件的处理。
+	 * @example <u--input v-model="value" :password="true" suffix-icon="lock-fill" />
+	 */
+	export default {
+		name: 'u--input',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvInput
+		},
+	}
+</script>

+ 72 - 0
uni_modules/uview-ui/components/u--text/u--text.vue

@@ -0,0 +1,72 @@
+<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"
+        @click="$emit('click')"
+    ></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";
+/**
+ * Text 文本
+ * @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。
+ * @tutorial https://www.uviewui.com/components/loading.html
+ * @property {String} 					type		主题颜色
+ * @property {Boolean} 					show		是否显示(默认 true )
+ * @property {String | Number}			text		显示的值
+ * @property {String}					prefixIcon	前置图标
+ * @property {String} 					suffixIcon	后置图标
+ * @property {String} 					mode		文本处理的匹配模式 text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
+ * @property {String} 					href		mode=link下,配置的链接
+ * @property {String | Function} 		format		格式化规则
+ * @property {Boolean} 					call		mode=phone时,点击文本是否拨打电话(默认 false )
+ * @property {String} 					openType	小程序的打开方式
+ * @property {Boolean} 					bold		是否粗体,默认normal(默认 false )
+ * @property {Boolean} 					block		是否块状(默认 false )
+ * @property {String | Number} 			lines		文本显示的行数,如果设置,超出此行数,将会显示省略号
+ * @property {String} 					color		文本颜色(默认 '#303133' )
+ * @property {String | Number} 			size		字体大小(默认 15 )
+ * @property {Object | String} 			iconStyle	图标的样式 (默认 {fontSize: '15px'} )
+ * @property {String} 					decoration	文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' )
+ * @property {Object | String | Number}	margin		外边距,对象、字符串,数值形式均可(默认 0 )
+ * @property {String | Number} 			lineHeight	文本行高
+ * @property {String} 					align		文本对齐方式,可选值left|center|right(默认 'left' )
+ * @property {String} 					wordWrap	文字换行,可选值break-word|normal|anywhere(默认 'normal' )
+ * @event {Function} click  点击触发事件
+ * @example <u--text text="我用十年青春,赴你最后之约"></u--text>
+ */
+export default {
+    name: "u--text",
+    mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+    components: {
+        uvText,
+    },
+};
+</script>

+ 85 - 0
uni_modules/uview-ui/components/u--textarea/u--textarea.vue

@@ -0,0 +1,85 @@
+<template>
+	<uvTextarea
+		:value="value"
+		: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"
+		@focus="e => $emit('focus', e)"
+		@blur="e => $emit('blur', e)"
+		@linechange="e => $emit('linechange', e)"
+		@confirm="e => $emit('confirm', e)"
+		@input="e => $emit('input', e)"
+		@keyboardheightchange="e => $emit('keyboardheightchange', 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'
+	/**
+	 * Textarea 文本域
+	 * @description 文本域此组件满足了可能出现的表单信息补充,编辑等实际逻辑的功能,内置了字数校验等
+	 * @tutorial https://www.uviewui.com/components/textarea.html
+	 *
+	 * @property {String | Number} 		value					输入框的内容
+	 * @property {String | Number}		placeholder				输入框为空时占位符
+	 * @property {String}			    placeholderClass		指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' )
+	 * @property {String | Object}	    placeholderStyle		指定placeholder的样式,字符串/对象形式,如"color: red;"
+	 * @property {String | Number}		height					输入框高度(默认 70 )
+	 * @property {String}				confirmType				设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效(默认 'done' )
+	 * @property {Boolean}				disabled				是否禁用(默认 false )
+	 * @property {Boolean}				count					是否显示统计字数(默认 false )
+	 * @property {Boolean}				focus					是否自动获取焦点,nvue不支持,H5取决于浏览器的实现(默认 false )
+	 * @property {Boolean | Function}	autoHeight				是否自动增加高度(默认 false )
+	 * @property {Boolean}				fixed					如果textarea是在一个position:fixed的区域,需要显示指定属性fixed为true(默认 false )
+	 * @property {Number}				cursorSpacing			指定光标与键盘的距离(默认 0 )
+	 * @property {String | Number}		cursor					指定focus时的光标位置
+	 * @property {Function}			    formatter			    内容式化函数
+	 * @property {Boolean}				showConfirmBar			是否显示键盘上方带有”完成“按钮那一栏,(默认 true )
+	 * @property {Number}				selectionStart			光标起始位置,自动聚焦时有效,需与selection-end搭配使用,(默认 -1 )
+	 * @property {Number | Number}		selectionEnd			光标结束位置,自动聚焦时有效,需与selection-start搭配使用(默认 -1 )
+	 * @property {Boolean}				adjustPosition			键盘弹起时,是否自动上推页面(默认 true )
+	 * @property {Boolean | Number}		disableDefaultPadding	是否去掉 iOS 下的默认内边距,只微信小程序有效(默认 false )
+	 * @property {Boolean}				holdKeyboard			focus时,点击页面的时候不收起键盘,只微信小程序有效(默认 false )
+	 * @property {String | Number}		maxlength				最大输入长度,设置为 -1 的时候不限制最大长度(默认 140 )
+	 * @property {String}				border					边框类型,surround-四周边框,none-无边框,bottom-底部边框(默认 'surround' )
+	 * @property {Boolean}				ignoreCompositionEvent	是否忽略组件内对文本合成系统事件的处理
+	 *
+	 * @event {Function(e)} focus					输入框聚焦时触发,event.detail = { value, height },height 为键盘高度
+	 * @event {Function(e)} blur					输入框失去焦点时触发,event.detail = {value, cursor}
+	 * @event {Function(e)} linechange				输入框行数变化时调用,event.detail = {height: 0, heightRpx: 0, lineCount: 0}
+	 * @event {Function(e)} input					当键盘输入时,触发 input 事件
+	 * @event {Function(e)} confirm					点击完成时, 触发 confirm 事件
+	 * @event {Function(e)} keyboardheightchange	键盘高度发生变化的时候触发此事件
+	 * @example <u--textarea v-model="value1" placeholder="请输入内容" ></u--textarea>
+	 */
+	export default {
+		name: 'u--textarea',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvTextarea
+		},
+	}
+</script>

+ 54 - 0
uni_modules/uview-ui/components/u-action-sheet/props.js

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

+ 278 - 0
uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue

@@ -0,0 +1,278 @@
+
+<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>
+				<view class="u-action-sheet__item-wrap">
+					<template v-for="(item, index) in actions">
+						<!-- #ifdef MP -->
+						<button
+						    :key="index"
+						    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>
+					</template>
+				</view>
+			</slot>
+			<u-gap
+			    bgColor="#eaeaec"
+			    height="6"
+			    v-if="cancelText"
+			></u-gap>
+			<view hover-class="u-action-sheet--hover">
+				<text
+				    @touchmove.stop.prevent
+				    :hover-stay-time="150"
+				    v-if="cancelText"
+				    class="u-action-sheet__cancel-text"
+				    @tap="cancel"
+				>{{cancelText}}</text>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	import openType from '../../libs/mixin/openType'
+	import button from '../../libs/mixin/button'
+	import props from './props.js';
+	/**
+	 * ActionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://www.uviewui.com/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, button, uni.$u.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 = uni.$u.addUnit(this.actions[index].fontSize)
+					// 选项被禁用的样式
+					if (this.actions[index].disabled) style.color = '#c0c4cc'
+					return style;
+				}
+			},
+		},
+		methods: {
+			closeHandler() {
+				// 允许点击遮罩关闭时,才发出close事件
+				if(this.closeOnClickOverlay) {
+					this.$emit('close')
+				}
+			},
+			// 点击取消按钮
+			cancel() {
+				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('close')
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	$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:15px !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>

+ 59 - 0
uni_modules/uview-ui/components/u-album/props.js

@@ -0,0 +1,59 @@
+export default {
+    props: {
+        // 图片地址,Array<String>|Array<Object>形式
+        urls: {
+            type: Array,
+            default: uni.$u.props.album.urls
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.album.keyName
+        },
+        // 单图时,图片长边的长度
+        singleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.singleSize
+        },
+        // 多图时,图片边长
+        multipleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.multipleSize
+        },
+        // 多图时,图片水平和垂直之间的间隔
+        space: {
+            type: [String, Number],
+            default: uni.$u.props.album.space
+        },
+        // 单图时,图片缩放裁剪的模式
+        singleMode: {
+            type: String,
+            default: uni.$u.props.album.singleMode
+        },
+        // 多图时,图片缩放裁剪的模式
+        multipleMode: {
+            type: String,
+            default: uni.$u.props.album.multipleMode
+        },
+        // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.maxCount
+        },
+        // 是否可以预览图片
+        previewFullImage: {
+            type: Boolean,
+            default: uni.$u.props.album.previewFullImage
+        },
+        // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+        rowCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.rowCount
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.album.showMore
+        }
+    }
+}

+ 259 - 0
uni_modules/uview-ui/components/u-album/u-album.vue

@@ -0,0 +1,259 @@
+<template>
+    <view class="u-album">
+        <view
+            class="u-album__row"
+            ref="u-album__row"
+            v-for="(arr, index) in showUrls"
+            :forComputedUse="albumWidth"
+            :key="index"
+        >
+            <view
+                class="u-album__row__wrapper"
+                v-for="(item, index1) in arr"
+                :key="index1"
+                :style="[imageStyle(index + 1, index1 + 1)]"
+                @tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
+            >
+                <image
+                    :src="getSrc(item)"
+                    :mode="
+                        urls.length === 1
+                            ? imageHeight > 0
+                                ? singleMode
+                                : 'widthFix'
+                            : multipleMode
+                    "
+                    :style="[
+                        {
+                            width: imageWidth,
+                            height: imageHeight
+                        }
+                    ]"
+                ></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"
+                >
+                    <u--text
+                        :text="`+${urls.length - maxCount}`"
+                        color="#fff"
+                        :size="multipleSize * 0.3"
+                        align="center"
+                        customStyle="justify-content: center"
+                    ></u--text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import props from './props.js'
+// #ifdef APP-NVUE
+// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+/**
+ * Album 相册
+ * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
+ * @tutorial https://www.uviewui.com/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 )
+ *
+ * @event    {Function}        albumWidth       某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送  (回调参数 width )
+ * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
+ */
+export default {
+    name: 'u-album',
+    mixins: [uni.$u.mpMixin, uni.$u.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,
+                    { addUnit, addStyle } = uni.$u,
+                    rowLen = this.showUrls.length,
+                    allLen = this.urls.length
+                const style = {
+                    marginRight: addUnit(space),
+                    marginBottom: addUnit(space)
+                }
+                // 如果为最后一行,则每个图片都无需下边框
+                if (index1 === rowLen) style.marginBottom = 0
+                // 每行的最右边一张和总长度的最后一张无需右边框
+                if (
+                    index2 === rowCount ||
+                    (index1 === rowLen &&
+                        index2 === this.showUrls[index1 - 1].length)
+                )
+                    style.marginRight = 0
+                return style
+            }
+        },
+        // 将数组划分为二维数组
+        showUrls() {
+            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 uni.$u.addUnit(
+                this.urls.length === 1 ? this.singleWidth : this.multipleSize
+            )
+        },
+        imageHeight() {
+            return uni.$u.addUnit(
+                this.urls.length === 1 ? this.singleHeight : this.multipleSize
+            )
+        },
+        // 此变量无实际用途,仅仅是为了利用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
+        }
+    },
+    methods: {
+        // 预览图片
+        onPreviewTap(url) {
+            const urls = this.urls.map((item) => {
+                return this.getSrc(item)
+            })
+            uni.previewImage({
+                current: url,
+                urls
+            })
+        },
+        // 获取图片的路径
+        getSrc(item) {
+            return uni.$u.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) => {
+                    // 判断图片横向还是竖向展示方式
+                    const isHorizotal = res.width >= res.height
+                    this.singleWidth = isHorizotal
+                        ? this.singleSize
+                        : (res.width / res.height) * this.singleSize
+                    this.singleHeight = !isHorizotal
+                        ? this.singleSize
+                        : (res.height / res.width) * this.singleWidth
+                },
+                fail: () => {
+                    this.getComponentWidth()
+                }
+            })
+        },
+        // 获取组件的宽度
+        async getComponentWidth() {
+            // 延时一定时间,以获取dom尺寸
+            await uni.$u.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>
+@import '../../libs/css/components.scss';
+
+.u-album {
+    @include flex(column);
+
+    &__row {
+        @include flex(row);
+        flex-wrap: wrap;
+
+        &__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>

+ 44 - 0
uni_modules/uview-ui/components/u-alert/props.js

@@ -0,0 +1,44 @@
+export default {
+    props: {
+        // 显示文字
+        title: {
+            type: String,
+            default: uni.$u.props.alert.title
+        },
+        // 主题,success/warning/info/error
+        type: {
+            type: String,
+            default: uni.$u.props.alert.type
+        },
+        // 辅助性文字
+        description: {
+            type: String,
+            default: uni.$u.props.alert.description
+        },
+        // 是否可关闭
+        closable: {
+            type: Boolean,
+            default: uni.$u.props.alert.closable
+        },
+        // 是否显示图标
+        showIcon: {
+            type: Boolean,
+            default: uni.$u.props.alert.showIcon
+        },
+        // 浅或深色调,light-浅色,dark-深色
+        effect: {
+            type: String,
+            default: uni.$u.props.alert.effect
+        },
+        // 文字是否居中
+        center: {
+            type: Boolean,
+            default: uni.$u.props.alert.center
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.alert.fontSize
+        }
+    }
+}

+ 243 - 0
uni_modules/uview-ui/components/u-alert/u-alert.vue

@@ -0,0 +1,243 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :show="show"
+	>
+		<view
+		    class="u-alert"
+		    :class="[`u-alert--${type}--${effect}`]"
+		    @tap.stop="clickHandler"
+		    :style="[$u.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: $u.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: $u.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.js';
+	/**
+	 * Alert  警告提示
+	 * @description 警告提示,展现需要关注的信息。
+	 * @tutorial https://www.uviewui.com/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       点击组件时触发
+	 * @example  <u-alert :title="title"  type = "warning" :closable="closable" :description = "description"></u-alert>
+	 */
+	export default {
+		name: 'u-alert',
+		mixins: [uni.$u.mpMixin, uni.$u.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';
+				}
+			}
+		},
+		methods: {
+			// 点击内容
+			clickHandler() {
+				this.$emit('click')
+			},
+			// 点击关闭按钮
+			closeHandler() {
+				this.show = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.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>

+ 52 - 0
uni_modules/uview-ui/components/u-avatar-group/props.js

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

+ 103 - 0
uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue

@@ -0,0 +1,103 @@
+<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 : $u.addUnit(-size * gap)
+			}"
+		>
+			<u-avatar
+			    :size="size"
+			    :shape="shape"
+			    :mode="mode"
+			    :src="$u.test.object(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"
+			>
+				<u--text
+				    color="#ffffff"
+				    :size="size * 0.4"
+				    :text="`+${extraValue || urls.length - showUrl.length}`"
+					align="center"
+					customStyle="justify-content: center"
+				></u--text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * AvatarGroup  头像组
+	 * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+	 * @tutorial https://www.uviewui.com/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: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			showUrl() {
+				return this.urls.slice(0, this.maxCount)
+			}
+		},
+		methods: {
+			clickHandler() {
+				this.$emit('showMore')
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.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>

+ 78 - 0
uni_modules/uview-ui/components/u-avatar/props.js

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

Dosya farkı çok büyük olduğundan ihmal edildi
+ 173 - 0
uni_modules/uview-ui/components/u-avatar/u-avatar.vue


+ 54 - 0
uni_modules/uview-ui/components/u-back-top/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 返回顶部的形状,circle-圆形,square-方形
+        mode: {
+            type: String,
+            default: uni.$u.props.backtop.mode
+        },
+        // 自定义图标
+        icon: {
+            type: String,
+            default: uni.$u.props.backtop.icon
+        },
+        // 提示文字
+        text: {
+            type: String,
+            default: uni.$u.props.backtop.text
+        },
+        // 返回顶部滚动时间
+        duration: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.duration
+        },
+        // 滚动距离
+        scrollTop: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.scrollTop
+        },
+        // 距离顶部多少距离显示,单位px
+        top: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.top
+        },
+        // 返回顶部按钮到底部的距离,单位px
+        bottom: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.bottom
+        },
+        // 返回顶部按钮到右边的距离,单位px
+        right: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.right
+        },
+        // 层级
+        zIndex: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.zIndex
+        },
+        // 图标的样式,对象形式
+        iconStyle: {
+            type: Object,
+            default: uni.$u.props.backtop.iconStyle
+        }
+    }
+}

+ 129 - 0
uni_modules/uview-ui/components/u-back-top/u-back-top.vue

@@ -0,0 +1,129 @@
+<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.js';
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom')
+	// #endif
+	/**
+	 * backTop 返回顶部
+	 * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+	 * @tutorial https://uviewui.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: [uni.$u.mpMixin, uni.$u.mixin,props],
+		computed: {
+			backTopStyle() {
+				// 动画组件样式
+				const style = {
+					bottom: uni.$u.addUnit(this.bottom),
+					right: uni.$u.addUnit(this.right),
+					width: '40px',
+					height: '40px',
+					position: 'fixed',
+					zIndex: 10,
+				}
+				return style
+			},
+			show() {
+				return uni.$u.getPx(this.scrollTop) > uni.$u.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 uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
+			}
+		},
+		methods: {
+			backToTop() {
+				// #ifdef APP-NVUE
+				if (!this.$parent.$refs['u-back-top']) {
+					uni.$u.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>
+	@import '../../libs/css/components.scss';
+     $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>

+ 72 - 0
uni_modules/uview-ui/components/u-badge/props.js

@@ -0,0 +1,72 @@
+export default {
+    props: {
+        // 是否显示圆点
+        isDot: {
+            type: Boolean,
+            default: uni.$u.props.badge.isDot
+        },
+        // 显示的内容
+        value: {
+            type: [Number, String],
+            default: uni.$u.props.badge.value
+        },
+        // 是否显示
+        show: {
+            type: Boolean,
+            default: uni.$u.props.badge.show
+        },
+        // 最大值,超过最大值会显示 '{max}+'
+        max: {
+            type: [Number, String],
+            default: uni.$u.props.badge.max
+        },
+        // 主题类型,error|warning|success|primary
+        type: {
+            type: String,
+            default: uni.$u.props.badge.type
+        },
+        // 当数值为 0 时,是否展示 Badge
+        showZero: {
+            type: Boolean,
+            default: uni.$u.props.badge.showZero
+        },
+        // 背景颜色,优先级比type高,如设置,type参数会失效
+        bgColor: {
+            type: [String, null],
+            default: uni.$u.props.badge.bgColor
+        },
+        // 字体颜色
+        color: {
+            type: [String, null],
+            default: uni.$u.props.badge.color
+        },
+        // 徽标形状,circle-四角均为圆角,horn-左下角为直角
+        shape: {
+            type: String,
+            default: uni.$u.props.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: uni.$u.props.badge.numberType
+        },
+        // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+        offset: {
+            type: Array,
+            default: uni.$u.props.badge.offset
+        },
+        // 是否反转背景和字体颜色
+        inverted: {
+            type: Boolean,
+            default: uni.$u.props.badge.inverted
+        },
+        // 是否绝对定位
+        absolute: {
+            type: Boolean,
+            default: uni.$u.props.badge.absolute
+        }
+    }
+}

+ 171 - 0
uni_modules/uview-ui/components/u-badge/u-badge.vue

@@ -0,0 +1,171 @@
+<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="[$u.addStyle(customStyle), badgeStyle]"
+		class="u-badge"
+	>{{ isDot ? '' :showValue }}</text>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * badge 徽标数
+	 * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+	 * @tutorial https://uviewui.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: [uni.$u.mpMixin, props, uni.$u.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 = uni.$u.addUnit(top)
+						style.right = uni.$u.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)
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	$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>

+ 46 - 0
uni_modules/uview-ui/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;
+	}
+}

+ 161 - 0
uni_modules/uview-ui/components/u-button/props.js

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

+ 495 - 0
uni_modules/uview-ui/components/u-button/u-button.vue

@@ -0,0 +1,495 @@
+<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, $u.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, $u.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>
+import button from "../../libs/mixin/button.js";
+import openType from "../../libs/mixin/openType.js";
+import props from "./props.js";
+/**
+ * button 按钮
+ * @description Button 按钮
+ * @tutorial https://www.uviewui.com/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: [uni.$u.mpMixin, uni.$u.mixin, button, openType, props],
+    // #endif
+    // #ifndef MP
+    mixins: [uni.$u.mpMixin, uni.$u.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
+                    : uni.$u.config.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;
+        },
+    },
+    methods: {
+        clickHandler() {
+            // 非禁止并且非加载中,才能点击
+            if (!this.disabled && !this.loading) {
+				// 进行节流控制,每this.throttle毫秒内,只在开始处执行
+				uni.$u.throttle(() => {
+					this.$emit("click");
+				}, this.throttleTime);
+            }
+        },
+        // 下面为对接uniapp官方按钮开放能力事件回调的对接
+        getphonenumber(res) {
+            this.$emit("getphonenumber", res);
+        },
+        getuserinfo(res) {
+            this.$emit("getuserinfo", res);
+        },
+        error(res) {
+            this.$emit("error", res);
+        },
+        opensetting(res) {
+            this.$emit("opensetting", res);
+        },
+        launchapp(res) {
+            this.$emit("launchapp", res);
+        },
+        agreeprivacyauthorization(res) {
+            this.$emit("agreeprivacyauthorization", res);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../libs/css/components.scss";
+
+/* #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>

+ 80 - 0
uni_modules/uview-ui/components/u-button/vue.scss

@@ -0,0 +1,80 @@
+// 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-error;
+
+.u-button {
+	width: 100%;
+	
+	&__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;
+		}
+	}
+}

+ 99 - 0
uni_modules/uview-ui/components/u-calendar/header.vue

@@ -0,0 +1,99 @@
+<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">一</text>
+			<text class="u-calendar-header__weekdays__weekday">二</text>
+			<text class="u-calendar-header__weekdays__weekday">三</text>
+			<text class="u-calendar-header__weekdays__weekday">四</text>
+			<text class="u-calendar-header__weekdays__weekday">五</text>
+			<text class="u-calendar-header__weekdays__weekday">六</text>
+			<text class="u-calendar-header__weekdays__weekday">日</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'u-calendar-header',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin],
+		props: {
+			// 标题
+			title: {
+				type: String,
+				default: ''
+			},
+			// 副标题
+			subtitle: {
+				type: String,
+				default: ''
+			},
+			// 是否显示标题
+			showTitle: {
+				type: Boolean,
+				default: true
+			},
+			// 是否显示副标题
+			showSubtitle: {
+				type: Boolean,
+				default: true
+			},
+		},
+		data() {
+			return {
+
+			}
+		},
+		methods: {
+			name() {
+
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-calendar-header {
+		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>

+ 579 - 0
uni_modules/uview-ui/components/u-calendar/month.vue

@@ -0,0 +1,579 @@
+<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 && '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 && '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 dayjs from '../../libs/util/dayjs.js';
+	export default {
+		name: 'u-calendar-month',
+		mixins: [uni.$u.mpMixin, uni.$u.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: uni.$u.props.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
+			}
+		},
+		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 = uni.$u.addUnit(dayWidth)
+					// #endif
+					style.height = uni.$u.addUnit(this.rowHeight)
+					if (index2 === 0) {
+						// 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
+						week = (week === 0 ? 7 : week) - 1
+						style.marginLeft = uni.$u.addUnit(week * dayWidth)
+					}
+					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 = uni.$u.colorGradient(this.color, '#ffffff', 100)[90]
+								// 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符
+								style.opacity = 0.7
+							}
+						} else if (this.selected.length === 1) {
+							// 之所以需要这么写,是因为DCloud公司的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()
+		},
+		methods: {
+			init() {
+				// 初始化默认选中
+				this.$emit('monthSelected', this.selected)
+				this.$nextTick(() => {
+					// 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度
+					// 因为nvue下,$nextTick并不是100%可靠的
+					uni.$u.sleep(10).then(() => {
+						this.getWrapperWidth()
+						this.getMonthRect()
+					})
+				})
+			},
+			// 判断两个日期是否相等
+			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://www.uviewui.com/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
+				// 对上一次选择的日期数组进行深度克隆
+				let selected = uni.$u.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) {
+									uni.$u.toast(this.rangePrompt)
+								} else {
+									uni.$u.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 (!uni.$u.test.array(this.defaultDate)) {
+						defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
+					} else {
+						defaultDate = [this.defaultDate[0]]
+					}
+				} else {
+					// 如果为非数组,则不执行
+					if (!uni.$u.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)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-calendar-month-wrapper {
+		margin-top: 4px;
+	}
+
+	.u-calendar-month {
+
+		&__title {
+			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>

+ 144 - 0
uni_modules/uview-ui/components/u-calendar/props.js

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

+ 384 - 0
uni_modules/uview-ui/components/u-calendar/u-calendar.vue

@@ -0,0 +1,384 @@
+<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"
+			></uHeader>
+			<scroll-view
+				:style="{
+                    height: $u.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"
+					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 '../../libs/util/dayjs.js'
+import Calendar from '../../libs/util/calendar.js'
+/**
+ * Calendar 日历
+ * @description  此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中.
+ * @tutorial https://www.uviewui.com/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 )
+ *
+ * @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: [uni.$u.mpMixin, uni.$u.mixin, props],
+	components: {
+		uHeader,
+		uMonth
+	},
+	data() {
+		return {
+			// 需要显示的月份的数组
+			months: [],
+			// 在月份滚动区域中,当前视图中月份的index索引
+			monthIndex: 0,
+			// 月份滚动区域的高度
+			listHeight: 0,
+			// month组件中选择的日期数组
+			selected: [],
+			scrollIntoView: '',
+			scrollTop:0,
+			// 过滤处理方法
+			innerFormatter: (value) => value
+		}
+	},
+	watch: {
+		selectedChange: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		},
+		// 打开弹窗时,设置月份数据
+		show: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		}
+	},
+	computed: {
+		// 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理
+		innerMaxDate() {
+			return uni.$u.test.number(this.maxDate)
+				? Number(this.maxDate)
+				: this.maxDate
+		},
+		innerMinDate() {
+			return uni.$u.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()
+	},
+	methods: {
+		// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
+		setFormatter(e) {
+			this.innerFormatter = e
+		},
+		// month组件内部选择日期后,通过事件通知给父组件
+		monthSelected(e) {
+			this.selected = e
+			if (!this.showConfirm) {
+				// 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还
+				if (
+					this.mode === 'multiple' ||
+					this.mode === 'single' ||
+					(this.mode === 'range' && this.selected.length >= 2)
+				) {
+					this.$emit('confirm', this.selected)
+				}
+			}
+		},
+		init() {
+			// 校验maxDate,不能小于minDate
+			if (
+				this.innerMaxDate &&
+				this.innerMinDate &&
+				new Date(this.innerMaxDate).getTime() < new Date(this.innerMinDate).getTime()
+			) {
+				return uni.$u.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 = uni.$u.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 = uni.$u.padZero(month)
+				return `${year}-${month}` === selected
+			})
+			if (_index !== -1) {
+				// #ifndef MP-WEIXIN
+				this.$nextTick(() => {
+					this.scrollIntoView = `month-${_index}`
+				})
+				// #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
+				}
+			}
+		},
+		// 更新月份的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 (!uni.$u.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>
+@import '../../libs/css/components.scss';
+
+.u-calendar {
+	&__confirm {
+		padding: 7px 18px;
+	}
+}
+</style>

+ 85 - 0
uni_modules/uview-ui/components/u-calendar/util.js

@@ -0,0 +1,85 @@
+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
+                        }
+                    })
+                )
+            }
+        }
+    }
+}

+ 14 - 0
uni_modules/uview-ui/components/u-car-keyboard/props.js

@@ -0,0 +1,14 @@
+export default {
+    props: {
+        // 是否打乱键盘按键的顺序
+        random: {
+            type: Boolean,
+            default: false
+        },
+        // 输入一个中文后,是否自动切换到英文
+        autoChange: {
+            type: Boolean,
+            default: false
+        }
+    }
+}

+ 311 - 0
uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue

@@ -0,0 +1,311 @@
+<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.js';
+	/**
+	 * keyboard 键盘组件
+	 * @description 此为uView自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。
+	 * @tutorial https://uviewui.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-keyboard",
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称
+				abc: false
+			};
+		},
+		computed: {
+			areaList() {
+				let data = [
+					'京',
+					'沪',
+					'粤',
+					'津',
+					'冀',
+					'豫',
+					'云',
+					'辽',
+					'黑',
+					'湘',
+					'皖',
+					'鲁',
+					'苏',
+					'浙',
+					'赣',
+					'鄂',
+					'桂',
+					'甘',
+					'晋',
+					'陕',
+					'蒙',
+					'吉',
+					'闽',
+					'贵',
+					'渝',
+					'川',
+					'青',
+					'琼',
+					'宁',
+					'挂',
+					'藏',
+					'港',
+					'澳',
+					'新',
+					'使',
+					'学'
+				];
+				let tmp = [];
+				// 打乱顺序
+				if (this.random) data = uni.$u.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 = uni.$u.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;
+			}
+		},
+		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) uni.$u.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>
+	@import "../../libs/css/components.scss";
+	$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>

+ 14 - 0
uni_modules/uview-ui/components/u-cell-group/props.js

@@ -0,0 +1,14 @@
+export default {
+    props: {
+        // 分组标题
+        title: {
+            type: String,
+            default: uni.$u.props.cellGroup.title
+        },
+        // 是否显示外边框
+        border: {
+            type: Boolean,
+            default: uni.$u.props.cellGroup.border
+        }
+    }
+}

+ 61 - 0
uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue

@@ -0,0 +1,61 @@
+<template>
+    <view :style="[$u.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.js';
+	/**
+	 * cellGroup  单元格
+	 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。
+	 * @tutorial https://uviewui.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: [uni.$u.mpMixin, uni.$u.mixin,props],
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	
+	$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>
+

+ 110 - 0
uni_modules/uview-ui/components/u-cell/props.js

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

+ 0 - 0
uni_modules/uview-ui/components/u-cell/u-cell.vue


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor