小欧追 1 rok temu
commit
4bceee9df6
100 zmienionych plików z 15130 dodań i 0 usunięć
  1. 4 0
      .gitignore
  2. 38 0
      App.vue
  3. 3 0
      README.md
  4. 9 0
      common/timu.js
  5. 145 0
      components/tabBar.vue
  6. 1941 0
      https/data.js
  7. 41 0
      https/http.js
  8. 48 0
      https/request.js
  9. 3 0
      https/url.js
  10. 59 0
      main.js
  11. 138 0
      manifest.json
  12. 146 0
      node_modules/promise-polyfill/CHANGELOG.md
  13. 20 0
      node_modules/promise-polyfill/LICENSE
  14. 121 0
      node_modules/promise-polyfill/README.md
  15. 266 0
      node_modules/promise-polyfill/dist/polyfill.js
  16. 0 0
      node_modules/promise-polyfill/dist/polyfill.min.js
  17. 248 0
      node_modules/promise-polyfill/dist/promise.js
  18. 0 0
      node_modules/promise-polyfill/dist/promise.min.js
  19. 242 0
      node_modules/promise-polyfill/lib/index.js
  20. 260 0
      node_modules/promise-polyfill/lib/polyfill.js
  21. 90 0
      node_modules/promise-polyfill/package.json
  22. 240 0
      node_modules/promise-polyfill/src/index.js
  23. 21 0
      node_modules/promise-polyfill/src/polyfill.js
  24. 16 0
      package-lock.json
  25. 24 0
      package.json
  26. 107 0
      pages.json
  27. 1096 0
      pages/doctor/upload.vue
  28. 582 0
      pages/doctor/uploadExpert.vue
  29. 70 0
      pages/doctor/words.vue
  30. 371 0
      pages/index/calendar.vue
  31. 269 0
      pages/index/calendarExpert.vue
  32. 63 0
      pages/index/guide.vue
  33. 81 0
      pages/index/index.vue
  34. 157 0
      pages/index/my.vue
  35. 1262 0
      pages/index/wenjuan.vue
  36. 50 0
      pages/login/agreement.vue
  37. 264 0
      pages/login/forget.vue
  38. 207 0
      pages/login/login.vue
  39. 329 0
      pages/login/perfect.vue
  40. 259 0
      pages/user/password.vue
  41. BIN
      static/img/Frame@2x(6).png
  42. BIN
      static/img/Frame@2x(7).png
  43. BIN
      static/img/P.png
  44. BIN
      static/img/V.png
  45. BIN
      static/img/bg@2x.png
  46. BIN
      static/img/bingli.png
  47. BIN
      static/img/calendar.png
  48. BIN
      static/img/calendar_acitve.png
  49. BIN
      static/img/group.png
  50. BIN
      static/img/guide.png
  51. BIN
      static/img/guide_active.png
  52. BIN
      static/img/index.png
  53. BIN
      static/img/index_active.png
  54. BIN
      static/img/jia.png
  55. BIN
      static/img/li1.png
  56. BIN
      static/img/li2.png
  57. BIN
      static/img/li3.png
  58. BIN
      static/img/li4.png
  59. BIN
      static/img/li5.png
  60. BIN
      static/img/li6.png
  61. BIN
      static/img/li7.png
  62. BIN
      static/img/li8.png
  63. BIN
      static/img/my.png
  64. BIN
      static/img/my1.png
  65. BIN
      static/img/my2.png
  66. BIN
      static/img/myBGC.png
  67. BIN
      static/img/my_acitve.png
  68. BIN
      static/img/no.png
  69. BIN
      static/img/null.png
  70. BIN
      static/img/r.png
  71. BIN
      static/img/right.png
  72. BIN
      static/img/suc.png
  73. BIN
      static/img/up.png
  74. BIN
      static/img/upButton.png
  75. BIN
      static/img/upData_up.png
  76. BIN
      static/img/up_r.png
  77. BIN
      static/img/wenjuan.png
  78. BIN
      static/img/下载.jfif
  79. 7 0
      uni.scss
  80. 21 0
      uview-ui/LICENSE
  81. 106 0
      uview-ui/README.md
  82. 190 0
      uview-ui/components/u-action-sheet/u-action-sheet.vue
  83. 256 0
      uview-ui/components/u-alert-tips/u-alert-tips.vue
  84. 290 0
      uview-ui/components/u-avatar-cropper/u-avatar-cropper.vue
  85. 1265 0
      uview-ui/components/u-avatar-cropper/weCropper.js
  86. 24 0
      uview-ui/components/u-avatar/u-avatar.vue
  87. 153 0
      uview-ui/components/u-back-top/u-back-top.vue
  88. 216 0
      uview-ui/components/u-badge/u-badge.vue
  89. 596 0
      uview-ui/components/u-button/u-button.vue
  90. 639 0
      uview-ui/components/u-calendar/u-calendar.vue
  91. 257 0
      uview-ui/components/u-car-keyboard/u-car-keyboard.vue
  92. 299 0
      uview-ui/components/u-card/u-card.vue
  93. 70 0
      uview-ui/components/u-cell-group/u-cell-group.vue
  94. 316 0
      uview-ui/components/u-cell-item/u-cell-item.vue
  95. 123 0
      uview-ui/components/u-checkbox-group/u-checkbox-group.vue
  96. 284 0
      uview-ui/components/u-checkbox/u-checkbox.vue
  97. 220 0
      uview-ui/components/u-circle-progress/u-circle-progress.vue
  98. 147 0
      uview-ui/components/u-circle-progress/u-line-progress/u-line-progress.vue
  99. 156 0
      uview-ui/components/u-col/u-col.vue
  100. 205 0
      uview-ui/components/u-collapse-item/u-collapse-item.vue

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+.hbuilderx
+.idea
+unpackage/
+.DS_Store

+ 38 - 0
App.vue

@@ -0,0 +1,38 @@
+<script>
+	export default {
+		
+	}
+</script>
+
+<style lang="scss">
+	@import "uview-ui/index.scss";
+	page{
+		background-color: #F8F8F8;
+	}
+	// 公共输入框
+	.input{
+		padding: 28rpx 0;
+		border-bottom: 1px solid rgba(0,0,0,0.08);
+		input{
+			font-size: 28rpx;
+			color: rgba(0,0,0,0.8);
+		}
+		.icon{
+			font-size: 28rpx;
+			color: #000;
+		}
+	}
+	.inputBorNode{
+		border-bottom: 1px solid transparent;
+	}
+	.sms{
+		justify-content: space-between;
+		color: $color;
+		font-size: 28rpx;
+	}
+	.flex{
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+	}
+</style>

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+# calendar
+
+病例微信小程序

+ 9 - 0
common/timu.js

@@ -0,0 +1,9 @@
+function getNumber(num) {
+	var arr = Array.from(Array(num).keys(), n => n + 1);
+	arr.unshift(0)
+	return arr
+}
+
+export default {
+	getNumber
+}

+ 145 - 0
components/tabBar.vue

@@ -0,0 +1,145 @@
+<template>
+	<view class="uni-tabbar">
+		<view class="uni-tabbar__item" v-for="(item,index) in tabbar" :key="index" @tap="changeTab(item)">
+			<view class="uni-tabbar__bd">
+				<view class="uni-tabbar__icon">
+					<image v-if="item.pagePath == pagePath" class="icon-img" mode="aspectFit" :src="item.selectedIconPath"></image>
+					<image v-else class="icon-img" mode="aspectFit" :src="item.iconPath"></image>
+				</view>
+			</view>
+			<view class="uni-tabbar__label" :class="{'active': item.pagePath == pagePath}">
+				{{item.text}}
+			</view>
+		</view>
+	</view>
+
+</template>
+
+<script>
+	export default {
+		props: {
+			pagePath: String
+		},
+		data() {
+			return {
+				page: 'contact',
+				showPage: false,
+				containerHeight: 400,
+				tabbar: [
+					//动态切换的菜单,先隐藏,动态插入
+					{
+						pagePath: "/pages/index/index",
+						iconPath: "/static/img/index_active.png",
+						selectedIconPath: "/static/img/index.png",
+						text: "活动详情"
+					},
+					{
+						pagePath: "/pages/index/guide",
+						iconPath: "/static/img/guide_active.png",
+						selectedIconPath: "/static/img/guide.png",
+						text: "操作指南"
+					},
+					{
+						pagePath: "/pages/index/calendar",
+						iconPath: "/static/img/calendar_acitve.png",
+						selectedIconPath: "/static/img/calendar.png",
+						text: " "
+					},
+					{
+						pagePath: "/pages/index/my",
+						iconPath: "/static/img/my_acitve.png",
+						selectedIconPath: "/static/img/my.png",
+						text: "个人中心"
+					}
+				]
+			};
+		},
+		watch: {
+			pagePath: {
+				handler(val) {
+					console.log('pagePath监听===val', val)
+				},
+				immediate: true
+			}
+		},
+		mounted() {
+			if(uni.getStorageSync('doctorAndSpecialist')==2){
+				this.tabbar.splice(2, 1, 
+				{
+					pagePath: "/pages/index/calendarExpert",
+					iconPath: "/static/img/calendar_acitve.png",
+					selectedIconPath: "/static/img/calendar.png",
+					text: "病历审核"
+				})
+			}else{
+				this.tabbar.splice(2, 1,
+				{
+					pagePath: "/pages/index/calendar",
+					iconPath: "/static/img/calendar_acitve.png",
+					selectedIconPath: "/static/img/calendar.png",
+					text: "数据中心"
+				},)
+			}
+		},
+		methods: {
+			changeTab(item) {
+				this.page = item.pagePath;
+				// 使用reLaunch关闭所有的页面,打开新的栏目页面
+				uni.reLaunch({
+					url: this.page
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-tabbar {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 999;
+		width: 100%;
+		display: flex;
+		justify-content: space-around;
+		padding: 16rpx 0;
+		box-sizing: border-box;
+		background-color: #fff;
+		// box-shadow: 0px 0px 17rpx 1rpx rgba(206, 206, 206, 0.32);
+		padding-bottom: 20rpx;
+
+		.uni-tabbar__item {
+			display: block;
+			line-height: 24rpx;
+			font-size: 20rpx;
+			text-align: center;
+			width: 25%;
+		}
+
+		.uni-tabbar__icon {
+			height: 24px;
+			line-height: 24px;
+			text-align: center;
+		}
+
+		.icon {
+			display: inline-block;
+		}
+
+		.uni-tabbar__label {
+			margin-top: 14rpx;
+			line-height: 24rpx;
+			font-size: 24rpx;
+			color: #999;
+
+			&.active {
+				color: #1ca6ec;
+			}
+		}
+
+		.icon-img {
+			height: 24px;
+			width: 24px;
+		}
+	}
+</style>

+ 1941 - 0
https/data.js

@@ -0,0 +1,1941 @@
+const data = [
+    {
+      "label":"北京",
+      "level":"1",
+      "code":"1100",
+      "value":[
+        {
+          "label":"北京",
+          "level":"1",
+          "code":"1100"
+        }
+      ]
+    },
+    {
+      "label":"天津",
+      "level":"1",
+      "code":"1200",
+      "value":[
+        {
+          "label":"天津",
+          "level":"1",
+          "code":"1200"
+        }
+      ]
+    },
+    {
+      "label":"河北",
+      "level":"1",
+      "code":"1300",
+      "value":[
+        {
+          "label":"石家庄",
+          "level":"2",
+          "code":"1301"
+        },
+        {
+          "label":"唐山",
+          "level":"3",
+          "code":"1302"
+        },
+        {
+          "label":"秦皇岛",
+          "level":"3",
+          "code":"1303"
+        },
+        {
+          "label":"邯郸",
+          "level":"3",
+          "code":"1304"
+        },
+        {
+          "label":"邢台",
+          "level":"3",
+          "code":"1305"
+        },
+        {
+          "label":"保定",
+          "level":"3",
+          "code":"1306"
+        },
+        {
+          "label":"张家口",
+          "level":"3",
+          "code":"1307"
+        },
+        {
+          "label":"承德",
+          "level":"3",
+          "code":"1308"
+        },
+        {
+          "label":"沧州",
+          "level":"3",
+          "code":"1309"
+        },
+        {
+          "label":"廊坊",
+          "level":"3",
+          "code":"1310"
+        },
+        {
+          "label":"衡水",
+          "level":"3",
+          "code":"1311"
+        }
+      ]
+    },
+    {
+      "label":"山西",
+      "level":"1",
+      "code":"1400",
+      "value":[
+        {
+          "label":"太原",
+          "level":"2",
+          "code":"1401"
+        },
+        {
+          "label":"大同",
+          "level":"3",
+          "code":"1402"
+        },
+        {
+          "label":"阳泉",
+          "level":"3",
+          "code":"1403"
+        },
+        {
+          "label":"长治",
+          "level":"3",
+          "code":"1404"
+        },
+        {
+          "label":"晋城",
+          "level":"3",
+          "code":"1405"
+        },
+        {
+          "label":"朔州",
+          "level":"3",
+          "code":"1406"
+        },
+        {
+          "label":"晋中",
+          "level":"3",
+          "code":"1407"
+        },
+        {
+          "label":"运城",
+          "level":"3",
+          "code":"1408"
+        },
+        {
+          "label":"忻州",
+          "level":"3",
+          "code":"1409"
+        },
+        {
+          "label":"临汾",
+          "level":"3",
+          "code":"1410"
+        },
+        {
+          "label":"吕梁",
+          "level":"3",
+          "code":"1411"
+        }
+      ]
+    },
+    {
+      "label":"内蒙古自治区",
+      "level":"1",
+      "code":"1500",
+      "value":[
+        {
+          "label":"呼和浩特",
+          "level":"2",
+          "code":"1501"
+        },
+        {
+          "label":"包头",
+          "level":"3",
+          "code":"1502"
+        },
+        {
+          "label":"乌海",
+          "level":"3",
+          "code":"1503"
+        },
+        {
+          "label":"赤峰",
+          "level":"3",
+          "code":"1504"
+        },
+        {
+          "label":"通辽",
+          "level":"3",
+          "code":"1505"
+        },
+        {
+          "label":"鄂尔多斯",
+          "level":"3",
+          "code":"1506"
+        },
+        {
+          "label":"呼伦贝尔",
+          "level":"3",
+          "code":"1507"
+        },
+        {
+          "label":"巴彦淖尔",
+          "level":"3",
+          "code":"1508"
+        },
+        {
+          "label":"乌兰察布",
+          "level":"3",
+          "code":"1509"
+        },
+        {
+          "label":"兴安盟",
+          "level":"3",
+          "code":"1522"
+        },
+        {
+          "label":"锡林郭勒盟",
+          "level":"3",
+          "code":"1525"
+        },
+        {
+          "label":"阿拉善盟",
+          "level":"3",
+          "code":"1529"
+        }
+      ]
+    },
+    {
+      "label":"辽宁",
+      "level":"1",
+      "code":"2100",
+      "value":[
+        {
+          "label":"沈阳",
+          "level":"2",
+          "code":"2101"
+        },
+        {
+          "label":"大连",
+          "level":"3",
+          "code":"2102"
+        },
+        {
+          "label":"鞍山",
+          "level":"3",
+          "code":"2103"
+        },
+        {
+          "label":"抚顺",
+          "level":"3",
+          "code":"2104"
+        },
+        {
+          "label":"本溪",
+          "level":"3",
+          "code":"2105"
+        },
+        {
+          "label":"丹东",
+          "level":"3",
+          "code":"2106"
+        },
+        {
+          "label":"锦州",
+          "level":"3",
+          "code":"2107"
+        },
+        {
+          "label":"营口",
+          "level":"3",
+          "code":"2108"
+        },
+        {
+          "label":"阜新",
+          "level":"3",
+          "code":"2109"
+        },
+        {
+          "label":"辽阳",
+          "level":"3",
+          "code":"2110"
+        },
+        {
+          "label":"盘锦",
+          "level":"3",
+          "code":"2111"
+        },
+        {
+          "label":"铁岭",
+          "level":"3",
+          "code":"2112"
+        },
+        {
+          "label":"朝阳",
+          "level":"3",
+          "code":"2113"
+        },
+        {
+          "label":"葫芦岛",
+          "level":"3",
+          "code":"2114"
+        }
+      ]
+    },
+    {
+      "label":"吉林",
+      "level":"1",
+      "code":"2200",
+      "value":[
+        {
+          "label":"长春",
+          "level":"2",
+          "code":"2201"
+        },
+        {
+          "label":"吉林",
+          "level":"3",
+          "code":"2202"
+        },
+        {
+          "label":"四平",
+          "level":"3",
+          "code":"2203"
+        },
+        {
+          "label":"辽源",
+          "level":"3",
+          "code":"2204"
+        },
+        {
+          "label":"通化",
+          "level":"3",
+          "code":"2205"
+        },
+        {
+          "label":"白山",
+          "level":"3",
+          "code":"2206"
+        },
+        {
+          "label":"松原",
+          "level":"3",
+          "code":"2207"
+        },
+        {
+          "label":"白城",
+          "level":"3",
+          "code":"2208"
+        },
+        {
+          "label":"延边朝鲜族自治州",
+          "level":"3",
+          "code":"2224"
+        }
+      ]
+    },
+    {
+      "label":"黑龙江",
+      "level":"1",
+      "code":"2300",
+      "value":[
+        {
+          "label":"哈尔滨",
+          "level":"2",
+          "code":"2301"
+        },
+        {
+          "label":"齐齐哈尔",
+          "level":"3",
+          "code":"2302"
+        },
+        {
+          "label":"鸡西",
+          "level":"3",
+          "code":"2303"
+        },
+        {
+          "label":"鹤岗",
+          "level":"3",
+          "code":"2304"
+        },
+        {
+          "label":"双鸭山",
+          "level":"3",
+          "code":"2305"
+        },
+        {
+          "label":"大庆",
+          "level":"3",
+          "code":"2306"
+        },
+        {
+          "label":"伊春",
+          "level":"3",
+          "code":"2307"
+        },
+        {
+          "label":"佳木斯",
+          "level":"3",
+          "code":"2308"
+        },
+        {
+          "label":"七台河",
+          "level":"3",
+          "code":"2309"
+        },
+        {
+          "label":"牡丹江",
+          "level":"3",
+          "code":"2310"
+        },
+        {
+          "label":"黑河",
+          "level":"3",
+          "code":"2311"
+        },
+        {
+          "label":"绥化",
+          "level":"3",
+          "code":"2312"
+        },
+        {
+          "label":"大兴安岭地区",
+          "level":"3",
+          "code":"2327"
+        }
+      ]
+    },
+    {
+      "label":"上海",
+      "level":"1",
+      "code":"3100",
+      "value":[
+        {
+          "label":"上海",
+          "level":"1",
+          "code":"3100"
+        }
+      ]
+    },
+    {
+      "label":"江苏",
+      "level":"1",
+      "code":"3200",
+      "value":[
+        {
+          "label":"南京",
+          "level":"2",
+          "code":"3201"
+        },
+        {
+          "label":"无锡",
+          "level":"3",
+          "code":"3202"
+        },
+        {
+          "label":"徐州",
+          "level":"3",
+          "code":"3203"
+        },
+        {
+          "label":"常州",
+          "level":"3",
+          "code":"3204"
+        },
+        {
+          "label":"苏州",
+          "level":"3",
+          "code":"3205"
+        },
+        {
+          "label":"南通",
+          "level":"3",
+          "code":"3206"
+        },
+        {
+          "label":"连云港",
+          "level":"3",
+          "code":"3207"
+        },
+        {
+          "label":"淮安",
+          "level":"3",
+          "code":"3208"
+        },
+        {
+          "label":"盐城",
+          "level":"3",
+          "code":"3209"
+        },
+        {
+          "label":"扬州",
+          "level":"3",
+          "code":"3210"
+        },
+        {
+          "label":"镇江",
+          "level":"3",
+          "code":"3211"
+        },
+        {
+          "label":"泰州",
+          "level":"3",
+          "code":"3212"
+        },
+        {
+          "label":"宿迁",
+          "level":"3",
+          "code":"3213"
+        }
+      ]
+    },
+    {
+      "label":"浙江",
+      "level":"1",
+      "code":"3300",
+      "value":[
+        {
+          "label":"杭州",
+          "level":"2",
+          "code":"3301"
+        },
+        {
+          "label":"宁波",
+          "level":"3",
+          "code":"3302"
+        },
+        {
+          "label":"温州",
+          "level":"3",
+          "code":"3303"
+        },
+        {
+          "label":"嘉兴",
+          "level":"3",
+          "code":"3304"
+        },
+        {
+          "label":"湖州",
+          "level":"3",
+          "code":"3305"
+        },
+        {
+          "label":"绍兴",
+          "level":"3",
+          "code":"3306"
+        },
+        {
+          "label":"金华",
+          "level":"3",
+          "code":"3307"
+        },
+        {
+          "label":"衢州",
+          "level":"3",
+          "code":"3308"
+        },
+        {
+          "label":"舟山",
+          "level":"3",
+          "code":"3309"
+        },
+        {
+          "label":"台州",
+          "level":"3",
+          "code":"3310"
+        },
+        {
+          "label":"丽水",
+          "level":"3",
+          "code":"3311"
+        }
+      ]
+    },
+    {
+      "label":"安徽",
+      "level":"1",
+      "code":"3400",
+      "value":[
+        {
+          "label":"合肥",
+          "level":"2",
+          "code":"3401"
+        },
+        {
+          "label":"芜湖",
+          "level":"3",
+          "code":"3402"
+        },
+        {
+          "label":"蚌埠",
+          "level":"3",
+          "code":"3403"
+        },
+        {
+          "label":"淮南",
+          "level":"3",
+          "code":"3404"
+        },
+        {
+          "label":"马鞍山",
+          "level":"3",
+          "code":"3405"
+        },
+        {
+          "label":"淮北",
+          "level":"3",
+          "code":"3406"
+        },
+        {
+          "label":"铜陵",
+          "level":"3",
+          "code":"3407"
+        },
+        {
+          "label":"安庆",
+          "level":"3",
+          "code":"3408"
+        },
+        {
+          "label":"黄山",
+          "level":"3",
+          "code":"3410"
+        },
+        {
+          "label":"滁州",
+          "level":"3",
+          "code":"3411"
+        },
+        {
+          "label":"阜阳",
+          "level":"3",
+          "code":"3412"
+        },
+        {
+          "label":"宿州",
+          "level":"3",
+          "code":"3413"
+        },
+        {
+          "label":"巢湖",
+          "level":"3",
+          "code":"3414"
+        },
+        {
+          "label":"六安",
+          "level":"3",
+          "code":"3415"
+        },
+        {
+          "label":"亳州",
+          "level":"3",
+          "code":"3416"
+        },
+        {
+          "label":"池州",
+          "level":"3",
+          "code":"3417"
+        },
+        {
+          "label":"宣城",
+          "level":"3",
+          "code":"3418"
+        }
+      ]
+    },
+    {
+      "label":"福建",
+      "level":"1",
+      "code":"3500",
+      "value":[
+        {
+          "label":"福州",
+          "level":"2",
+          "code":"3501"
+        },
+        {
+          "label":"厦门",
+          "level":"3",
+          "code":"3502"
+        },
+        {
+          "label":"莆田",
+          "level":"3",
+          "code":"3503"
+        },
+        {
+          "label":"三明",
+          "level":"3",
+          "code":"3504"
+        },
+        {
+          "label":"泉州",
+          "level":"3",
+          "code":"3505"
+        },
+        {
+          "label":"漳州",
+          "level":"3",
+          "code":"3506"
+        },
+        {
+          "label":"南平",
+          "level":"3",
+          "code":"3507"
+        },
+        {
+          "label":"龙岩",
+          "level":"3",
+          "code":"3508"
+        },
+        {
+          "label":"宁德",
+          "level":"3",
+          "code":"3509"
+        }
+      ]
+    },
+    {
+      "label":"江西",
+      "level":"1",
+      "code":"3600",
+      "value":[
+        {
+          "label":"南昌",
+          "level":"2",
+          "code":"3601"
+        },
+        {
+          "label":"景德镇",
+          "level":"3",
+          "code":"3602"
+        },
+        {
+          "label":"萍乡",
+          "level":"3",
+          "code":"3603"
+        },
+        {
+          "label":"九江",
+          "level":"3",
+          "code":"3604"
+        },
+        {
+          "label":"新余",
+          "level":"3",
+          "code":"3605"
+        },
+        {
+          "label":"鹰潭",
+          "level":"3",
+          "code":"3606"
+        },
+        {
+          "label":"赣州",
+          "level":"3",
+          "code":"3607"
+        },
+        {
+          "label":"吉安",
+          "level":"3",
+          "code":"3608"
+        },
+        {
+          "label":"宜春",
+          "level":"3",
+          "code":"3609"
+        },
+        {
+          "label":"抚州",
+          "level":"3",
+          "code":"3610"
+        },
+        {
+          "label":"上饶",
+          "level":"3",
+          "code":"3611"
+        }
+      ]
+    },
+    {
+      "label":"山东",
+      "level":"1",
+      "code":"3700",
+      "value":[
+        {
+          "label":"济南",
+          "level":"2",
+          "code":"3701"
+        },
+        {
+          "label":"青岛",
+          "level":"3",
+          "code":"3702"
+        },
+        {
+          "label":"淄博",
+          "level":"3",
+          "code":"3703"
+        },
+        {
+          "label":"枣庄",
+          "level":"3",
+          "code":"3704"
+        },
+        {
+          "label":"东营",
+          "level":"3",
+          "code":"3705"
+        },
+        {
+          "label":"烟台",
+          "level":"3",
+          "code":"3706"
+        },
+        {
+          "label":"潍坊",
+          "level":"3",
+          "code":"3707"
+        },
+        {
+          "label":"济宁",
+          "level":"3",
+          "code":"3708"
+        },
+        {
+          "label":"泰安",
+          "level":"3",
+          "code":"3709"
+        },
+        {
+          "label":"威海",
+          "level":"3",
+          "code":"3710"
+        },
+        {
+          "label":"日照",
+          "level":"3",
+          "code":"3711"
+        },
+        {
+          "label":"莱芜",
+          "level":"3",
+          "code":"3712"
+        },
+        {
+          "label":"临沂",
+          "level":"3",
+          "code":"3713"
+        },
+        {
+          "label":"德州",
+          "level":"3",
+          "code":"3714"
+        },
+        {
+          "label":"聊城",
+          "level":"3",
+          "code":"3715"
+        },
+        {
+          "label":"滨州",
+          "level":"3",
+          "code":"3716"
+        },
+        {
+          "label":"菏泽",
+          "level":"3",
+          "code":"3717"
+        }
+      ]
+    },
+    {
+      "label":"河南",
+      "level":"1",
+      "code":"4100",
+      "value":[
+        {
+          "label":"郑州",
+          "level":"2",
+          "code":"4101"
+        },
+        {
+          "label":"开封",
+          "level":"3",
+          "code":"4102"
+        },
+        {
+          "label":"洛阳",
+          "level":"3",
+          "code":"4103"
+        },
+        {
+          "label":"平顶山",
+          "level":"3",
+          "code":"4104"
+        },
+        {
+          "label":"安阳",
+          "level":"3",
+          "code":"4105"
+        },
+        {
+          "label":"鹤壁",
+          "level":"3",
+          "code":"4106"
+        },
+        {
+          "label":"新乡",
+          "level":"3",
+          "code":"4107"
+        },
+        {
+          "label":"焦作",
+          "level":"3",
+          "code":"4108"
+        },
+        {
+          "label":"濮阳",
+          "level":"3",
+          "code":"4109"
+        },
+        {
+          "label":"许昌",
+          "level":"3",
+          "code":"4110"
+        },
+        {
+          "label":"漯河",
+          "level":"3",
+          "code":"4111"
+        },
+        {
+          "label":"三门峡",
+          "level":"3",
+          "code":"4112"
+        },
+        {
+          "label":"南阳",
+          "level":"3",
+          "code":"4113"
+        },
+        {
+          "label":"商丘",
+          "level":"3",
+          "code":"4114"
+        },
+        {
+          "label":"信阳",
+          "level":"3",
+          "code":"4115"
+        },
+        {
+          "label":"周口",
+          "level":"3",
+          "code":"4116"
+        },
+        {
+          "label":"驻马店",
+          "level":"3",
+          "code":"4117"
+        }
+      ]
+    },
+    {
+      "label":"湖北",
+      "level":"1",
+      "code":"4200",
+      "value":[
+        {
+          "label":"武汉",
+          "level":"2",
+          "code":"4201"
+        },
+        {
+          "label":"黄石",
+          "level":"3",
+          "code":"4202"
+        },
+        {
+          "label":"十堰",
+          "level":"3",
+          "code":"4203"
+        },
+        {
+          "label":"宜昌",
+          "level":"3",
+          "code":"4205"
+        },
+        {
+          "label":"襄樊",
+          "level":"3",
+          "code":"4206"
+        },
+        {
+          "label":"鄂州",
+          "level":"3",
+          "code":"4207"
+        },
+        {
+          "label":"荆门",
+          "level":"3",
+          "code":"4208"
+        },
+        {
+          "label":"孝感",
+          "level":"3",
+          "code":"4209"
+        },
+        {
+          "label":"荆州",
+          "level":"3",
+          "code":"4210"
+        },
+        {
+          "label":"黄冈",
+          "level":"3",
+          "code":"4211"
+        },
+        {
+          "label":"咸宁",
+          "level":"3",
+          "code":"4212"
+        },
+        {
+          "label":"随州",
+          "level":"3",
+          "code":"4213"
+        },
+        {
+          "label":"恩施土家族苗族自治州",
+          "level":"3",
+          "code":"4228"
+        }
+      ]
+    },
+    {
+      "label":"湖南",
+      "level":"1",
+      "code":"4300",
+      "value":[
+        {
+          "label":"长沙",
+          "level":"2",
+          "code":"4301"
+        },
+        {
+          "label":"株洲",
+          "level":"3",
+          "code":"4302"
+        },
+        {
+          "label":"湘潭",
+          "level":"3",
+          "code":"4303"
+        },
+        {
+          "label":"衡阳",
+          "level":"3",
+          "code":"4304"
+        },
+        {
+          "label":"邵阳",
+          "level":"3",
+          "code":"4305"
+        },
+        {
+          "label":"岳阳",
+          "level":"3",
+          "code":"4306"
+        },
+        {
+          "label":"常德",
+          "level":"3",
+          "code":"4307"
+        },
+        {
+          "label":"张家界",
+          "level":"3",
+          "code":"4308"
+        },
+        {
+          "label":"益阳",
+          "level":"3",
+          "code":"4309"
+        },
+        {
+          "label":"郴州",
+          "level":"3",
+          "code":"4310"
+        },
+        {
+          "label":"永州",
+          "level":"3",
+          "code":"4311"
+        },
+        {
+          "label":"怀化",
+          "level":"3",
+          "code":"4312"
+        },
+        {
+          "label":"娄底",
+          "level":"3",
+          "code":"4313"
+        },
+        {
+          "label":"湘西土家族苗族自治州",
+          "level":"3",
+          "code":"4331"
+        }
+      ]
+    },
+    {
+      "label":"广东",
+      "level":"1",
+      "code":"4400",
+      "value":[
+        {
+          "label":"广州",
+          "level":"2",
+          "code":"4401"
+        },
+        {
+          "label":"韶关",
+          "level":"3",
+          "code":"4402"
+        },
+        {
+          "label":"深圳",
+          "level":"3",
+          "code":"4403"
+        },
+        {
+          "label":"珠海",
+          "level":"3",
+          "code":"4404"
+        },
+        {
+          "label":"汕头",
+          "level":"3",
+          "code":"4405"
+        },
+        {
+          "label":"佛山",
+          "level":"3",
+          "code":"4406"
+        },
+        {
+          "label":"江门",
+          "level":"3",
+          "code":"4407"
+        },
+        {
+          "label":"湛江",
+          "level":"3",
+          "code":"4408"
+        },
+        {
+          "label":"茂名",
+          "level":"3",
+          "code":"4409"
+        },
+        {
+          "label":"肇庆",
+          "level":"3",
+          "code":"4412"
+        },
+        {
+          "label":"惠州",
+          "level":"3",
+          "code":"4413"
+        },
+        {
+          "label":"梅州",
+          "level":"3",
+          "code":"4414"
+        },
+        {
+          "label":"汕尾",
+          "level":"3",
+          "code":"4415"
+        },
+        {
+          "label":"河源",
+          "level":"3",
+          "code":"4416"
+        },
+        {
+          "label":"阳江",
+          "level":"3",
+          "code":"4417"
+        },
+        {
+          "label":"清远",
+          "level":"3",
+          "code":"4418"
+        },
+        {
+          "label":"东莞",
+          "level":"3",
+          "code":"4419"
+        },
+        {
+          "label":"中山",
+          "level":"3",
+          "code":"4420"
+        },
+        {
+          "label":"潮州",
+          "level":"3",
+          "code":"4451"
+        },
+        {
+          "label":"揭阳",
+          "level":"3",
+          "code":"4452"
+        },
+        {
+          "label":"云浮",
+          "level":"3",
+          "code":"4453"
+        }
+      ]
+    },
+    {
+      "label":"广西壮族自治区",
+      "level":"1",
+      "code":"4500",
+      "value":[
+        {
+          "label":"南宁",
+          "level":"2",
+          "code":"4501"
+        },
+        {
+          "label":"柳州",
+          "level":"3",
+          "code":"4502"
+        },
+        {
+          "label":"桂林",
+          "level":"3",
+          "code":"4503"
+        },
+        {
+          "label":"梧州",
+          "level":"3",
+          "code":"4504"
+        },
+        {
+          "label":"北海",
+          "level":"3",
+          "code":"4505"
+        },
+        {
+          "label":"防城港",
+          "level":"3",
+          "code":"4506"
+        },
+        {
+          "label":"钦州",
+          "level":"3",
+          "code":"4507"
+        },
+        {
+          "label":"贵港",
+          "level":"3",
+          "code":"4508"
+        },
+        {
+          "label":"玉林",
+          "level":"3",
+          "code":"4509"
+        },
+        {
+          "label":"百色",
+          "level":"3",
+          "code":"4510"
+        },
+        {
+          "label":"贺州",
+          "level":"3",
+          "code":"4511"
+        },
+        {
+          "label":"河池",
+          "level":"3",
+          "code":"4512"
+        },
+        {
+          "label":"来宾",
+          "level":"3",
+          "code":"4513"
+        },
+        {
+          "label":"崇左",
+          "level":"3",
+          "code":"4514"
+        }
+      ]
+    },
+    {
+      "label":"海南",
+      "level":"1",
+      "code":"4600",
+      "value":[
+        {
+          "label":"海口",
+          "level":"2",
+          "code":"4601"
+        },
+        {
+          "label":"三亚",
+          "level":"3",
+          "code":"4602"
+        }
+      ]
+    },
+    {
+      "label":"重庆",
+      "level":"1",
+      "code":"5000",
+      "value":[
+        {
+          "label":"重庆",
+          "level":"1",
+          "code":"5000"
+        }
+      ]
+    },
+    {
+      "label":"四川",
+      "level":"1",
+      "code":"5100",
+      "value":[
+        {
+          "label":"成都",
+          "level":"2",
+          "code":"5101"
+        },
+        {
+          "label":"自贡",
+          "level":"3",
+          "code":"5103"
+        },
+        {
+          "label":"攀枝花",
+          "level":"3",
+          "code":"5104"
+        },
+        {
+          "label":"泸州",
+          "level":"3",
+          "code":"5105"
+        },
+        {
+          "label":"德阳",
+          "level":"3",
+          "code":"5106"
+        },
+        {
+          "label":"绵阳",
+          "level":"3",
+          "code":"5107"
+        },
+        {
+          "label":"广元",
+          "level":"3",
+          "code":"5108"
+        },
+        {
+          "label":"遂宁",
+          "level":"3",
+          "code":"5109"
+        },
+        {
+          "label":"内江",
+          "level":"3",
+          "code":"5110"
+        },
+        {
+          "label":"乐山",
+          "level":"3",
+          "code":"5111"
+        },
+        {
+          "label":"南充",
+          "level":"3",
+          "code":"5113"
+        },
+        {
+          "label":"眉山",
+          "level":"3",
+          "code":"5114"
+        },
+        {
+          "label":"宜宾",
+          "level":"3",
+          "code":"5115"
+        },
+        {
+          "label":"广安",
+          "level":"3",
+          "code":"5116"
+        },
+        {
+          "label":"达州",
+          "level":"3",
+          "code":"5117"
+        },
+        {
+          "label":"雅安",
+          "level":"3",
+          "code":"5118"
+        },
+        {
+          "label":"巴中",
+          "level":"3",
+          "code":"5119"
+        },
+        {
+          "label":"资阳",
+          "level":"3",
+          "code":"5120"
+        },
+        {
+          "label":"阿坝藏族羌族自治州",
+          "level":"3",
+          "code":"5132"
+        },
+        {
+          "label":"甘孜藏族自治州",
+          "level":"3",
+          "code":"5133"
+        },
+        {
+          "label":"凉山彝族自治州",
+          "level":"3",
+          "code":"5134"
+        }
+      ]
+    },
+    {
+      "label":"贵州",
+      "level":"1",
+      "code":"5200",
+      "value":[
+        {
+          "label":"贵阳",
+          "level":"2",
+          "code":"5201"
+        },
+        {
+          "label":"六盘水",
+          "level":"3",
+          "code":"5202"
+        },
+        {
+          "label":"遵义",
+          "level":"3",
+          "code":"5203"
+        },
+        {
+          "label":"安顺",
+          "level":"3",
+          "code":"5204"
+        },
+        {
+          "label":"铜仁地区",
+          "level":"3",
+          "code":"5222"
+        },
+        {
+          "label":"黔西南布依族苗族自治州",
+          "level":"3",
+          "code":"5223"
+        },
+        {
+          "label":"毕节地区",
+          "level":"3",
+          "code":"5224"
+        },
+        {
+          "label":"黔东南苗族侗族自治州",
+          "level":"3",
+          "code":"5226"
+        },
+        {
+          "label":"黔南布依族苗族自治州",
+          "level":"3",
+          "code":"5227"
+        }
+      ]
+    },
+    {
+      "label":"云南",
+      "level":"1",
+      "code":"5300",
+      "value":[
+        {
+          "label":"昆明",
+          "level":"2",
+          "code":"5301"
+        },
+        {
+          "label":"曲靖",
+          "level":"3",
+          "code":"5303"
+        },
+        {
+          "label":"玉溪",
+          "level":"3",
+          "code":"5304"
+        },
+        {
+          "label":"保山",
+          "level":"3",
+          "code":"5305"
+        },
+        {
+          "label":"昭通",
+          "level":"3",
+          "code":"5306"
+        },
+        {
+          "label":"丽江",
+          "level":"3",
+          "code":"5307"
+        },
+        {
+          "label":"普洱",
+          "level":"3",
+          "code":"5308"
+        },
+        {
+          "label":"临沧",
+          "level":"3",
+          "code":"5309"
+        },
+        {
+          "label":"楚雄彝族自治州",
+          "level":"3",
+          "code":"5323"
+        },
+        {
+          "label":"红河哈尼族彝族自治州",
+          "level":"3",
+          "code":"5325"
+        },
+        {
+          "label":"文山壮族苗族自治州",
+          "level":"3",
+          "code":"5326"
+        },
+        {
+          "label":"西双版纳傣族自治州",
+          "level":"3",
+          "code":"5328"
+        },
+        {
+          "label":"大理",
+          "level":"3",
+          "code":"5329"
+        },
+        {
+          "label":"德宏傣族景颇族自治州",
+          "level":"3",
+          "code":"5331"
+        },
+        {
+          "label":"怒江傈僳族自治州",
+          "level":"3",
+          "code":"5333"
+        },
+        {
+          "label":"迪庆藏族自治州",
+          "level":"3",
+          "code":"5334"
+        }
+      ]
+    },
+    {
+      "label":"西藏自治区",
+      "level":"1",
+      "code":"5400",
+      "value":[
+        {
+          "label":"拉萨",
+          "level":"2",
+          "code":"5401"
+        },
+        {
+          "label":"昌都地区",
+          "level":"3",
+          "code":"5421"
+        },
+        {
+          "label":"山南地区",
+          "level":"3",
+          "code":"5422"
+        },
+        {
+          "label":"日喀则地区",
+          "level":"3",
+          "code":"5423"
+        },
+        {
+          "label":"那曲地区",
+          "level":"3",
+          "code":"5424"
+        },
+        {
+          "label":"阿里地区",
+          "level":"3",
+          "code":"5425"
+        },
+        {
+          "label":"林芝地区",
+          "level":"3",
+          "code":"5426"
+        }
+      ]
+    },
+    {
+      "label":"陕西",
+      "level":"1",
+      "code":"6100",
+      "value":[
+        {
+          "label":"西安",
+          "level":"2",
+          "code":"6101"
+        },
+        {
+          "label":"铜川",
+          "level":"3",
+          "code":"6102"
+        },
+        {
+          "label":"宝鸡",
+          "level":"3",
+          "code":"6103"
+        },
+        {
+          "label":"咸阳",
+          "level":"3",
+          "code":"6104"
+        },
+        {
+          "label":"渭南",
+          "level":"3",
+          "code":"6105"
+        },
+        {
+          "label":"延安",
+          "level":"3",
+          "code":"6106"
+        },
+        {
+          "label":"汉中",
+          "level":"3",
+          "code":"6107"
+        },
+        {
+          "label":"榆林",
+          "level":"3",
+          "code":"6108"
+        },
+        {
+          "label":"安康",
+          "level":"3",
+          "code":"6109"
+        },
+        {
+          "label":"商洛",
+          "level":"3",
+          "code":"6110"
+        }
+      ]
+    },
+    {
+      "label":"甘肃",
+      "level":"1",
+      "code":"6200",
+      "value":[
+        {
+          "label":"兰州",
+          "level":"2",
+          "code":"6201"
+        },
+        {
+          "label":"嘉峪关",
+          "level":"3",
+          "code":"6202"
+        },
+        {
+          "label":"金昌",
+          "level":"3",
+          "code":"6203"
+        },
+        {
+          "label":"白银",
+          "level":"3",
+          "code":"6204"
+        },
+        {
+          "label":"天水",
+          "level":"3",
+          "code":"6205"
+        },
+        {
+          "label":"武威",
+          "level":"3",
+          "code":"6206"
+        },
+        {
+          "label":"张掖",
+          "level":"3",
+          "code":"6207"
+        },
+        {
+          "label":"平凉",
+          "level":"3",
+          "code":"6208"
+        },
+        {
+          "label":"酒泉",
+          "level":"3",
+          "code":"6209"
+        },
+        {
+          "label":"庆阳",
+          "level":"3",
+          "code":"6210"
+        },
+        {
+          "label":"定西",
+          "level":"3",
+          "code":"6211"
+        },
+        {
+          "label":"陇南",
+          "level":"3",
+          "code":"6212"
+        },
+        {
+          "label":"临夏回族自治州",
+          "level":"3",
+          "code":"6229"
+        },
+        {
+          "label":"甘南藏族自治州",
+          "level":"3",
+          "code":"6230"
+        }
+      ]
+    },
+    {
+      "label":"青海",
+      "level":"1",
+      "code":"6300",
+      "value":[
+        {
+          "label":"西宁",
+          "level":"2",
+          "code":"6301"
+        },
+        {
+          "label":"海东地区",
+          "level":"3",
+          "code":"6321"
+        },
+        {
+          "label":"海北藏族自治州",
+          "level":"3",
+          "code":"6322"
+        },
+        {
+          "label":"黄南藏族自治州",
+          "level":"3",
+          "code":"6323"
+        },
+        {
+          "label":"海南藏族自治州",
+          "level":"3",
+          "code":"6325"
+        },
+        {
+          "label":"果洛藏族自治州",
+          "level":"3",
+          "code":"6326"
+        },
+        {
+          "label":"玉树藏族自治州",
+          "level":"3",
+          "code":"6327"
+        },
+        {
+          "label":"海西蒙古族藏族自治州",
+          "level":"3",
+          "code":"6328"
+        }
+      ]
+    },
+    {
+      "label":"宁夏回族自治区",
+      "level":"1",
+      "code":"6400",
+      "value":[
+        {
+          "label":"银川",
+          "level":"2",
+          "code":"6401"
+        },
+        {
+          "label":"石嘴山",
+          "level":"3",
+          "code":"6402"
+        },
+        {
+          "label":"吴忠",
+          "level":"3",
+          "code":"6403"
+        },
+        {
+          "label":"固原",
+          "level":"3",
+          "code":"6404"
+        },
+        {
+          "label":"中卫",
+          "level":"3",
+          "code":"6405"
+        }
+      ]
+    },
+    {
+      "label":"新疆维吾尔自治区",
+      "level":"1",
+      "code":"6500",
+      "value":[
+        {
+          "label":"乌鲁木齐",
+          "level":"2",
+          "code":"6501"
+        },
+        {
+          "label":"克拉玛依",
+          "level":"3",
+          "code":"6502"
+        },
+        {
+          "label":"吐鲁番地区",
+          "level":"3",
+          "code":"6521"
+        },
+        {
+          "label":"哈密地区",
+          "level":"3",
+          "code":"6522"
+        },
+        {
+          "label":"昌吉回族自治州",
+          "level":"3",
+          "code":"6523"
+        },
+        {
+          "label":"博尔塔拉蒙古自治州",
+          "level":"3",
+          "code":"6527"
+        },
+        {
+          "label":"巴音郭楞蒙古自治州",
+          "level":"3",
+          "code":"6528"
+        },
+        {
+          "label":"阿克苏地区",
+          "level":"3",
+          "code":"6529"
+        },
+        {
+          "label":"克孜勒苏柯尔克孜自治州",
+          "level":"3",
+          "code":"6530"
+        },
+        {
+          "label":"喀什地区",
+          "level":"3",
+          "code":"6531"
+        },
+        {
+          "label":"和田地区",
+          "level":"3",
+          "code":"6532"
+        },
+        {
+          "label":"伊犁哈萨克自治州",
+          "level":"3",
+          "code":"6540"
+        },
+        {
+          "label":"塔城地区",
+          "level":"3",
+          "code":"6542"
+        },
+        {
+          "label":"阿勒泰地区",
+          "level":"3",
+          "code":"6543"
+        }
+      ]
+    },
+    {
+      "label":"台湾",
+      "level":"1",
+      "code":"7100",
+      "value":[
+        {
+          "label":"台湾",
+          "level":"1",
+          "code":"7100"
+        }
+      ]
+    },
+    {
+      "label":"香港特别行政区",
+      "level":"1",
+      "code":"8100",
+      "value":[
+        {
+          "label":"香港特别行政区",
+          "level":"1",
+          "code":"8100"
+        }
+      ]
+    },
+    {
+      "label":"澳门特别行政区",
+      "level":"1",
+      "code":"8200",
+      "value":[
+        {
+          "label":"澳门特别行政区",
+          "level":"1",
+          "code":"8200"
+        }
+      ]
+    }
+  ]
+  export { data }

+ 41 - 0
https/http.js

@@ -0,0 +1,41 @@
+import http from '@/https/request.js'
+
+export default {
+	// 地区信息
+	area_tree(data){ return http('/api/common/area_tree' , "GET" , data) },
+	//用户登录
+	passwordLogin(data){ return http('/api/passwordLogin' , "POST" , data) },
+	//用户信息
+	getUserInfo(data){ return http('/api/getUserInfo' , "POST" , data) },
+	//短信接口
+	smsSend(data){ return http('/api/smsSend' , "POST" , data) },
+	//忘记密码
+	ForgetPassword(data){ return http('/api/ForgetPassword' , "POST" , data) },
+	//完善信息
+	completeInformation(data){ return http('/api/completeInformation' , "POST" , data) },
+	//获取系统设置
+	configInfo(data){ return http('/api/configInfo' , "POST" , data) },
+	//上传接口
+	uploadLocality(data){ return http('/api/uploadLocality' , "POST" , data) },
+	//上传分类
+	getUploadingName(data){ return http('/api/getUploadingName' , "POST" , data) },
+	//病例分类
+	getCaseName(data){ return http('/api/getCaseName' , "POST" , data) },
+	//病例上传
+	patientQuide(data){ return http('/api/patientQuide' , "POST" , data) },
+	//病例列表
+	patientQuideByPhongList(data){ return http('/api/patientQuideByPhongList' , "POST" , data) },
+	//专家病例列表
+	patientListZj(data){ return http('/api/patientListZj' , "POST" , data) },
+	//病例编辑
+	patientQuideEdit(data){ return http('/api/patientQuideEdit' , "POST" , data) },
+	
+	//上传接口2
+	uploadLocality2(data){ return http('/api/uploadLocality2' , "POST" , data) },
+	
+	//问卷
+	topicAdd(data){ return http('/api/topicAdd' , "POST" , data) },
+	// 问卷数量
+	getTopicNum(data){ return http('/api/getTopicNum' , "POST" , data) },
+	
+}

+ 48 - 0
https/request.js

@@ -0,0 +1,48 @@
+//请求头 
+import { baseUrl } from '@/https/url.js'
+
+export default(url, method, params) => { // 传参: 接口url,  method类型, params参数
+	const header = {
+		'accept':'application/json, text/plain, */*',
+		'Content-type': 'application/json',
+		'Authorization':uni.getStorageSync('token')?uni.getStorageSync('token'):"",
+	}
+    return new Promise((resolve, reject) => {
+        uni.request({
+            method: method,
+            url: baseUrl + url,
+            data: method=='GET'?params:JSON.stringify(params),
+            header: header
+        })
+        .then((response) => {
+            // 请求成功后的处理
+			if(response[1].data.code==201){
+				uni.removeStorageSync('token');
+				uni.showToast({
+					title: '登录失效,请刷新界面.',
+					duration: 2000,
+					icon: 'none'
+				});
+			}else if(response[1].data.code==0){
+				if(response[1].data.message == 'token不能为空'){
+					uni.removeStorageSync('token');
+					uni.showToast({
+						title: '登录失效,请刷新界面.',
+						duration: 2000,
+						icon: 'none'
+					});
+					uni.reLaunch({
+						url: '/pages/login/login'
+					});
+				}
+			}
+            resolve(response[1]) 
+			// if(response[1].)
+        }).catch((reject) => {
+            // 请求失败后的处理
+            console.log('请求失败')
+			reject(reject)
+        })
+    })
+    
+}

+ 3 - 0
https/url.js

@@ -0,0 +1,3 @@
+// const baseUrl = 'http://d06u7pg536nk.ngrok.xiaomiqiu123.top' //测试环境
+const baseUrl = 'https://www.speacemedblxm.cn' //正式
+export { baseUrl }

+ 59 - 0
main.js

@@ -0,0 +1,59 @@
+import Vue from 'vue';
+import App from './App';
+
+Vue.config.productionTip = false;
+
+App.mpType = 'app';
+
+
+// 接口封装
+import request from '@/https/http.js'
+Vue.prototype.$http = request;
+// 请求地址
+import { baseUrl } from '@/https/url.js'
+Vue.prototype.$httpUrl = baseUrl;  //请求地址上传图片路径 
+//校验手机格式
+function checkMobile(mobile) {
+	return RegExp(/^1[34578]\d{9}$/).test(mobile);
+}
+// 校验身份证
+var id=/^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}([0-9]|x|X)$/;
+function cardtest (idCard){
+	  return  id.test(idCard)
+};
+Vue.prototype.$checkMobile = checkMobile;
+Vue.prototype.$checkId = cardtest;
+
+import tabBar from "@/components/tabBar.vue"
+Vue.component('tabBar', tabBar)
+// 引入全局uView
+import uView from 'uview-ui';
+Vue.use(uView);
+
+Vue.mixin({
+	onLoad(option) {
+		this.initLogin()
+	},
+	methods: {
+		initLogin(){
+			let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
+			let curRoute = routes[routes.length - 1].route //获取当前页面路由
+			console.log(curRoute)
+			if(!uni.getStorageSync('token')){
+				if(!(curRoute == 'pages/login/login' || curRoute == 'pages/login/agreement' || curRoute == 'pages/login/forget' || curRoute == 'pages/login/perfect')){
+					uni.reLaunch({
+						url: '/pages/login/login'
+					});
+					
+				}
+			}
+		},
+	}
+})
+
+const app = new Vue({
+	...App
+});
+
+
+app.$mount();

+ 138 - 0
manifest.json

@@ -0,0 +1,138 @@
+{
+    "name" : "uView",
+    "appid" : "__UNI__ED9A287",
+    "description" : "多平台快速开发的UI框架",
+    "versionName" : "1.8.4",
+    "versionCode" : "100",
+    "transformPx" : false,
+    "app-plus" : {
+        "optimization" : {
+            "subPackages" : true
+        },
+        "safearea" : {
+            "bottom" : {
+                "offset" : "none"
+            }
+        },
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "modules" : {},
+        "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" : {},
+            "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" : "wxebfcd6bc94ebdb32",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : false,
+            "minified" : true,
+            "postcss" : true
+        },
+        "optimization" : {
+            "subPackages" : true
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true,
+        "component2" : true
+    },
+    "mp-qq" : {
+        "optimization" : {
+            "subPackages" : true
+        },
+        "appid" : "15646153"
+    },
+    "mp-baidu" : {
+        "usingComponents" : true,
+        "appid" : ""
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true,
+        "appid" : ""
+    },
+    "h5" : {
+        "template" : "template.h5.html",
+        "router" : {
+            "mode" : "hash",
+            "base" : ""
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : false
+            }
+        },
+        "title" : "uView UI"
+    }
+}

+ 146 - 0
node_modules/promise-polyfill/CHANGELOG.md

@@ -0,0 +1,146 @@
+# Changelog
+
+### 7.1.2
+
+* Fixed bug in Node JS Promise polyfill
+
+### 7.1.0
+
+* Added Promise.prototype.finally
+
+### 7.0.2
+
+* Added IE8 compatability back to minify
+
+### 7.0.1
+
+* Fixed a bug in 'catch' keyword in IE8
+* Fixed import error when using `require`
+
+## 7.0.0
+
+* Updated code to ES6 module definitions (thanks Andarist)
+* Added polyfill.js file
+* move compiled files to dist folder
+
+## 6.1.0
+
+* Bug fix for non-array values in `Promise.all()`
+* Small optimization checking for making sure `Promise` is called with `new`
+
+## 6.0.0 Deprecated `Promise._setImmediateFn` and `Promise._setUnhandledRejectionFn`
+
+This allows subclassing Promise without rewriting functions
+
+* `Promise._setImmediateFn(<immediateFn>)` has been deprecated. Use `Promise._immediateFn = <immediateFn>` instead.
+* `Promise._setUnhandledRejectionFn(<rejectionFn>)` has been deprecated. Use `Promise._unhandledRejectionFn = <rejectionFn>` instead.
+  These functions will be removed in the next major version.
+
+### 5.2.1 setTimeout to 0
+
+Fixed bug where setTimeout was set to 1 instead of 0 for async execution
+
+### 5.2.0 Subclassing
+
+Allowed Subclassing. [#27](https://github.com/taylorhakes/promise-polyfill/pull/27)
+
+### 5.1.0 Fixed reliance on setTimeout
+
+Changed possibly unhanded warnings to use asap function instead of setTimeout
+
+## 5.0.0 Removed multiple params from Promise.all
+
+Removed non standard functionality of passing multiple params to Promise.all. You must pass an array now. You must change this code
+
+```js
+Promise.all(prom1, prom2, prom3);
+```
+
+to this
+
+```js
+Promise.all([prom1, prom2, prom3]);
+```
+
+### 4.0.4 IE8 console.warn fix
+
+IE8 does not have console, unless you open the developer tools. This fix checks to makes sure console.warn is defined before calling it.
+
+### 4.0.3 Fix case in bower.json
+
+bower.json had Promise.js instead of promise.js
+
+### 4.0.2 promise.js case fix in package.json
+
+Fixed promise.js in package.json. It was accidently published as Promise.js
+
+## 4.0.1 Unhandled Rejections and Other Fixes
+
+* Added unhandled rejection warnings to the console
+* Removed Grunt, jasmine and other unused code
+* Renamed Promise.js to lowercase promise.js in multiple places
+
+### 3.0.1 Fixed shadowing issue on setTimeout
+
+New version fixing this major bug https://github.com/taylorhakes/promise-polyfill/pull/17
+
+## 3.0.0 Updated setTimeout to not be affected by test mocks
+
+This is considered a breaking change because people may have been using this functionality. If you would like to keep version 2 functionality, set Promise.\_setImmediateFn on `promise-polyfill` like the code below.
+
+```js
+var Promise = require('promise-polyfill');
+Promise._setImmedateFn(function(fn) {
+  setTimeout(fn, 1);
+});
+```
+
+### 2.1.0 Promise.\_setImmedateFn
+
+Removed dead code Promise.immedateFn and added Promise.\_setImmediateFn(fn);
+
+### 2.0.2 Simplified Global detection
+
+Simplified attaching to global object
+
+### 2.0.1 Webworker bugfixes
+
+Fixed Webworkers missing window object
+
+## 2.0.0
+
+**Changed the following line**
+
+```
+module.exports = root.Promise ? root.Promise : Promise;
+```
+
+to
+
+```
+module.exports = Promise;
+```
+
+This means the library will not use built-in Promise by default. This allows for more consistency.
+
+You can easily add the functionality back.
+
+```
+var Promise = window.Promise || require('promise-polyfill');
+```
+
+**Added Promise.immediateFn to allow changing the setImmedate function**
+
+```
+Promise.immediateFn = window.setAsap;
+```
+
+### 1.1.4 Updated Promise to use correct global object in Browser and Node
+
+### 1.1.3 Fixed browserify issue with `this`
+
+### 1.1.2 Updated Promise.resolve to resolve with original Promise
+
+### 1.1.0 Performance Improvements for Modern Browsers
+
+## 1.0.1 Update README.md

+ 20 - 0
node_modules/promise-polyfill/LICENSE

@@ -0,0 +1,20 @@
+Copyright (c) 2014 Taylor Hakes
+Copyright (c) 2014 Forbes Lindesay
+
+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.

+ 121 - 0
node_modules/promise-polyfill/README.md

@@ -0,0 +1,121 @@
+# Promise Polyfill
+
+[![travis][travis-image]][travis-url]
+
+[travis-image]: https://img.shields.io/travis/taylorhakes/promise-polyfill.svg?style=flat
+[travis-url]: https://travis-ci.org/taylorhakes/promise-polyfill
+
+Lightweight ES6 Promise polyfill for the browser and node. Adheres closely to
+the spec. It is a perfect polyfill IE, Firefox or any other browser that does
+not support native promises.
+
+For API information about Promises, please check out this article
+[HTML5Rocks article](http://www.html5rocks.com/en/tutorials/es6/promises/).
+
+It is extremely lightweight. **_< 1kb Gzipped_**
+
+## Browser Support
+
+IE8+, Chrome, Firefox, IOS 4+, Safari 5+, Opera
+
+### NPM Use
+
+```
+npm install promise-polyfill --save-exact
+```
+
+### Bower Use
+
+```
+bower install promise-polyfill
+```
+
+### CDN Polyfill Use
+
+```html
+<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@7/dist/polyfill.min.js"></script>
+```
+
+## Downloads
+
+* [Promise](https://raw.github.com/taylorhakes/promise-polyfill/master/dist/promise.js)
+* [Promise-min](https://raw.github.com/taylorhakes/promise-polyfill/master/dist/promise.min.js)
+
+## Simple use
+
+```js
+import Promise from 'promise-polyfill';
+```
+
+then you can use like normal Promises
+
+```js
+var prom = new Promise(function(resolve, reject) {
+  // do a thing, possibly async, then…
+
+  if (/* everything turned out fine */) {
+    resolve("Stuff worked!");
+  }  else {
+    reject(new Error("It broke"));
+  }
+});
+
+prom.then(function(result) {
+  // Do something when async done
+});
+```
+
+If you would like to just polyfill, only if native doesn't exist.
+
+```js
+import 'promise-polyfill/src/polyfill';
+```
+
+## Performance
+
+By default promise-polyfill uses `setImmediate`, but falls back to `setTimeout`
+for executing asynchronously. If a browser does not support `setImmediate`
+(IE/Edge are the only browsers with setImmediate), you may see performance
+issues. Use a `setImmediate` polyfill to fix this issue.
+[setAsap](https://github.com/taylorhakes/setAsap) or
+[setImmediate](https://github.com/YuzuJS/setImmediate) work well.
+
+If you polyfill `window.setImmediate` or use `Promise._immediateFn = yourImmediateFn` it will be used instead of `window.setTimeout`
+
+```
+npm install setasap --save
+```
+
+```js
+import Promise from 'promise-polyfill/src/polyfill';
+import setAsap from 'setasap';
+Promise._immediateFn = setAsap;
+```
+
+## Unhandled Rejections
+
+promise-polyfill will warn you about possibly unhandled rejections. It will show
+a console warning if a Promise is rejected, but no `.catch` is used. You can
+change this behavior by doing.
+
+```js
+Promise._unhandledRejectionFn = <your reject error handler>;
+```
+
+If you would like to disable unhandled rejection messages. Use a noop like
+below.
+
+```js
+Promise._unhandledRejectionFn = function(rejectError) {};
+```
+
+## Testing
+
+```
+npm install
+npm test
+```
+
+## License
+
+MIT

+ 266 - 0
node_modules/promise-polyfill/dist/polyfill.js

@@ -0,0 +1,266 @@
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(factory());
+}(this, (function () { 'use strict';
+
+// Store setTimeout reference so promise-polyfill will be unaffected by
+// other code modifying setTimeout (like sinon.useFakeTimers())
+var setTimeoutFunc = setTimeout;
+
+function noop() {}
+
+// Polyfill for Function.prototype.bind
+function bind(fn, thisArg) {
+  return function() {
+    fn.apply(thisArg, arguments);
+  };
+}
+
+function Promise(fn) {
+  if (!(this instanceof Promise))
+    throw new TypeError('Promises must be constructed via new');
+  if (typeof fn !== 'function') throw new TypeError('not a function');
+  this._state = 0;
+  this._handled = false;
+  this._value = undefined;
+  this._deferreds = [];
+
+  doResolve(fn, this);
+}
+
+function handle(self, deferred) {
+  while (self._state === 3) {
+    self = self._value;
+  }
+  if (self._state === 0) {
+    self._deferreds.push(deferred);
+    return;
+  }
+  self._handled = true;
+  Promise._immediateFn(function() {
+    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+    if (cb === null) {
+      (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+      return;
+    }
+    var ret;
+    try {
+      ret = cb(self._value);
+    } catch (e) {
+      reject(deferred.promise, e);
+      return;
+    }
+    resolve(deferred.promise, ret);
+  });
+}
+
+function resolve(self, newValue) {
+  try {
+    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+    if (newValue === self)
+      throw new TypeError('A promise cannot be resolved with itself.');
+    if (
+      newValue &&
+      (typeof newValue === 'object' || typeof newValue === 'function')
+    ) {
+      var then = newValue.then;
+      if (newValue instanceof Promise) {
+        self._state = 3;
+        self._value = newValue;
+        finale(self);
+        return;
+      } else if (typeof then === 'function') {
+        doResolve(bind(then, newValue), self);
+        return;
+      }
+    }
+    self._state = 1;
+    self._value = newValue;
+    finale(self);
+  } catch (e) {
+    reject(self, e);
+  }
+}
+
+function reject(self, newValue) {
+  self._state = 2;
+  self._value = newValue;
+  finale(self);
+}
+
+function finale(self) {
+  if (self._state === 2 && self._deferreds.length === 0) {
+    Promise._immediateFn(function() {
+      if (!self._handled) {
+        Promise._unhandledRejectionFn(self._value);
+      }
+    });
+  }
+
+  for (var i = 0, len = self._deferreds.length; i < len; i++) {
+    handle(self, self._deferreds[i]);
+  }
+  self._deferreds = null;
+}
+
+function Handler(onFulfilled, onRejected, promise) {
+  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+  this.promise = promise;
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, self) {
+  var done = false;
+  try {
+    fn(
+      function(value) {
+        if (done) return;
+        done = true;
+        resolve(self, value);
+      },
+      function(reason) {
+        if (done) return;
+        done = true;
+        reject(self, reason);
+      }
+    );
+  } catch (ex) {
+    if (done) return;
+    done = true;
+    reject(self, ex);
+  }
+}
+
+Promise.prototype['catch'] = function(onRejected) {
+  return this.then(null, onRejected);
+};
+
+Promise.prototype.then = function(onFulfilled, onRejected) {
+  var prom = new this.constructor(noop);
+
+  handle(this, new Handler(onFulfilled, onRejected, prom));
+  return prom;
+};
+
+Promise.prototype['finally'] = function(callback) {
+  var constructor = this.constructor;
+  return this.then(
+    function(value) {
+      return constructor.resolve(callback()).then(function() {
+        return value;
+      });
+    },
+    function(reason) {
+      return constructor.resolve(callback()).then(function() {
+        return constructor.reject(reason);
+      });
+    }
+  );
+};
+
+Promise.all = function(arr) {
+  return new Promise(function(resolve, reject) {
+    if (!arr || typeof arr.length === 'undefined')
+      throw new TypeError('Promise.all accepts an array');
+    var args = Array.prototype.slice.call(arr);
+    if (args.length === 0) return resolve([]);
+    var remaining = args.length;
+
+    function res(i, val) {
+      try {
+        if (val && (typeof val === 'object' || typeof val === 'function')) {
+          var then = val.then;
+          if (typeof then === 'function') {
+            then.call(
+              val,
+              function(val) {
+                res(i, val);
+              },
+              reject
+            );
+            return;
+          }
+        }
+        args[i] = val;
+        if (--remaining === 0) {
+          resolve(args);
+        }
+      } catch (ex) {
+        reject(ex);
+      }
+    }
+
+    for (var i = 0; i < args.length; i++) {
+      res(i, args[i]);
+    }
+  });
+};
+
+Promise.resolve = function(value) {
+  if (value && typeof value === 'object' && value.constructor === Promise) {
+    return value;
+  }
+
+  return new Promise(function(resolve) {
+    resolve(value);
+  });
+};
+
+Promise.reject = function(value) {
+  return new Promise(function(resolve, reject) {
+    reject(value);
+  });
+};
+
+Promise.race = function(values) {
+  return new Promise(function(resolve, reject) {
+    for (var i = 0, len = values.length; i < len; i++) {
+      values[i].then(resolve, reject);
+    }
+  });
+};
+
+// Use polyfill for setImmediate for performance gains
+Promise._immediateFn =
+  (typeof setImmediate === 'function' &&
+    function(fn) {
+      setImmediate(fn);
+    }) ||
+  function(fn) {
+    setTimeoutFunc(fn, 0);
+  };
+
+Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+  if (typeof console !== 'undefined' && console) {
+    console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+  }
+};
+
+var globalNS = (function() {
+  // the only reliable means to get the global object is
+  // `Function('return this')()`
+  // However, this causes CSP violations in Chrome apps.
+  if (typeof self !== 'undefined') {
+    return self;
+  }
+  if (typeof window !== 'undefined') {
+    return window;
+  }
+  if (typeof global !== 'undefined') {
+    return global;
+  }
+  throw new Error('unable to locate global object');
+})();
+
+if (!globalNS.Promise) {
+  globalNS.Promise = Promise;
+}
+
+})));

Plik diff jest za duży
+ 0 - 0
node_modules/promise-polyfill/dist/polyfill.min.js


+ 248 - 0
node_modules/promise-polyfill/dist/promise.js

@@ -0,0 +1,248 @@
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.Promise = factory());
+}(this, (function () { 'use strict';
+
+// Store setTimeout reference so promise-polyfill will be unaffected by
+// other code modifying setTimeout (like sinon.useFakeTimers())
+var setTimeoutFunc = setTimeout;
+
+function noop() {}
+
+// Polyfill for Function.prototype.bind
+function bind(fn, thisArg) {
+  return function() {
+    fn.apply(thisArg, arguments);
+  };
+}
+
+function Promise(fn) {
+  if (!(this instanceof Promise))
+    throw new TypeError('Promises must be constructed via new');
+  if (typeof fn !== 'function') throw new TypeError('not a function');
+  this._state = 0;
+  this._handled = false;
+  this._value = undefined;
+  this._deferreds = [];
+
+  doResolve(fn, this);
+}
+
+function handle(self, deferred) {
+  while (self._state === 3) {
+    self = self._value;
+  }
+  if (self._state === 0) {
+    self._deferreds.push(deferred);
+    return;
+  }
+  self._handled = true;
+  Promise._immediateFn(function() {
+    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+    if (cb === null) {
+      (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+      return;
+    }
+    var ret;
+    try {
+      ret = cb(self._value);
+    } catch (e) {
+      reject(deferred.promise, e);
+      return;
+    }
+    resolve(deferred.promise, ret);
+  });
+}
+
+function resolve(self, newValue) {
+  try {
+    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+    if (newValue === self)
+      throw new TypeError('A promise cannot be resolved with itself.');
+    if (
+      newValue &&
+      (typeof newValue === 'object' || typeof newValue === 'function')
+    ) {
+      var then = newValue.then;
+      if (newValue instanceof Promise) {
+        self._state = 3;
+        self._value = newValue;
+        finale(self);
+        return;
+      } else if (typeof then === 'function') {
+        doResolve(bind(then, newValue), self);
+        return;
+      }
+    }
+    self._state = 1;
+    self._value = newValue;
+    finale(self);
+  } catch (e) {
+    reject(self, e);
+  }
+}
+
+function reject(self, newValue) {
+  self._state = 2;
+  self._value = newValue;
+  finale(self);
+}
+
+function finale(self) {
+  if (self._state === 2 && self._deferreds.length === 0) {
+    Promise._immediateFn(function() {
+      if (!self._handled) {
+        Promise._unhandledRejectionFn(self._value);
+      }
+    });
+  }
+
+  for (var i = 0, len = self._deferreds.length; i < len; i++) {
+    handle(self, self._deferreds[i]);
+  }
+  self._deferreds = null;
+}
+
+function Handler(onFulfilled, onRejected, promise) {
+  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+  this.promise = promise;
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, self) {
+  var done = false;
+  try {
+    fn(
+      function(value) {
+        if (done) return;
+        done = true;
+        resolve(self, value);
+      },
+      function(reason) {
+        if (done) return;
+        done = true;
+        reject(self, reason);
+      }
+    );
+  } catch (ex) {
+    if (done) return;
+    done = true;
+    reject(self, ex);
+  }
+}
+
+Promise.prototype['catch'] = function(onRejected) {
+  return this.then(null, onRejected);
+};
+
+Promise.prototype.then = function(onFulfilled, onRejected) {
+  var prom = new this.constructor(noop);
+
+  handle(this, new Handler(onFulfilled, onRejected, prom));
+  return prom;
+};
+
+Promise.prototype['finally'] = function(callback) {
+  var constructor = this.constructor;
+  return this.then(
+    function(value) {
+      return constructor.resolve(callback()).then(function() {
+        return value;
+      });
+    },
+    function(reason) {
+      return constructor.resolve(callback()).then(function() {
+        return constructor.reject(reason);
+      });
+    }
+  );
+};
+
+Promise.all = function(arr) {
+  return new Promise(function(resolve, reject) {
+    if (!arr || typeof arr.length === 'undefined')
+      throw new TypeError('Promise.all accepts an array');
+    var args = Array.prototype.slice.call(arr);
+    if (args.length === 0) return resolve([]);
+    var remaining = args.length;
+
+    function res(i, val) {
+      try {
+        if (val && (typeof val === 'object' || typeof val === 'function')) {
+          var then = val.then;
+          if (typeof then === 'function') {
+            then.call(
+              val,
+              function(val) {
+                res(i, val);
+              },
+              reject
+            );
+            return;
+          }
+        }
+        args[i] = val;
+        if (--remaining === 0) {
+          resolve(args);
+        }
+      } catch (ex) {
+        reject(ex);
+      }
+    }
+
+    for (var i = 0; i < args.length; i++) {
+      res(i, args[i]);
+    }
+  });
+};
+
+Promise.resolve = function(value) {
+  if (value && typeof value === 'object' && value.constructor === Promise) {
+    return value;
+  }
+
+  return new Promise(function(resolve) {
+    resolve(value);
+  });
+};
+
+Promise.reject = function(value) {
+  return new Promise(function(resolve, reject) {
+    reject(value);
+  });
+};
+
+Promise.race = function(values) {
+  return new Promise(function(resolve, reject) {
+    for (var i = 0, len = values.length; i < len; i++) {
+      values[i].then(resolve, reject);
+    }
+  });
+};
+
+// Use polyfill for setImmediate for performance gains
+Promise._immediateFn =
+  (typeof setImmediate === 'function' &&
+    function(fn) {
+      setImmediate(fn);
+    }) ||
+  function(fn) {
+    setTimeoutFunc(fn, 0);
+  };
+
+Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+  if (typeof console !== 'undefined' && console) {
+    console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+  }
+};
+
+return Promise;
+
+})));

Plik diff jest za duży
+ 0 - 0
node_modules/promise-polyfill/dist/promise.min.js


+ 242 - 0
node_modules/promise-polyfill/lib/index.js

@@ -0,0 +1,242 @@
+'use strict';
+
+// Store setTimeout reference so promise-polyfill will be unaffected by
+// other code modifying setTimeout (like sinon.useFakeTimers())
+var setTimeoutFunc = setTimeout;
+
+function noop() {}
+
+// Polyfill for Function.prototype.bind
+function bind(fn, thisArg) {
+  return function() {
+    fn.apply(thisArg, arguments);
+  };
+}
+
+function Promise(fn) {
+  if (!(this instanceof Promise))
+    throw new TypeError('Promises must be constructed via new');
+  if (typeof fn !== 'function') throw new TypeError('not a function');
+  this._state = 0;
+  this._handled = false;
+  this._value = undefined;
+  this._deferreds = [];
+
+  doResolve(fn, this);
+}
+
+function handle(self, deferred) {
+  while (self._state === 3) {
+    self = self._value;
+  }
+  if (self._state === 0) {
+    self._deferreds.push(deferred);
+    return;
+  }
+  self._handled = true;
+  Promise._immediateFn(function() {
+    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+    if (cb === null) {
+      (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+      return;
+    }
+    var ret;
+    try {
+      ret = cb(self._value);
+    } catch (e) {
+      reject(deferred.promise, e);
+      return;
+    }
+    resolve(deferred.promise, ret);
+  });
+}
+
+function resolve(self, newValue) {
+  try {
+    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+    if (newValue === self)
+      throw new TypeError('A promise cannot be resolved with itself.');
+    if (
+      newValue &&
+      (typeof newValue === 'object' || typeof newValue === 'function')
+    ) {
+      var then = newValue.then;
+      if (newValue instanceof Promise) {
+        self._state = 3;
+        self._value = newValue;
+        finale(self);
+        return;
+      } else if (typeof then === 'function') {
+        doResolve(bind(then, newValue), self);
+        return;
+      }
+    }
+    self._state = 1;
+    self._value = newValue;
+    finale(self);
+  } catch (e) {
+    reject(self, e);
+  }
+}
+
+function reject(self, newValue) {
+  self._state = 2;
+  self._value = newValue;
+  finale(self);
+}
+
+function finale(self) {
+  if (self._state === 2 && self._deferreds.length === 0) {
+    Promise._immediateFn(function() {
+      if (!self._handled) {
+        Promise._unhandledRejectionFn(self._value);
+      }
+    });
+  }
+
+  for (var i = 0, len = self._deferreds.length; i < len; i++) {
+    handle(self, self._deferreds[i]);
+  }
+  self._deferreds = null;
+}
+
+function Handler(onFulfilled, onRejected, promise) {
+  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+  this.promise = promise;
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, self) {
+  var done = false;
+  try {
+    fn(
+      function(value) {
+        if (done) return;
+        done = true;
+        resolve(self, value);
+      },
+      function(reason) {
+        if (done) return;
+        done = true;
+        reject(self, reason);
+      }
+    );
+  } catch (ex) {
+    if (done) return;
+    done = true;
+    reject(self, ex);
+  }
+}
+
+Promise.prototype['catch'] = function(onRejected) {
+  return this.then(null, onRejected);
+};
+
+Promise.prototype.then = function(onFulfilled, onRejected) {
+  var prom = new this.constructor(noop);
+
+  handle(this, new Handler(onFulfilled, onRejected, prom));
+  return prom;
+};
+
+Promise.prototype['finally'] = function(callback) {
+  var constructor = this.constructor;
+  return this.then(
+    function(value) {
+      return constructor.resolve(callback()).then(function() {
+        return value;
+      });
+    },
+    function(reason) {
+      return constructor.resolve(callback()).then(function() {
+        return constructor.reject(reason);
+      });
+    }
+  );
+};
+
+Promise.all = function(arr) {
+  return new Promise(function(resolve, reject) {
+    if (!arr || typeof arr.length === 'undefined')
+      throw new TypeError('Promise.all accepts an array');
+    var args = Array.prototype.slice.call(arr);
+    if (args.length === 0) return resolve([]);
+    var remaining = args.length;
+
+    function res(i, val) {
+      try {
+        if (val && (typeof val === 'object' || typeof val === 'function')) {
+          var then = val.then;
+          if (typeof then === 'function') {
+            then.call(
+              val,
+              function(val) {
+                res(i, val);
+              },
+              reject
+            );
+            return;
+          }
+        }
+        args[i] = val;
+        if (--remaining === 0) {
+          resolve(args);
+        }
+      } catch (ex) {
+        reject(ex);
+      }
+    }
+
+    for (var i = 0; i < args.length; i++) {
+      res(i, args[i]);
+    }
+  });
+};
+
+Promise.resolve = function(value) {
+  if (value && typeof value === 'object' && value.constructor === Promise) {
+    return value;
+  }
+
+  return new Promise(function(resolve) {
+    resolve(value);
+  });
+};
+
+Promise.reject = function(value) {
+  return new Promise(function(resolve, reject) {
+    reject(value);
+  });
+};
+
+Promise.race = function(values) {
+  return new Promise(function(resolve, reject) {
+    for (var i = 0, len = values.length; i < len; i++) {
+      values[i].then(resolve, reject);
+    }
+  });
+};
+
+// Use polyfill for setImmediate for performance gains
+Promise._immediateFn =
+  (typeof setImmediate === 'function' &&
+    function(fn) {
+      setImmediate(fn);
+    }) ||
+  function(fn) {
+    setTimeoutFunc(fn, 0);
+  };
+
+Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+  if (typeof console !== 'undefined' && console) {
+    console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+  }
+};
+
+module.exports = Promise;

+ 260 - 0
node_modules/promise-polyfill/lib/polyfill.js

@@ -0,0 +1,260 @@
+'use strict';
+
+// Store setTimeout reference so promise-polyfill will be unaffected by
+// other code modifying setTimeout (like sinon.useFakeTimers())
+var setTimeoutFunc = setTimeout;
+
+function noop() {}
+
+// Polyfill for Function.prototype.bind
+function bind(fn, thisArg) {
+  return function() {
+    fn.apply(thisArg, arguments);
+  };
+}
+
+function Promise(fn) {
+  if (!(this instanceof Promise))
+    throw new TypeError('Promises must be constructed via new');
+  if (typeof fn !== 'function') throw new TypeError('not a function');
+  this._state = 0;
+  this._handled = false;
+  this._value = undefined;
+  this._deferreds = [];
+
+  doResolve(fn, this);
+}
+
+function handle(self, deferred) {
+  while (self._state === 3) {
+    self = self._value;
+  }
+  if (self._state === 0) {
+    self._deferreds.push(deferred);
+    return;
+  }
+  self._handled = true;
+  Promise._immediateFn(function() {
+    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+    if (cb === null) {
+      (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+      return;
+    }
+    var ret;
+    try {
+      ret = cb(self._value);
+    } catch (e) {
+      reject(deferred.promise, e);
+      return;
+    }
+    resolve(deferred.promise, ret);
+  });
+}
+
+function resolve(self, newValue) {
+  try {
+    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+    if (newValue === self)
+      throw new TypeError('A promise cannot be resolved with itself.');
+    if (
+      newValue &&
+      (typeof newValue === 'object' || typeof newValue === 'function')
+    ) {
+      var then = newValue.then;
+      if (newValue instanceof Promise) {
+        self._state = 3;
+        self._value = newValue;
+        finale(self);
+        return;
+      } else if (typeof then === 'function') {
+        doResolve(bind(then, newValue), self);
+        return;
+      }
+    }
+    self._state = 1;
+    self._value = newValue;
+    finale(self);
+  } catch (e) {
+    reject(self, e);
+  }
+}
+
+function reject(self, newValue) {
+  self._state = 2;
+  self._value = newValue;
+  finale(self);
+}
+
+function finale(self) {
+  if (self._state === 2 && self._deferreds.length === 0) {
+    Promise._immediateFn(function() {
+      if (!self._handled) {
+        Promise._unhandledRejectionFn(self._value);
+      }
+    });
+  }
+
+  for (var i = 0, len = self._deferreds.length; i < len; i++) {
+    handle(self, self._deferreds[i]);
+  }
+  self._deferreds = null;
+}
+
+function Handler(onFulfilled, onRejected, promise) {
+  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+  this.promise = promise;
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, self) {
+  var done = false;
+  try {
+    fn(
+      function(value) {
+        if (done) return;
+        done = true;
+        resolve(self, value);
+      },
+      function(reason) {
+        if (done) return;
+        done = true;
+        reject(self, reason);
+      }
+    );
+  } catch (ex) {
+    if (done) return;
+    done = true;
+    reject(self, ex);
+  }
+}
+
+Promise.prototype['catch'] = function(onRejected) {
+  return this.then(null, onRejected);
+};
+
+Promise.prototype.then = function(onFulfilled, onRejected) {
+  var prom = new this.constructor(noop);
+
+  handle(this, new Handler(onFulfilled, onRejected, prom));
+  return prom;
+};
+
+Promise.prototype['finally'] = function(callback) {
+  var constructor = this.constructor;
+  return this.then(
+    function(value) {
+      return constructor.resolve(callback()).then(function() {
+        return value;
+      });
+    },
+    function(reason) {
+      return constructor.resolve(callback()).then(function() {
+        return constructor.reject(reason);
+      });
+    }
+  );
+};
+
+Promise.all = function(arr) {
+  return new Promise(function(resolve, reject) {
+    if (!arr || typeof arr.length === 'undefined')
+      throw new TypeError('Promise.all accepts an array');
+    var args = Array.prototype.slice.call(arr);
+    if (args.length === 0) return resolve([]);
+    var remaining = args.length;
+
+    function res(i, val) {
+      try {
+        if (val && (typeof val === 'object' || typeof val === 'function')) {
+          var then = val.then;
+          if (typeof then === 'function') {
+            then.call(
+              val,
+              function(val) {
+                res(i, val);
+              },
+              reject
+            );
+            return;
+          }
+        }
+        args[i] = val;
+        if (--remaining === 0) {
+          resolve(args);
+        }
+      } catch (ex) {
+        reject(ex);
+      }
+    }
+
+    for (var i = 0; i < args.length; i++) {
+      res(i, args[i]);
+    }
+  });
+};
+
+Promise.resolve = function(value) {
+  if (value && typeof value === 'object' && value.constructor === Promise) {
+    return value;
+  }
+
+  return new Promise(function(resolve) {
+    resolve(value);
+  });
+};
+
+Promise.reject = function(value) {
+  return new Promise(function(resolve, reject) {
+    reject(value);
+  });
+};
+
+Promise.race = function(values) {
+  return new Promise(function(resolve, reject) {
+    for (var i = 0, len = values.length; i < len; i++) {
+      values[i].then(resolve, reject);
+    }
+  });
+};
+
+// Use polyfill for setImmediate for performance gains
+Promise._immediateFn =
+  (typeof setImmediate === 'function' &&
+    function(fn) {
+      setImmediate(fn);
+    }) ||
+  function(fn) {
+    setTimeoutFunc(fn, 0);
+  };
+
+Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+  if (typeof console !== 'undefined' && console) {
+    console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+  }
+};
+
+var globalNS = (function() {
+  // the only reliable means to get the global object is
+  // `Function('return this')()`
+  // However, this causes CSP violations in Chrome apps.
+  if (typeof self !== 'undefined') {
+    return self;
+  }
+  if (typeof window !== 'undefined') {
+    return window;
+  }
+  if (typeof global !== 'undefined') {
+    return global;
+  }
+  throw new Error('unable to locate global object');
+})();
+
+if (!globalNS.Promise) {
+  globalNS.Promise = Promise;
+}

+ 90 - 0
node_modules/promise-polyfill/package.json

@@ -0,0 +1,90 @@
+{
+  "_from": "promise-polyfill@^7.1.0",
+  "_id": "promise-polyfill@7.1.2",
+  "_inBundle": false,
+  "_integrity": "sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==",
+  "_location": "/promise-polyfill",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "promise-polyfill@^7.1.0",
+    "name": "promise-polyfill",
+    "escapedName": "promise-polyfill",
+    "rawSpec": "^7.1.0",
+    "saveSpec": null,
+    "fetchSpec": "^7.1.0"
+  },
+  "_requiredBy": [
+    "/dingtalk-jsapi"
+  ],
+  "_resolved": "https://registry.npmmirror.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz",
+  "_shasum": "ab05301d8c28536301622d69227632269a70ca3b",
+  "_spec": "promise-polyfill@^7.1.0",
+  "_where": "D:\\zs项目\\Ding\\node_modules\\dingtalk-jsapi",
+  "author": {
+    "name": "Taylor Hakes"
+  },
+  "bugs": {
+    "url": "https://github.com/taylorhakes/promise-polyfill/issues"
+  },
+  "bundleDependencies": false,
+  "dependencies": {},
+  "deprecated": false,
+  "description": "Lightweight promise polyfill. A+ compliant",
+  "devDependencies": {
+    "cross-env": "^5.1.1",
+    "eslint": "^4.11.0",
+    "husky": "^0.14.3",
+    "karma": "^0.13.19",
+    "karma-browserify": "^4.4.2",
+    "karma-chrome-launcher": "^0.2.2",
+    "karma-mocha": "^0.2.1",
+    "lint-staged": "^5.0.0",
+    "mocha": "^2.3.4",
+    "npm-run-all": "^4.1.2",
+    "prettier": "^1.8.2",
+    "promises-aplus-tests": "*",
+    "rimraf": "^2.6.2",
+    "rollup": "^0.52.0",
+    "rollup-plugin-uglify": "^2.0.1",
+    "sinon": "^1.17.2"
+  },
+  "files": [
+    "src",
+    "lib",
+    "dist"
+  ],
+  "homepage": "https://github.com/taylorhakes/promise-polyfill",
+  "jsnext:main": "src/index.js",
+  "keywords": [
+    "promise",
+    "promise-polyfill",
+    "ES6",
+    "promises-aplus"
+  ],
+  "license": "MIT",
+  "main": "lib/index.js",
+  "name": "promise-polyfill",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/taylorhakes/promise-polyfill.git"
+  },
+  "scripts": {
+    "build": "run-p build:**",
+    "build:cjs": "rollup -i src/index.js -o lib/index.js -f cjs",
+    "build:cjs-polyfill": "rollup -i src/polyfill.js -o lib/polyfill.js -f cjs",
+    "build:umd": "cross-env NODE_ENV=development rollup -i src/index.js -o dist/promise.js -c rollup.umd.config.js",
+    "build:umd-polyfill": "cross-env NODE_ENV=development rollup -i src/polyfill.js -o dist/polyfill.js -c rollup.umd.config.js",
+    "build:umd-polyfill:min": "cross-env NODE_ENV=production rollup -i src/polyfill.js -o dist/polyfill.min.js -c rollup.umd.config.js",
+    "build:umd:min": "cross-env NODE_ENV=production rollup -i src/index.js -o dist/promise.min.js -c rollup.umd.config.js",
+    "prebuild": "rimraf lib dist",
+    "precommit": "lint-staged",
+    "prepare": "npm run build",
+    "prepublish": "test $(npm -v | tr . '\\n' | head -n 1) -ge '4' || exit 1",
+    "pretest": "npm run build:cjs",
+    "test": "eslint src && mocha && karma start --single-run"
+  },
+  "unpkg": "dist/promise.min.js",
+  "version": "7.1.2"
+}

+ 240 - 0
node_modules/promise-polyfill/src/index.js

@@ -0,0 +1,240 @@
+// Store setTimeout reference so promise-polyfill will be unaffected by
+// other code modifying setTimeout (like sinon.useFakeTimers())
+var setTimeoutFunc = setTimeout;
+
+function noop() {}
+
+// Polyfill for Function.prototype.bind
+function bind(fn, thisArg) {
+  return function() {
+    fn.apply(thisArg, arguments);
+  };
+}
+
+function Promise(fn) {
+  if (!(this instanceof Promise))
+    throw new TypeError('Promises must be constructed via new');
+  if (typeof fn !== 'function') throw new TypeError('not a function');
+  this._state = 0;
+  this._handled = false;
+  this._value = undefined;
+  this._deferreds = [];
+
+  doResolve(fn, this);
+}
+
+function handle(self, deferred) {
+  while (self._state === 3) {
+    self = self._value;
+  }
+  if (self._state === 0) {
+    self._deferreds.push(deferred);
+    return;
+  }
+  self._handled = true;
+  Promise._immediateFn(function() {
+    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+    if (cb === null) {
+      (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+      return;
+    }
+    var ret;
+    try {
+      ret = cb(self._value);
+    } catch (e) {
+      reject(deferred.promise, e);
+      return;
+    }
+    resolve(deferred.promise, ret);
+  });
+}
+
+function resolve(self, newValue) {
+  try {
+    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+    if (newValue === self)
+      throw new TypeError('A promise cannot be resolved with itself.');
+    if (
+      newValue &&
+      (typeof newValue === 'object' || typeof newValue === 'function')
+    ) {
+      var then = newValue.then;
+      if (newValue instanceof Promise) {
+        self._state = 3;
+        self._value = newValue;
+        finale(self);
+        return;
+      } else if (typeof then === 'function') {
+        doResolve(bind(then, newValue), self);
+        return;
+      }
+    }
+    self._state = 1;
+    self._value = newValue;
+    finale(self);
+  } catch (e) {
+    reject(self, e);
+  }
+}
+
+function reject(self, newValue) {
+  self._state = 2;
+  self._value = newValue;
+  finale(self);
+}
+
+function finale(self) {
+  if (self._state === 2 && self._deferreds.length === 0) {
+    Promise._immediateFn(function() {
+      if (!self._handled) {
+        Promise._unhandledRejectionFn(self._value);
+      }
+    });
+  }
+
+  for (var i = 0, len = self._deferreds.length; i < len; i++) {
+    handle(self, self._deferreds[i]);
+  }
+  self._deferreds = null;
+}
+
+function Handler(onFulfilled, onRejected, promise) {
+  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+  this.promise = promise;
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, self) {
+  var done = false;
+  try {
+    fn(
+      function(value) {
+        if (done) return;
+        done = true;
+        resolve(self, value);
+      },
+      function(reason) {
+        if (done) return;
+        done = true;
+        reject(self, reason);
+      }
+    );
+  } catch (ex) {
+    if (done) return;
+    done = true;
+    reject(self, ex);
+  }
+}
+
+Promise.prototype['catch'] = function(onRejected) {
+  return this.then(null, onRejected);
+};
+
+Promise.prototype.then = function(onFulfilled, onRejected) {
+  var prom = new this.constructor(noop);
+
+  handle(this, new Handler(onFulfilled, onRejected, prom));
+  return prom;
+};
+
+Promise.prototype['finally'] = function(callback) {
+  var constructor = this.constructor;
+  return this.then(
+    function(value) {
+      return constructor.resolve(callback()).then(function() {
+        return value;
+      });
+    },
+    function(reason) {
+      return constructor.resolve(callback()).then(function() {
+        return constructor.reject(reason);
+      });
+    }
+  );
+};
+
+Promise.all = function(arr) {
+  return new Promise(function(resolve, reject) {
+    if (!arr || typeof arr.length === 'undefined')
+      throw new TypeError('Promise.all accepts an array');
+    var args = Array.prototype.slice.call(arr);
+    if (args.length === 0) return resolve([]);
+    var remaining = args.length;
+
+    function res(i, val) {
+      try {
+        if (val && (typeof val === 'object' || typeof val === 'function')) {
+          var then = val.then;
+          if (typeof then === 'function') {
+            then.call(
+              val,
+              function(val) {
+                res(i, val);
+              },
+              reject
+            );
+            return;
+          }
+        }
+        args[i] = val;
+        if (--remaining === 0) {
+          resolve(args);
+        }
+      } catch (ex) {
+        reject(ex);
+      }
+    }
+
+    for (var i = 0; i < args.length; i++) {
+      res(i, args[i]);
+    }
+  });
+};
+
+Promise.resolve = function(value) {
+  if (value && typeof value === 'object' && value.constructor === Promise) {
+    return value;
+  }
+
+  return new Promise(function(resolve) {
+    resolve(value);
+  });
+};
+
+Promise.reject = function(value) {
+  return new Promise(function(resolve, reject) {
+    reject(value);
+  });
+};
+
+Promise.race = function(values) {
+  return new Promise(function(resolve, reject) {
+    for (var i = 0, len = values.length; i < len; i++) {
+      values[i].then(resolve, reject);
+    }
+  });
+};
+
+// Use polyfill for setImmediate for performance gains
+Promise._immediateFn =
+  (typeof setImmediate === 'function' &&
+    function(fn) {
+      setImmediate(fn);
+    }) ||
+  function(fn) {
+    setTimeoutFunc(fn, 0);
+  };
+
+Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+  if (typeof console !== 'undefined' && console) {
+    console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+  }
+};
+
+export default Promise;

+ 21 - 0
node_modules/promise-polyfill/src/polyfill.js

@@ -0,0 +1,21 @@
+import Promise from './index';
+
+var globalNS = (function() {
+  // the only reliable means to get the global object is
+  // `Function('return this')()`
+  // However, this causes CSP violations in Chrome apps.
+  if (typeof self !== 'undefined') {
+    return self;
+  }
+  if (typeof window !== 'undefined') {
+    return window;
+  }
+  if (typeof global !== 'undefined') {
+    return global;
+  }
+  throw new Error('unable to locate global object');
+})();
+
+if (!globalNS.Promise) {
+  globalNS.Promise = Promise;
+}

+ 16 - 0
package-lock.json

@@ -0,0 +1,16 @@
+{
+  "name": "uView",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "dingtalk-jsapi": {
+      "version": "3.0.12",
+      "resolved": "https://registry.npmmirror.com/dingtalk-jsapi/-/dingtalk-jsapi-3.0.12.tgz",
+      "integrity": "sha512-vSP5fGuSthytrIYsN9ITa4DIO5h0bhIIKmcIYIRdvHN0brhzn1IX3hkFtNNNnjg6+u+pHWT8bKH/PRheWIhOLQ==",
+      "requires": {
+        "promise-polyfill": "^7.1.0"
+      }
+    }
+  }
+}

+ 24 - 0
package.json

@@ -0,0 +1,24 @@
+{
+  "name": "uView",
+  "version": "1.0.0",
+  "description": "<p align=\"center\">\r     <img alt=\"logo\" src=\"https://uviewui.com/common/logo.png\" width=\"120\" height=\"120\" style=\"margin-bottom: 10px;\">\r </p>\r <h3 align=\"center\" style=\"margin: 30px 0 30px;font-weight: bold;font-size:40px;\">uView</h3>\r <h3 align=\"center\">多平台快速开发的UI框架</h3>",
+  "main": "main.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/YanxinNet/uView.git"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "bugs": {
+    "url": "https://github.com/YanxinNet/uView/issues"
+  },
+  "homepage": "https://github.com/YanxinNet/uView#readme",
+  "dependencies": {
+    "dingtalk-jsapi": "^3.0.12",
+    "vue-i18n": "^8.20.0"
+  }
+}

+ 107 - 0
pages.json

@@ -0,0 +1,107 @@
+{
+	"easycom": {
+		"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue",
+		"^d-(.*)": "@/components/d-$1.vue"
+	},
+	"pages": [
+		// 演示-组件
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "活动详情"
+			}
+		},
+		{
+			"path": "pages/index/guide",
+			"style": {
+				"navigationBarTitleText": "操作指南"
+			}
+		},
+		{
+			"path": "pages/index/wenjuan",
+			"style": {
+				"navigationBarTitleText": "问卷调查"
+			}
+		},
+		{
+			"path": "pages/index/calendar",
+			"style": {
+				"navigationBarTitleText": "数据中心"
+			}
+		},
+		{
+			"path": "pages/index/calendarExpert",
+			"style": {
+				"navigationBarTitleText": "病历审核"
+			}
+		},
+		{
+			"path": "pages/doctor/upload",
+			"style": {
+				"navigationBarTitleText": "病例审核"
+			}
+		},
+		{
+			"path": "pages/doctor/uploadExpert",
+			"style": {
+				"navigationBarTitleText": "病例详情"
+			}
+		},
+		{
+			"path": "pages/doctor/words",
+			"style": {
+				"navigationBarTitleText": "文字识别"
+			}
+		},
+		{
+			"path": "pages/index/my",
+			"style": {
+				"navigationBarTitleText": "个人中心",
+				"navigationStyle":"custom"
+			}
+		},
+		{
+			"path": "pages/user/password",
+			"style": {
+				"navigationBarTitleText": "修改密码"
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "登录",
+				"navigationStyle":"custom"
+			}
+		},
+		{
+			"path": "pages/login/perfect",
+			"style": {
+				"navigationBarTitleText": "完善信息",
+				"navigationStyle":"custom"
+			}
+		},
+		{
+			"path": "pages/login/agreement",
+			"style": {
+				"navigationBarTitleText": "病历评比用户隐私协议"
+			}
+		},
+		{
+			"path": "pages/login/forget",
+			"style": {
+				"navigationBarTitleText": "忘记密码",
+				"navigationStyle":"custom"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "钉钉",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"backgroundColor": "#F2F3F5"
+		// "maxWidth": 400,
+		// "rpxCalcMaxDeviceWidth":400,
+		// "rpxCalcBaseDeviceWidth":400,
+		// "rpxCalcIncludeWidth":400
+	}
+}

+ 1096 - 0
pages/doctor/upload.vue

@@ -0,0 +1,1096 @@
+<template>
+	<view class="upload" @click="uploadMask()">
+		<u-toast ref="uToast" />
+		<view>
+			<view class="title" v-if="htmlType==0">
+				<u-input v-model="FormData.title" :disabled="ishtmlType" placeholder-style="color: rgba(0,0,0,0.8);font-size:48rpx;font-weight: 700;" :clearable="false" type="text" :border="border" placeholder="添加标题名称" />
+			</view>
+			<view class="title" v-else>
+				<view style="font-size: 48rpx;color: rgba(0,0,0,0.8);">{{FormData.title}}</view>
+			</view>
+			<view class="flex" v-if="htmlType==0">
+				<view v-if="FormData.uploadingName!=''" class="upData flex" @click.stop="Dropdown=true">
+					{{FormData.uploadingName}}
+					<u-icon name="arrow-right" size="28" color="#2979ff" style="margin-left: 10rpx;"></u-icon>
+				</view>
+				<view v-else class="upData flex" @click.stop="Dropdown=true">
+					选择上传分类
+					<u-icon name="arrow-right" size="28" color="#2979ff" style="margin-left: 10rpx;"></u-icon>
+				</view>
+				<view class="Dropdown" v-if="Dropdown">
+					<view class="li">选择上传分类<u-icon name="arrow-down"></u-icon></view>
+					<view :class="item == DropdownIndex?'li active':'li'" v-for="(item,index) in getUploading" @click.stop="DropdownMeh(item.name)">{{item.name}}</view>
+				</view>
+			</view>
+			<view class="flex" v-else>
+				<view class="upData flex">
+					{{FormData.uploadingName}}
+				</view>
+			</view>
+			<!-- 补充资料 -->
+			<view class="box" v-if="htmlType == 1">
+				<view class="input">
+					<view class="label flex numbers">补充资料<view class="msg" style="margin: 0; padding: 0 12rpx 0;">PPT、视频</view>
+					</view>
+					<view :class="format(item)?'list flex': 'list list1 flex'" v-for="(item,index) in mhFileTwo" :key="index">
+						<image v-if="format(item)" src="../../static/img/P.png" mode=""></image>
+						<image v-else src="../../static/img/V.png" mode=""></image>
+						<text>{{ item }}</text>
+						<u-icon name="close" @click="deFile(index,2)" color="rgba(0,0,0,0.15)"></u-icon>
+					</view>
+					<view class="upButton" @click.stop="chooseMessageFile(2)">
+						<image src="../../static/img/upButton.png" mode=""></image>上传新文件
+					</view>
+				</view>
+				<view class="template" @click="downloadFile(ppt)">下载PPT模板(<text class="herf">{{ppt}}</text>)</view>
+			</view>
+			<view class="box" v-if="uploadData.type == 2">
+				<view class="tit_box">通过原因</view>
+				<text class="msg">{{uploadData.zjspPass}}</text>
+			</view>
+			<view class="box" v-if="uploadData.type == 1">
+				<view class="tit_box" style="color: #F02E2F;">不通过原因</view>
+				<text class="msg">{{uploadData.zjspNotpass}}</text>
+			</view>
+			<view class="box">
+				<view class="tit">患者信息</view>
+				<view class="input flex">
+					<view class="icon">姓名</view>
+					<u-input v-model="FormData.name" :disabled="ishtmlType" :clearable="false" type="text" :border="border" placeholder="患者姓名" />
+				</view>
+				<view class="input flex">
+					<view class="icon">省份</view>
+					<view class="text" :style="FormData.province?'flex: 1;':'flex: 1;color:rgba(0, 0, 0, 0.35);'" @click="provinceMet()">
+						{{FormData.province?FormData.province:'请选择省份'}}
+					</view>
+					<u-select v-model="provinceType" :list="provinceList" @confirm="provinceConfirm"></u-select>
+					<!-- <u-input v-model="FormData.province" :disabled="ishtmlType" :clearable="false" type="text" :border="border" placeholder="请输入省份" /> -->
+				</view>
+				<view class="input flex">
+					<view class="icon">城市</view>
+					<view class="text" @click="cityMet()" :style="FormData.city?'flex: 1;':'flex: 1;color:rgba(0, 0, 0, 0.35);'">
+						{{FormData.city?FormData.city:'请选择城市'}}
+					</view>
+					<u-select v-model="cityType" :list="cityList" @confirm="cityConfirm"></u-select>
+					<!-- <u-input v-model="FormData.city" :disabled="ishtmlType" :clearable="false" type="text" :border="border" placeholder="请输入城市" /> -->
+				</view>
+				<view class="input flex inputBorNode">
+					<view class="icon">医院</view>
+					<u-input v-model="FormData.hospital" :disabled="ishtmlType" :clearable="false" type="text" :border="border" placeholder="目前所处医院名称" />
+				</view>
+			</view>
+			<view class="box">
+				<view class="tit">病例详情</view>
+				<view class="input flex" v-if="wisBllx == '1'">
+					<view class="icon">病例类型</view>
+					<view class="classType" v-if="FormData.caseName!=''" style="color:#167FFF;" @click.stop="!ishtmlType?selectType=true:''">{{FormData.caseName}}</view>
+					<view v-else style="font-size: 28rpx;color: rgba(0,0,0,0.35);flex: 1;" @click.stop="!ishtmlType?selectType=true:''">请选择病病例类型</view>
+					<!-- <u-input  :disabled="true" :clearable="false" type="number" :border="border" placeholder="请选择病病例类型" /> -->
+					<image v-if="FormData.caseName ==''" src="../../static/img/r.png" mode=""></image>
+					<view class="select Dropdown" v-if="selectType">
+						<view v-for="(val,i) in itemList" @click.stop="selectMeh(val.name)" :class="selectIndex == val.name?'li active': 'li'" :key="i">{{val.name}}<u-icon style="float: right;" v-if="i==0" name="arrow-down"></u-icon></view>
+					</view>
+				</view>
+				<view class="input">
+					<view class="label">备注</view>
+					<u-input v-model="FormData.remark" :disabled="ishtmlType" :clearable="false" type="text" :border="border" placeholder="详细信息" />
+				</view>
+				<view class="input">
+					<view class="label flex numbers">相关照片<view class="msg" style="margin: 0; padding: 0 12rpx 0;">最多可上传十张图片</view>
+					</view>
+					<u-upload ref="uUpload1" width="160rpx" height="160rpx" :show-progress="true" :deletable="htmlType == 0 ? true : false" :action="action" @on-change="progress1" upload-text=" " :max-count="maxCount" :file-list="fileList"></u-upload>
+				</view>
+				<view class="input">
+					<view class="label flex numbers">相关文件<view class="msg" style="margin: 0; padding: 0 12rpx 0;">PPT、视频</view>
+					</view>
+					<view :class="format(item)?'list flex': 'list list1 flex'" v-for="(item,index) in FormData.mhFile" :key="index">
+						<image v-if="format(item)" src="../../static/img/P.png" mode=""></image>
+						<image v-else src="../../static/img/V.png" mode=""></image>
+						<text>{{ item }}</text>
+						<u-icon name="close" color="rgba(0,0,0,0.15)" v-if="!ishtmlType" @click="deFile(index)"></u-icon>
+					</view>
+					<view v-if="htmlType == 0" class="upButton" @click.stop="chooseMessageFile()">
+						<image src="../../static/img/upButton.png" mode=""></image>上传新文件
+					</view>
+				</view>
+				<view class="template" v-if="htmlType == 0">下载PPT模板(<text class="herf" v-for="(a,b) in ppt.split(',')" :key="b" @click="downloadFile(a)">{{a}},</text>)</view>
+			</view>
+		</view>
+		<u-popup v-model="show" mode="center">
+			<view class="suc">
+				<image src="../../static/img/suc.png" mode=""></image>
+				<view class="con">
+					<view class="tit">上传成功</view>
+					<view class="p">患者资料上传成功,是否继续上传</view>
+					<view class="btn">
+						<view @click="indexHerf()">返回首页</view>
+						<view class="next" @click.stop="nextMeh()">
+							继续上传
+						</view>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+		<view class="uploadBtn" v-if="htmlType!=2">
+			<view class="yes" v-if="yesType==1" @click="patientQuide()">提交案例</view>
+			<view class="yes" v-else @click="patientQuide()">重新提交</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	const cityData = require('@/https/data.js')
+	export default {
+		data() {
+			return {
+				optionId: '',
+				// 自定义界面状态 0:第一次提交 1:二次上传 2:信息查询 
+				htmlType: 0,
+				ishtmlType: false, //是否可输入
+				// 演示地址,请勿直接使用
+				action: 'http://www.example.com/upload',
+				fileList: [],
+				Dropdown: false,
+				DropdownIndex: 0,
+				selectType: false,
+				selectIndex: 0,
+				itemList: [],
+				show: false, //成功弹框
+				getUploading: [], //上传分类
+				// 第一次上传所需要值
+				FormData: {
+					title: '', //标题
+					uploadingName: '', //分类名称	
+					name: '', //姓名
+					city: '', //城市
+					province: '', //省份
+					hospital: '', //医院名称
+					caseName: '', //病历类型	
+					remark: '', //备注
+					pic: [], //上传图片
+					mhFile: [], //上传文件
+					mhFileName: [], //文件名称(小程序使用)
+				},
+				border: false,
+				mhFileTwo: [], //二次上传
+				mhFileTwoName: [], //二次上传名称
+				uploadData: {
+					type: 0
+				},
+				ppt: '',
+				yesType: 1, //显示 提交按钮 还是重新提交
+				maxCount: 10, //上传图片数量限制
+
+				provinceType: false,
+				provinceList: [],
+				cityType: false,
+				cityList: [],
+				wisBllx: ''
+			}
+		},
+		onLoad(option) {
+			console.log(cityData.data)
+			this.provinceList = cityData.data;
+
+			uni.setNavigationBarTitle({
+				title: '上传新病例'
+			});
+			this.action = this.$httpUrl + '/api/uploadLocality'
+			if (option.id) {
+				this.optionId = option.id
+				if (uni.getStorageSync('token') && option.id == JSON.parse(uni.getStorageSync('uploadData')).id) {
+					this.uploadData = JSON.parse(uni.getStorageSync('uploadData'))
+					// 是否专家分配
+					if (this.uploadData.allocation == 2) {
+						// 二次上传完 展示详情
+						this.htmlType = 2
+						this.ishtmlType = true
+					} else {
+						if (this.uploadData.firstTrial == 2 && this.uploadData.isSecondaryUpload == 2 && this.uploadData.secondaryUpload == 1) {
+							// 通过初审 并且 未二次上传
+							this.htmlType = 1
+							this.ishtmlType = true
+						} else if ((this.uploadData.firstTrial == 2 && this.uploadData.isSecondaryUpload != 1) || (this.uploadData.firstTrial == 2 && this.uploadData.isSecondaryUpload == 1 && this.uploadData.secondaryUpload == 2)) {
+							// 二次上传完 展示详情
+							this.htmlType = 2
+							this.ishtmlType = true
+						} else if (this.uploadData.firstTrial == 1) {
+							// 未通过重新上传
+							this.htmlType = 0
+							uni.setNavigationBarTitle({
+								title: '重新编辑'
+							});
+							this.yesType = 2
+						} else if (this.uploadData.firstTrial == 0) {
+							// 未审核
+							this.htmlType = 2
+							this.ishtmlType = true
+						}
+					}
+					console.log(this.htmlType)
+					this.initDataTwo()
+				}
+			}
+			this.getUploadingName()
+			this.getCaseName()
+			this.configInfo()
+			// console.log(JSON.parse(uni.getStorageSync('uploadData')))
+		},
+		methods: {
+			provinceConfirm(e) {
+				console.log(e);
+				this.FormData.province = e[0].label
+				this.cityList = e[0].value
+			},
+			provinceMet() {
+				if (this.ishtmlType) {
+					return
+				}
+				this.provinceType = true
+			},
+			cityConfirm(e) {
+				this.FormData.city = e[0].label
+			},
+			cityMet() {
+				if (this.ishtmlType) {
+					return
+				}
+				if (this.cityList.length != 0) {
+					this.cityType = true
+				} else {
+					this.$refs.uToast.show({
+						title: '请先选择省份',
+						type: 'error ',
+					})
+				}
+			},
+			format(str) {
+				if (str.indexOf('ppt') != -1) {
+					return true
+				} else {
+					return false
+				}
+			},
+			// 二次上传赋值
+			initDataTwo() {
+				console.log(this.FormData, this.uploadData);
+				for (let key in this.FormData) {
+					if (key == 'pic') {
+						if (this.uploadData[key] == '') {
+							return
+						}
+						// 相关图片处理
+						let arr = this.uploadData.picTwo.split(',')
+						this.FormData[key] = arr
+						// this.maxCount = 10 - arr.length
+						arr.forEach(item => {
+							this.fileList.push({
+								url: item
+							})
+						})
+					} else if (key == 'mhFile') {
+						if (this.uploadData[key] != '' && this.uploadData[key] != null) {
+							if (this.uploadData.secondaryUploadFile != '' && this.uploadData.secondaryUploadFile != null) {
+								this.FormData[key] = this.uploadData[key].split(',').concat(this.uploadData.secondaryUploadFile.split(','))
+							} else {
+								// 相关文件处理
+								this.FormData[key] = this.uploadData[key].split(',')
+
+							}
+						}
+					} else if (key == 'mhFileName') {
+						if (this.uploadData[key] != '' && this.uploadData[key] != null) {
+							if (this.uploadData.secondaryUploadFileName != '' && this.uploadData.secondaryUploadFileName != null) {
+								this.FormData[key] = this.uploadData[key].split(',').concat(this.uploadData.secondaryUploadFileName.split(','))
+							} else {
+								// 相关文件处理
+								this.FormData[key] = this.uploadData[key].split(',')
+
+							}
+						}
+					} else {
+						this.FormData[key] = this.uploadData[key]
+					}
+				}
+				console.log(this.FormData)
+			},
+			chooseMessageFile(type) {
+				let this_ = this;
+				wx.chooseMessageFile({
+					count: 10,
+					type: 'all',
+					success(res) {
+						console.log(res)
+						const tempFilePaths = res.tempFiles
+						for (let i = 0; i < tempFilePaths.length; i++) {
+							if (tempFilePaths[i].path.indexOf('ppt') == -1 && tempFilePaths[i].path.indexOf('mp4') == -1) {
+								this_.$refs.uToast.show({
+									title: '请上传ppt或mp4格式.',
+									type: 'error ',
+								})
+							} else {
+								this_.uploadFile(tempFilePaths[i], type)
+							}
+						}
+
+					}
+				})
+			},
+			// 微信上传
+			uploadFile(val, type) {
+				wx.uploadFile({
+					url: this.$httpUrl + '/api/uploadLocality2/' + val.name, //仅为示例,非真实的接口地址
+					filePath: val.path,
+					name: 'file',
+					formData: {
+						'file': val,
+						'name': val.name,
+					},
+					success: (uploadFileRes) => {
+						let data = JSON.parse(uploadFileRes.data)
+						if (data.code == 200) {
+							this.$refs.uToast.show({
+								title: '上传成功',
+								type: 'success',
+							})
+							if (type == 2) {
+								// 补充资料上传
+								this.mhFileTwo.push(data.result)
+								this.mhFileTwoName.push(val.name)
+							} else {
+								// 第一次上传
+								this.FormData.mhFile.push(data.result)
+								this.FormData.mhFileName.push(val.name)
+							}
+						}
+					},
+					fail(err) {
+						console.log(err)
+					},
+				});
+			},
+			// 上传接口
+			patientQuide() {
+				console.log(this.$refs.uUpload1.lists);
+				if (this.optionId == '') {
+					// 第一次上传
+					if (this.FormData.title == '') {
+						this.$refs.uToast.show({
+							title: '请输入标题',
+							type: 'error ',
+						})
+						return;
+					}
+					if (this.FormData.uploadingName == '') {
+						this.$refs.uToast.show({
+							title: '请输入上传分类',
+							type: 'error ',
+						})
+						return;
+					}
+
+					if (this.FormData.name == '') {
+						this.$refs.uToast.show({
+							title: '请输入姓名',
+							type: 'error ',
+						})
+						return;
+					}
+					if (this.FormData.province == '') {
+						this.$refs.uToast.show({
+							title: '请输入省份',
+							type: 'error ',
+						})
+						return;
+					}
+					if (this.FormData.city == '') {
+						this.$refs.uToast.show({
+							title: '请输入城市',
+							type: 'error ',
+						})
+						return;
+					}
+					if (this.FormData.hospital == '') {
+						this.$refs.uToast.show({
+							title: '请输入医院',
+							type: 'error ',
+						})
+						return;
+					}
+					if (this.wisBllx == 1) {
+						if (this.FormData.caseName == '') {
+							this.$refs.uToast.show({
+								title: '请输入病例类型',
+								type: 'error ',
+							})
+							return;
+						}
+					}
+
+					let files1 = [];
+					// 通过filter,筛选出上传进度为100的文件(因为某些上传失败的文件,进度值不为100,这个是可选的操作)
+					files1 = this.$refs.uUpload1.lists.filter(val => {
+						return val.progress == 100;
+					})
+					console.log(files1)
+					if (files1.length == 0) {
+						this.$refs.uToast.show({
+							title: '请上传相关照片',
+							type: 'error ',
+						})
+						return;
+					}
+					let values = []
+					files1.forEach(item => {
+						if (item.response) {
+							values.push(item.response.result)
+						} else {
+							values.push(item.url)
+						}
+					})
+
+					// let values = files1.map(item => item.response.result)
+					this.FormData.pic = values.join(',')
+					if (this.FormData.mhFile instanceof Array) {
+						this.FormData.mhFile = this.FormData.mhFile.join(',')
+					}
+					if (this.FormData.mhFileName instanceof Array) {
+						this.FormData.mhFileName = this.FormData.mhFileName.join(',')
+					}
+					uni.showLoading({
+						mask: true,
+						title:'请稍后'
+					})
+					this.$http.patientQuide(this.FormData).then(res => {
+						uni.hideLoading()
+						if (res.data.code == 200) {
+							console.log(res.data)
+							if (this.htmlType == 0) {
+								this.show = true;
+							} else {
+								this.$refs.uToast.show({
+									title: '上传成功',
+									type: 'success',
+								})
+								uni.navigateTo({
+									url: '/pages/index/calendar',
+								})
+							}
+						} else {
+							this.$refs.uToast.show({
+								title: res.data.message,
+								type: 'error ',
+							})
+						}
+					})
+				} else {
+					// 审核失败
+					if (this.uploadData.firstTrial == 1) {
+						let data = JSON.parse(JSON.stringify(this.FormData))
+						data['firstTrial'] = '0';
+						data['id'] = this.uploadData.id;
+						if (data.title == '') {
+							this.$refs.uToast.show({
+								title: '请输入标题',
+								type: 'error ',
+							})
+							return;
+						}
+						if (data.uploadingName == '') {
+							this.$refs.uToast.show({
+								title: '请输入上传分类',
+								type: 'error ',
+							})
+							return;
+						}
+						if (data.name == '') {
+							this.$refs.uToast.show({
+								title: '请输入姓名',
+								type: 'error ',
+							})
+							return;
+						}
+						if (data.city == '') {
+							this.$refs.uToast.show({
+								title: '请输入城市',
+								type: 'error ',
+							})
+							return;
+						}
+						if (data.province == '') {
+							this.$refs.uToast.show({
+								title: '请输入省份',
+								type: 'error ',
+							})
+							return;
+						}
+						if (data.hospital == '') {
+							this.$refs.uToast.show({
+								title: '请输入医院',
+								type: 'error ',
+							})
+							return;
+						}
+						if (this.wisBllx == 1) {
+							if (data.caseName == '') {
+								this.$refs.uToast.show({
+									title: '请输入病例类型',
+									type: 'error ',
+								})
+								return;
+							}
+						}
+
+						let files1 = [];
+						// 通过filter,筛选出上传进度为100的文件(因为某些上传失败的文件,进度值不为100,这个是可选的操作)
+						files1 = this.$refs.uUpload1.lists.filter(val => {
+							return val.progress == 100;
+						})
+						console.log(files1)
+						if (files1.length == 0) {
+							this.$refs.uToast.show({
+								title: '请上传相关照片',
+								type: 'error ',
+							})
+							return;
+						}
+						let values = []
+						files1.forEach(item => {
+							if (item.response) {
+								values.push(item.response.result)
+							} else {
+								values.push(item.url)
+							}
+						})
+						// let values = files1.map(item => item.response.result)
+						data.pic = values.join(',')
+						data.picTwo = values.join(',')
+						if (data.mhFile instanceof Array) {
+							data.mhFile = data.mhFile.join(',')
+						}
+						if (data.mhFileName instanceof Array) {
+							data.mhFileName = data.mhFileName.join(',')
+						}
+						uni.showLoading({
+							mask: true,
+							title:'请稍后'
+						})
+						this.$http.patientQuideEdit(data).then(res => {
+							console.log(res)
+							uni.hideLoading()
+							if (res.data.code == 200) {
+								this.$refs.uToast.show({
+									title: '提交成功',
+								})
+								uni.navigateTo({
+									url: '/pages/index/calendar',
+								})
+							} else {
+								this.$refs.uToast.show({
+									title: res.data.message,
+									type: 'error ',
+								})
+							}
+						})
+					} else {
+						// 二次上传
+						if (this.mhFileTwo == '') {
+							this.$refs.uToast.show({
+								title: '请上传补充资料',
+								type: 'error ',
+							})
+							return;
+						}
+						let files1 = [];
+						// 通过filter,筛选出上传进度为100的文件(因为某些上传失败的文件,进度值不为100,这个是可选的操作)
+						files1 = this.$refs.uUpload1.lists.filter(val => {
+							return val.progress == 100;
+						})
+						console.log(files1)
+						if (files1.length == 0) {
+							this.$refs.uToast.show({
+								title: '请上传相关照片',
+								type: 'error ',
+							})
+							return;
+						}
+						let values = []
+						files1.forEach(item => {
+							if (item.response) {
+								values.push(item.response.result)
+							} else {
+								values.push(item.url)
+							}
+						})
+						// let values = files1.map(item => item.response.result)
+						this.FormData.pic = values.join(',')
+						this.FormData.picTwo = values.join(',')
+						if (this.FormData.mhFile instanceof Array) {
+							this.FormData.mhFile = this.FormData.mhFile.join(',')
+						}
+						if (this.FormData.mhFileName instanceof Array) {
+							this.FormData.mhFileName = this.FormData.mhFileName.join(',')
+						}
+						// 二次上传
+						if (this.htmlType == 1) {
+							let datas = {
+								id: this.optionId,
+								secondaryUpload: 2,
+								secondaryUploadFileName: this.mhFileTwoName.join(','),
+								secondaryUploadFile: this.mhFileTwo.join(',')
+							}
+							console.log(datas)
+							uni.showLoading({
+								mask: true,
+								title:'请稍后'
+							})
+							this.$http.patientQuideEdit(datas).then(res => {
+								uni.hideLoading()
+								console.log(res)
+								if (res.data.code == 200) {
+									this.$refs.uToast.show({
+										title: '提交成功',
+									})
+									uni.navigateTo({
+										url: '/pages/index/calendar',
+									})
+								} else {
+									this.$refs.uToast.show({
+										title: res.data.message,
+										type: 'error ',
+									})
+								}
+							})
+						}
+					}
+				}
+				// if(this.htmlType!=0){
+				// 	// 二次上传 补充资料
+				// 	if(this.FormData.mhFile instanceof Array){
+				// 		this.FormData.mhFile = this.FormData.mhFile.concat(this.mhFileTwo).join(',')
+				// 	}
+				// }else{
+				// 	if(this.FormData.mhFile instanceof Array){
+				// 		this.FormData.mhFile = this.FormData.mhFile.join(',')
+				// 	}
+				// }
+				console.log(this.FormData)
+
+			},
+			// 继续提交
+			nextMeh() {
+				console.log('继续提交')
+				this.show = false
+
+				this.fileList = []
+				this.$refs.uUpload1.clear()
+				this.FormData = {
+					title: '', //标题
+					uploadingName: '', //分类名称	
+					name: '', //姓名
+					city: '', //城市
+					province: '', //省份
+					hospital: '', //医院名称
+					caseName: '', //病历类型	
+					remark: '', //备注
+					pic: [], //上传图片
+					mhFile: [], //上传文件
+					mhFileName: [], //文件名称(小程序使用)
+				}
+			},
+			uploadMask() {
+				console.log('false')
+				this.Dropdown = false;
+				this.selectType = false;
+			},
+			// 上传分类
+			getUploadingName() {
+				this.$http.getUploadingName()
+					.then(res => {
+						if (res.data.code == 200) {
+							this.getUploading = res.data.result
+							// this.DropdownIndex=res.data.result[0].id
+						}
+					})
+			},
+			// 病历分类
+			getCaseName() {
+				this.$http.getCaseName()
+					.then(res => {
+						if (res.data.code == 200) {
+							this.itemList = res.data.result
+						}
+					})
+			},
+			// 系统设置
+			configInfo() {
+				this.$http.configInfo()
+					.then(res => {
+						if (res.data.code == 200) {
+							console.log(res)
+							// console.log(this.data)
+							this.ppt = res.data.result.ppt
+							this.wisBllx = res.data.result.wisBllx
+
+						}
+					})
+			},
+			// 下载文件
+			downloadFile(url) {
+				uni.showLoading({
+					title: '正在下载……'
+				});
+				uni.downloadFile({
+					url: url,
+					success: (data) => {
+						console.log('打印data', data)
+						if (data.statusCode === 200) {
+							console.log('下载成功');
+							//隐藏加载框
+							uni.hideLoading();
+							//文件保存到本地
+							uni.saveFile({
+								tempFilePath: data.tempFilePath, //临时路径
+								success: function(res) {
+									// console.log('打印res',res)
+									uni.showToast({
+										icon: 'success',
+										mask: true,
+										// title: '文件已保存:' + res.savedFilePath, //保存路径
+										title: '下载成功',
+										duration: 2000,
+									});
+
+									//自动打开文档查看
+									setTimeout(() => {
+										var filePath = res.savedFilePath;
+										uni.openDocument({ //新开页面打开文档,支持格式:doc, xls, ppt, pdf, docx, xlsx, pptx。
+											filePath: filePath,
+											showMenu: true,
+											success: function(res) {
+												console.log('打开文档成功');
+											}
+										});
+									}, 1000)
+								},
+							});
+						}
+					}
+				});
+			},
+			// 上传分类选择
+			DropdownMeh(item) {
+				this.FormData.uploadingName = item;
+				this.DropdownIndex = item;
+				this.Dropdown = false;
+			},
+			selectMeh(item) {
+				this.FormData.caseName = item;
+				this.selectIndex = item;
+				this.selectType = false;
+
+			},
+			// 图片上传失败 清空
+			progress1(res, index, lists, name) {
+				// console.log(JSON.parse(res.data))
+				if (JSON.parse(res.data).code == 0) {
+					this.$refs.uToast.show({
+						title: JSON.parse(res.data).msg,
+						type: 'error ',
+					})
+					// this.$refs.uUpload1.clear()
+				}
+			},
+			// 删除相关文件
+			deFile(index, type) {
+				if (type == 2) {
+					this.mhFileTwo.splice(index, 1)
+					this.mhFileTwoName.splice(index, 1)
+				} else {
+					this.FormData.mhFile.splice(index, 1)
+					this.FormData.mhFileName.splice(index, 1)
+				}
+			},
+			// 返回首页
+			indexHerf() {
+				uni.navigateTo({
+					url: '/pages/index/index',
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.upload {
+		padding: 32rpx 32rpx 120rpx;
+
+		.title {
+			input {
+				font-size: 48rpx;
+				color: #000;
+				padding-bottom: 24rpx;
+			}
+		}
+
+		.flex {
+			position: relative;
+
+			.Dropdown {
+				position: absolute;
+				top: 0;
+				z-index: 10;
+				width: 100%;
+				padding: 24rpx 36rpx 0;
+				background-color: #fff;
+				box-shadow: 0px 2rpx 20rpx 0px rgba(0, 0, 0, 0.1);
+				border-radius: 16rpx;
+				font-size: 28rpx;
+				color: rgba(0, 0, 0, 0.8);
+
+				.li {
+					margin-bottom: 28rpx;
+				}
+
+				.active {
+					color: $color;
+				}
+			}
+		}
+
+		.upData {
+			display: inline-block;
+			padding: 4rpx 24rpx;
+			background: rgba(22, 127, 255, 0.1);
+			border-radius: 24rpx;
+			font-size: 28rpx;
+			color: $color;
+
+			image {
+				width: 32rpx;
+				height: 32rpx;
+				margin-left: 12rpx;
+			}
+		}
+
+		.box {
+			box-sizing: border-box;
+			padding: 32rpx 20rpx 0;
+			background-color: #fff;
+			border-radius: 24rpx;
+			margin-top: 24rpx;
+
+			.tit_box {
+				font-size: 32rpx;
+				color: $color;
+			}
+
+			.msg {
+				display: block;
+				margin-top: 24rpx;
+				font-size: 28rpx;
+				color: #000000;
+				padding-bottom: 32rpx;
+			}
+
+			.tit {
+				font-weight: 700;
+				font-size: 32rpx;
+				color: rgba(0, 0, 0, 0.8);
+			}
+
+			.input {
+				position: relative;
+
+				.u-upload {
+					.u-list-item {
+						width: 180rpx !important;
+						height: 180rpx !important;
+					}
+				}
+
+				.label {}
+
+				.icon {
+					width: 140rpx;
+					color: rgba(0, 0, 0, 0.8);
+				}
+
+				.msg {
+					color: rgba(0, 0, 0, 0.35);
+					margin-left: 10rpx;
+				}
+
+				.list {
+					padding: 20rpx;
+					background: rgba(227, 66, 33, 0.1);
+					border-radius: 24rpx;
+					font-size: 28rpx;
+					color: #000000;
+					margin-top: 20rpx;
+
+					image {
+						width: 40rpx;
+						height: 40rpx;
+						margin-right: 20rpx;
+					}
+
+					text {
+						flex: 1;
+						white-space: nowrap;
+						text-overflow: ellipsis;
+						overflow: hidden;
+						word-break: break-all;
+					}
+				}
+
+				.list1 {
+					background: rgba(22, 127, 255, 0.1);
+				}
+
+				u-input {
+					flex: 1;
+				}
+
+				image {
+					width: 32rpx;
+					height: 32rpx;
+				}
+
+				.upButton {
+					padding: 24rpx 0;
+					text-align: center;
+					background: #F3F3F3;
+					border-radius: 24rpx;
+					color: rgba(0, 0, 0, 0.35);
+					font-size: 32rpx;
+					margin-top: 20rpx;
+
+					image {
+						vertical-align: middle;
+					}
+				}
+
+				.select {
+					position: absolute;
+					top: 10rpx;
+					z-index: 10;
+					right: 0;
+					width: 550rpx;
+					padding: 24rpx 36rpx 0;
+					background-color: #fff;
+					box-shadow: 0px 2rpx 20rpx 0px rgba(0, 0, 0, 0.1);
+					border-radius: 16rpx;
+					font-size: 28rpx;
+					color: rgba(0, 0, 0, 0.8);
+				}
+			}
+
+			.input:last-child {
+				border-bottom: 1px solid transparent;
+			}
+		}
+
+		.template {
+			padding: 24rpx 0;
+			font-size: 28rpx;
+			color: #000;
+
+			.herf {
+				color: $color;
+			}
+		}
+
+		.suc {
+			position: relative;
+			text-align: center;
+			width: 520rpx;
+			height: 510rpx;
+			margin: 0 auto;
+
+			image {
+				position: absolute;
+				left: 0;
+				top: 0;
+				width: 100%;
+				height: 510rpx;
+			}
+
+			.con {
+				position: relative;
+				z-index: 11;
+				text-align: center;
+				box-sizing: border-box;
+				padding: 0 40rpx;
+				overflow: hidden;
+
+				.tit {
+					font-size: 36rpx;
+					color: $color;
+					padding-top: 190rpx;
+				}
+
+				.p {
+					width: 230rpx;
+					margin: 32rpx auto 50rpx;
+					font-size: 28rpx;
+					color: rgba(0, 0, 0, 0.6);
+				}
+
+				.btn {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					justify-content: space-between;
+
+					view {
+						width: 208rpx;
+						height: 56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						border: 2rpx solid rgba(0, 0, 0, 0.35);
+						font-size: 28rpx;
+						color: rgba(0, 0, 0, 0.35);
+						border-radius: 40rpx;
+					}
+
+					.next {
+						background-color: $color;
+						color: #fff;
+						border: 2rpx solid $color;
+					}
+				}
+			}
+		}
+
+		.u-mode-center-box {
+			background-color: transparent !important;
+		}
+
+		.uploadBtn {
+			position: fixed;
+			bottom: 0;
+			left: 0;
+			width: 100%;
+			box-sizing: border-box;
+			padding: 12rpx 32rpx;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			justify-content: space-between;
+			font-size: 32rpx;
+			color: #fff;
+			background-color: #fff;
+			z-index: 11;
+
+			.yes {
+				width: 100%;
+				box-sizing: border-box;
+				border-radius: 48rpx;
+				padding: 20rpx 0;
+				text-align: center;
+				background-color: $color;
+			}
+		}
+	}
+</style>

+ 582 - 0
pages/doctor/uploadExpert.vue

@@ -0,0 +1,582 @@
+<template>
+	<view class="uploadExpert">
+		<u-toast ref="uToast" />
+		<view class="title">{{patient.title}}</view>
+		<view class="flex">
+			<view class="upData flex">
+				{{patient.caseName}}
+			</view>
+		</view>
+		<view class="box" v-if="patient.type==1">
+			<view class="tit_box" style="color: #FF5E59;">不通过原因</view>
+			<text class="msg">{{patient.zjspNotpass}}</text>
+		</view>
+		<view class="box" v-else-if="patient.type==2">
+			<view class="tit_box">通过原因</view>
+			<text class="msg">{{patient.zjspPass}}</text>
+		</view>
+		<view class="box">
+			<view class="tit">患者信息</view>
+			<view class="input flex">
+				<view class="icon">姓名</view>
+				<text>{{EncryptName(patient.name)}}</text>
+			</view>
+			<view class="input flex">
+				<view class="icon">城市</view>
+				<text>{{patient.city}}</text>
+			</view>
+			<view class="input flex">
+				<view class="icon">医院</view>
+				<text>{{patient.hospital}}</text>
+			</view>
+		</view>
+		<view class="box">
+			<view class="tit">病例详情</view>
+			<view class="input flex">
+				<view class="icon">病例类型</view>
+				<text style="color:#167FFF">{{patient.caseName}}</text>
+			</view>
+			<view class="input">
+				<view class="label">备注</view>
+				<text>{{patient.remark}}</text>
+			</view>
+			<view class="input">
+				<view class="label flex numbers" style="margin-bottom: 24rpx;">相关照片</view>
+				<view class="imgList" v-for="(item,index) in pic" :key="index" >
+					<image :src="item" mode="" @click="previewImage(item)"></image>
+					<view class="text" v-if="patient.picWordRecognition" @click="words()">
+						{{strArr[index].slice(0,70)}}...
+						<text>更多</text>
+					</view>
+				</view>
+			</view>
+			<view class="input">
+				<view class="label flex numbers">相关文件</view>
+				<view v-for="(item,index) in mhFile" :key="index">
+					<view class="list flex">
+						<image v-if="format(item)" src="../../static/img/P.png" mode=""></image>
+						<image v-else src="../../static/img/V.png" mode=""></image>
+						<text>{{mhFileName[index]}}</text>
+					</view>
+					<view class="template" @click="downloadFile(item)">下载地址(<text class="herf">{{item}}</text>)</view>
+				</view>
+			</view>
+		</view>
+		<view class="uploadBtn" v-if="patient.type==0">
+			<view class="no" @click="showType(1)">不通过</view>
+			<view class="yes" @click="showType(2)">通过</view>
+		</view>
+		<u-mask :show="show" @click="show = false">
+		</u-mask>
+		<view class="popup" v-if="show" :style="'bottom:'+KeyboardHeight+'px;'">
+			<view class="tit" v-if="type==2">通过原因</view>
+			<view class="tit" v-else>不通过原因</view>
+			<textarea v-model="value" type="textarea" :border="false" :show-confirm-bar="false" :clearable="false" :adjust-position="false" @focus="inputBindFocus" @blur="inputBindBlur"/>
+			<view class="btn" v-if="type==2" @click="patientQuideEdit()">确定通过</view>
+			<view class="btn" v-else @click="patientQuideEdit()">确定不通过</view>
+		</view>
+			
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				// 演示地址,请勿直接使用
+				action: 'http://www.example.com/upload',
+				pic:[],
+				mhFile:[],
+				mhFileName:[],
+				strArr:[],
+				show:false,
+				value:'',//审批理由
+				patient:{},
+				type:'',//专家是否通过 0未审理 1没通过 2通过了
+				id:'',
+				KeyboardHeight:''
+			}
+		},
+		onLoad(option) {
+			console.log(this.format())
+			if(option.id){
+				this.id=option.id
+				if(uni.getStorageSync('patient') && option.id == JSON.parse(uni.getStorageSync('patient')).id){
+					this.patient = JSON.parse(uni.getStorageSync('patient'))
+					console.log(this.patient)
+					if(this.patient.pic != ''){
+						this.pic = this.patient.pic.split(',')
+					}
+					// 文件回显
+					if(this.patient.mhFile && this.patient.secondaryUploadFile){
+						this.mhFile = this.patient.mhFile.split(',').concat(this.patient.secondaryUploadFile.split(','))
+					}else{
+						if(this.patient.mhFile){
+							this.mhFile = this.patient.mhFile.split(',')
+						}
+						if(this.patient.secondaryUploadFile){
+							this.mhFile = this.patient.secondaryUploadFile.split(',')
+						}
+					}
+					console.log(this.mhFile)
+					//文件名回显
+					if(this.patient.mhFileName && this.patient.secondaryUploadFileName){
+						this.mhFileName = this.patient.mhFileName.split(',').concat(this.patient.secondaryUploadFileName.split(','))
+					}else{
+						if(this.patient.mhFileName){
+							this.mhFileName = this.patient.mhFileName.split(',')
+						}
+						if(this.patient.secondaryUploadFileName){
+							this.mhFileName = this.patient.secondaryUploadFileName.split(',')
+						}
+					}
+					 console.log(this.mhFileName)
+					if(this.patient.picWordRecognition != '' && this.patient.picWordRecognition != null){
+						this.strArr = this.patient.picWordRecognition.split(',')
+					}
+				}				
+			}
+		},
+		methods: {
+			format(str){
+				if(str.indexOf('ppt')!=-1){
+					return true
+				}else{
+					return false
+				}
+			},
+			EncryptName(name) {
+			    let newStr;
+			    if(name.length === 2) {
+			      newStr = name.substr(0, 1) + '*';
+			    } else if (name.length > 2) {
+			      let char = '';
+			      for (let i = 0, len = name.length - 2; i < len; i++) {
+			        char += '*';
+			      }
+			      newStr = name.substr(0, 1) + char + name.substr(-1, 1);
+			    } else {
+			      newStr = name;
+			    }
+			
+			    return newStr;
+			 },
+			// 图片预览
+			previewImage(url){
+				console.log(url)
+				let arr=[];
+				  arr.push(url)
+				  console.log(arr,'图片的地址')
+				  uni.previewImage({
+					   current: 0,
+					   urls: arr //必须是网址路径,否则加载不出来,如:http:或https:
+				  });
+			},
+			inputBindFocus(e) {
+				
+			    uni.onKeyboardHeightChange(res=>{
+			     console.log('log',res);
+			     this.KeyboardHeight = res.height
+			    })
+			 },
+			inputBindBlur(){
+			  this.KeyboardHeight = 0
+			  },
+			DropdownMeh(item){
+				this.DropdownIndex=item;
+				this.Dropdown=false;
+			},
+			showType(type){
+				this.type = type;
+				this.show = true
+			},
+			// 时间格式化
+			format(){
+				var date = new Date();
+				console.log(date.getFullYear()); // 返回当前日期的年  2019
+				console.log(date.getMonth() + 1); // 月份 返回的月份小1个月   记得月份+1 呦
+				console.log(date.getDate()); // 返回的是 几号
+				console.log(date.getDay()); // 3  周一返回的是 1 周六返回的是 6 但是 周日返回的是 0
+				// 我们写一个 2019年 5月 1日 星期三
+				var year = date.getFullYear();
+				var month = date.getMonth() + 1;
+				var dates = date.getDate();
+				var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
+				var day = date.getDay();
+				
+				console.log('今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day]);
+				var h = date.getHours();
+				h = h < 10 ? '0' + h : h;
+				var m = date.getMinutes();
+				m = m < 10 ? '0' + m : m;
+				var s = date.getSeconds();
+				s = s < 10 ? '0' + s : s;
+			return year +'-'+ month+'-' + dates + ' ' + h + ':' + m + ':' + s;
+			},
+			patientQuideEdit(type){
+				let data = {};
+				if(this.type == 1){
+					data = {
+						id:this.id,
+						type:this.type,
+						zjspNotpass:this.value,
+						// auditorTime:this.format()
+					}
+				}else{
+					data = {
+						id:this.id,
+						type:this.type,
+						zjspPass:this.value,
+						// auditorTime:this.format()
+					}
+				}
+				
+				if(this.value == ''){
+					this.$refs.uToast.show({ title: '请输入原因', type: 'error ', })
+					return;
+				}
+				this.$http.patientQuideEdit(data)
+				.then(res=>{
+					console.log(res)
+					if(res.data.code==200){
+						this.$refs.uToast.show({
+							title: '提交成功',
+						})
+						uni.navigateTo({
+						  url: '/pages/index/calendarExpert',
+						})						
+					}else{
+						this.$refs.uToast.show({
+							title: res.data.message,
+							type: 'error ',
+						})
+					}
+				})
+			},
+			words(){
+				if(this.strArr==''){
+					return
+				}
+				uni.navigateTo({
+				  url: '/pages/doctor/words',
+				})
+			},
+			// 下载文件
+			downloadFile(url){
+				uni.showLoading({title: '正在下载……'});
+				uni.downloadFile({
+					url: url,
+					success: (data) => {
+						console.log('打印data',data)
+						if (data.statusCode === 200) {
+							console.log('下载成功');
+							//隐藏加载框
+							uni.hideLoading();
+							//文件保存到本地
+							uni.saveFile({
+								tempFilePath: data.tempFilePath, //临时路径
+								success: function(res) {
+									// console.log('打印res',res)
+									uni.showToast({
+										icon: 'success',
+										mask: true,
+										// title: '文件已保存:' + res.savedFilePath, //保存路径
+										title: '下载成功' , 
+										duration: 2000,
+								});
+												
+								//自动打开文档查看
+								setTimeout(() => {
+									var filePath = res.savedFilePath;
+									uni.openDocument({  //新开页面打开文档,支持格式:doc, xls, ppt, pdf, docx, xlsx, pptx。
+										 filePath: filePath,
+										showMenu: true,
+										success: function (res) {
+											 console.log('打开文档成功');
+										}
+									});
+								}, 1000)
+								},
+							});
+						}
+					}
+				});
+			},
+	
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uploadExpert{
+		padding: 32rpx 32rpx 120rpx;
+		.popup{
+			position: fixed;
+			width: 100%;
+			left: 0;
+			bottom:0;
+			padding:32rpx;
+			background-color: #fff;
+			border-radius: 24rpx 24rpx 0px 0px;
+			z-index: 99999;
+			textarea{
+				box-sizing: border-box;
+				width: 100%;
+				padding:20rpx;
+				border-radius: 24rpx;
+				background-color: #F3F3F3;
+				font-size: 28rpx;
+				color:#000;
+				margin-top: 24rpx;
+				margin-bottom: 70rpx;
+			}
+			.tit{
+				font-size: 32rpx;
+				color: rgba(0,0,0,0.8);
+			}
+			.u-input__input{
+				
+			}
+			.btn{
+				width: 100%;
+				height: 96rpx;
+				line-height: 96rpx;
+				text-align: center;
+				font-size: 32rpx;
+				color:#fff;
+				background: #167FFF;
+				border-radius: 48rpx;
+			}
+		}
+		.title{
+			font-size: 48rpx;
+			color: #000;
+			padding-bottom: 24rpx;
+			font-weight: bold;
+		}
+		.flex{
+			position: relative;
+			.Dropdown{
+				position: absolute;
+				top: 0;
+				z-index: 10;
+				width: 100%;
+				padding: 24rpx 36rpx 0;
+				background-color: #fff;
+				box-shadow: 0px 2rpx 20rpx 0px rgba(0,0,0,0.1);
+				border-radius: 16rpx;
+				font-size:28rpx;
+				color: rgba(0,0,0,0.8);
+				.li{
+					margin-bottom: 28rpx;
+				}
+				.active{
+					color:$color;
+				}
+			}
+		}
+		.upData{
+			display: inline-block;
+			padding: 4rpx 24rpx;
+			background: rgba(22,127,255,0.1);
+			border-radius: 24rpx;
+			font-size: 28rpx;
+			color: $color;
+			image{
+				width: 32rpx;
+				height: 32rpx;
+				margin-left: 12rpx;
+			}
+		}
+		.box{
+			box-sizing: border-box;
+			padding: 32rpx 20rpx 0;
+			background-color: #fff;
+			border-radius: 24rpx;
+			margin-top: 24rpx;
+			.tit_box{
+				font-size: 32rpx;
+				color:$color;
+				font-weight: bold;
+			}
+			.msg{
+				display: block;
+				margin-top: 24rpx;
+				font-size: 28rpx;
+				color: #000000;
+				padding-bottom: 32rpx;
+			}
+			.tit{
+				font-size: 32rpx;
+				color: rgba(0,0,0,0.8);
+				font-weight: bold;
+			}
+			
+			.input{
+				position: relative;
+				.label{
+					font-size: 28rpx;
+					font-weight: bold;
+				}
+				.icon{
+					font-weight: bold;
+					width: 140rpx;	
+					color: rgba(0,0,0,0.8);
+				}
+				.msg{
+					color: rgba(0,0,0,0.35);
+					margin-left: 10rpx;
+				}
+				.list{
+					padding: 20rpx;					
+					background: rgba(227,66,33,0.1);		
+					border-radius: 24rpx;
+					font-size: 28rpx;
+					color: #000000;
+					margin-top: 20rpx;
+					image{
+						width: 40rpx;
+						height: 40rpx;
+						margin-right: 20rpx;
+					}
+					text{
+						flex: 1;
+						width: 100%;
+						word-wrap: break-word;
+					}
+				}
+				.list1{
+					background: rgba(22,127,255,0.1);
+				}
+				u-input{
+					flex: 1;
+				}
+				.imgList{
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					margin-bottom: 24rpx;
+					image{
+						width: 160rpx;
+						height: 160rpx;
+						border-radius: 24rpx;
+					}
+					.text{
+						flex: 1;
+						padding-left: 24rpx;
+						text{
+							color: $color;
+						}
+					}
+				}
+				image{
+					width: 32rpx;
+					height: 32rpx;
+				}
+				text{
+					color: rgba(0,0,0,0.8);
+				}
+			}
+			.input:last-child{
+				border-bottom:1px solid transparent;
+			}
+		}
+		.template{
+			padding:24rpx 0;
+			font-size: 28rpx;
+			color: #000;
+			.herf{
+				color: #167FFF!important;
+				word-wrap: break-word;
+			}
+		}
+		.suc{
+			position: relative;
+			text-align: center;
+			width: 520rpx;
+			height: 510rpx;
+			margin:0 auto;
+			image{
+				position: absolute;
+				left: 0;
+				top: 0;
+				width: 100%;
+				height: 510rpx;
+			}
+			.con{
+				position: relative;
+				z-index: 11;
+				text-align: center;
+				box-sizing: border-box;
+				padding: 0 40rpx;
+				overflow: hidden;
+				.tit{
+					font-size: 36rpx;
+					color: $color;
+					padding-top: 190rpx;
+				}
+				.p{
+					width: 230rpx;
+					margin: 32rpx auto 50rpx;
+					font-size: 28rpx;
+					color: rgba(0,0,0,0.6);
+				}
+				.btn{
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					justify-content: space-between;
+					view{
+						width: 208rpx;
+						height: 56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						border: 2rpx solid rgba(0,0,0,0.35);
+						font-size: 28rpx;
+						color: rgba(0,0,0,0.35);
+						border-radius: 40rpx;
+					}
+					.next{
+						background-color: $color;
+						color: #fff;
+						border: 2rpx solid $color;
+					}
+				}
+			}
+		}
+		.u-mode-center-box{
+			background-color: transparent!important;
+		}
+		.uploadBtn{
+			position: fixed;
+			bottom: 0;
+			left: 0;
+			width: 100%;
+			box-sizing: border-box;
+			padding: 12rpx 32rpx;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			justify-content: space-between;
+			font-size: 32rpx;
+			color:#fff;
+			background-color: #fff;
+			.no{
+				width: 320rpx;
+				padding: 20rpx 0;
+				text-align: center;
+				box-sizing: border-box;
+				border-radius: 48rpx;
+				background: #FF8F2E;
+			}
+			.yes{
+				width: 320rpx;
+				box-sizing: border-box;
+				border-radius: 48rpx;
+				padding: 20rpx 0;
+				text-align: center;
+				background-color: $color;
+			}
+		}
+	}
+</style>

+ 70 - 0
pages/doctor/words.vue

@@ -0,0 +1,70 @@
+<template>
+	<view class="words">
+		<u-toast ref="uToast" />
+		<view class="ul">
+			<image @click="index = i" :class="i==index?'active':''" :src="item" mode="" v-for="(item,i) in pic" :key="i"></image>
+		</view>
+		<view class="con" v-html="strArr[index]">
+			111
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				index:0,
+				patient:{},
+				pic:[],
+				strArr:[]
+			}
+		},
+		onLoad(option) {
+			if(uni.getStorageSync('patient')){
+				this.patient = JSON.parse(uni.getStorageSync('patient'))
+				if(this.patient.pic != ''){
+					this.pic = this.patient.pic.split(',')
+				}
+				if(this.patient.picWordRecognition != '' && this.patient.picWordRecognition != null){
+					this.strArr = this.patient.picWordRecognition.split(',')
+				}
+			}		
+		},
+		methods: {
+		}
+	}
+</script>
+
+<style lang="scss">
+	.words{
+		height: 100%;
+		.ul{
+			display: flex;
+			// flex-direction: row;
+			// align-items: center;
+			overflow: hidden;
+			overflow-x: auto;
+			margin-left: 32rpx;
+			image{
+				flex-grow: 0;
+				flex-shrink: 0;
+				width: 160rpx;
+				height: 160rpx;
+				border-radius: 24rpx;
+				border: 2rpx solid #fff;
+				margin-right: 24rpx;
+			}
+			.active{
+				border: 2rpx solid #167FFF;
+			}
+		}
+		.con{
+			width:680rpx;
+			margin: 24rpx auto;
+			padding:32rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+		}
+	}
+</style>

+ 371 - 0
pages/index/calendar.vue

@@ -0,0 +1,371 @@
+<template>
+	<view class="calendar">
+		<u-toast ref="uToast" />
+		<view class="updata u-flex u-row-between u-col-center">
+			<view class="updata-item1 u-flex u-row-center" @click="towenjuan" v-if="wisWjdc	== '允许' && wisWjdcUser == '1'">
+				<text class="text">问卷调查</text>
+				<!-- <image class="image1" src="../../static/img/right.png" mode=""></image> -->
+				<image class="image2" src="../../static/img/wenjuan.png" mode=""></image>
+			</view>
+			<view class="updata-item2 u-flex u-row-center" @click="upload()" v-if="wisMhu	== '允许'">
+				<text class="text">上传病历</text>
+				<!-- <image class="image1" src="../../static/img/right.png" mode=""></image> -->
+				<image class="image2" src="../../static/img/bingli.png" mode=""></image>
+			</view>
+		</view>
+		<view class="" style="padding: 10rpx 0;text-align: center;" v-if="wisWjdc	== '允许' && wisWjdcUser == '1'">
+			您本月提交调研问卷{{count || 0}}次
+		</view>
+		<view class="con" v-if="dataList.length == 0 && wisMhu == '允许'">
+			<image class="no" src="../../static/img/no.png" mode=""></image>
+			<view class="text">列表内容为空</view>
+		</view>
+		<view class="list" v-if="dataList.length > 0">
+			<view class="h6">历史上传</view>
+			<view class="li" v-for="(item,index) in dataList" :key="index" @click="uploadID(item)">
+				<view v-if="item.allocation==2">
+					<view class="flex one">
+						<view class="type">{{item.title}}</view>
+						<view class="name">{{item.name}}</view>
+						<view class="status status1">初审已通过</view>
+					</view>
+					<view class="flex address">
+						<view>{{item.city}}</view>
+						<view>{{item.hospital}}</view>
+					</view>
+					<view class="flex" style="justify-content: space-between;">
+						<view class="class">
+							<view class="box">{{item.uploadingName}}</view>
+							<view class="box">{{item.caseName}}</view>
+						</view>
+					</view>
+				</view>
+				<view v-else>
+					<view class="flex one">
+						<view class="type">{{item.title}}</view>
+						<view class="name">{{item.name}}</view>
+						<view class="status" v-if="item.firstTrial==0">未审核</view>
+						<view class="status status1" v-else-if="item.firstTrial==2">初审已通过</view>
+						<view class="status status2" v-else>未通过</view>
+					</view>
+					<view class="flex address">
+						<view>{{item.city}}</view>
+						<view>{{item.hospital}}</view>
+					</view>
+					<view class="flex" style="justify-content: space-between;">
+						<view class="class">
+							<view class="box">{{item.uploadingName}}</view>
+							<view class="box">{{item.caseName}}</view>
+						</view>
+						<view class="btn" v-if="item.firstTrial==1">重新编辑</view>
+						<view class="btn btn1" v-if="isup(item)">二次上传</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<tabBar :pagePath="'/pages/index/calendar'"></tabBar>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				dataList: [],
+				wisMhu: '',
+				wisWjdc: '',
+				wisWjdcUser:'',
+				count:0
+			}
+		},
+		onLoad(option) {
+			this.getsetting()
+		},
+		onShow() {
+			this.getuser()
+			this.$http.getTopicNum().then(res => {
+				this.count = res.data.result
+			})
+		},
+		methods: {
+			getsetting(){
+				this.$http.configInfo().then(res => {
+					if (res.data.code == 200) {
+						this.wisWjdcUser = res.data.result.wisWjdcUser
+					}
+					
+				})
+			},
+			getuser() {
+				this.$http.getUserInfo().then(res => {
+					if (res.data.code == 200) {
+						this.wisWjdc = res.data.result.wisWjdc
+						this.wisMhu = res.data.result.wisMhu
+						if (this.wisMhu == '允许') {
+							this.patientQuideByPhongList()
+						}
+					}
+
+				})
+			},
+			towenjuan() {
+				uni.navigateTo({
+					url: "./wenjuan"
+				})
+			},
+			isup(item) {
+				if (item.isSecondaryUpload == 2) {
+					if (item.firstTrial == 2 && item.secondaryUpload == 1) {
+						return true
+					} else {
+						return false
+					}
+				} else {
+					return false
+				}
+			},
+			patientQuideByPhongList() {
+				this.$http.patientQuideByPhongList()
+					.then(res => {
+						// console.log(res)
+						if (res.data.code == 200) {
+							this.dataList = res.data.result
+						}
+					})
+			},
+			uploadID(item) {
+				uni.setStorageSync('uploadData', JSON.stringify(item));
+				uni.navigateTo({
+					url: '/pages/doctor/upload?id=' + item.id,
+				})
+			},
+			upload() {
+				uni.navigateTo({
+					url: '/pages/doctor/upload',
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.calendar {
+		padding-bottom: 120rpx;
+
+		.updata {
+			position: relative;
+			width: 680rpx;
+			margin: 24rpx auto 0;
+			border-radius: 24rpx;
+			// padding: 68rpx 0;
+			text-align: center;
+
+			// font-size: 32rpx;
+			// color: $color;
+			.updata-item2 {
+				flex: 1;
+				margin: 0 8rpx;
+				height: 168rpx;
+				background: linear-gradient(180deg, #22F5FB 0%, #9AFFF8 100%);
+				border-radius: 24rpx;
+
+				// padding: 0 12rpx 0 20rpx;
+				.text {
+					font-size: 36rpx;
+					font-family: PingFangSC-Semibold, PingFang SC;
+					font-weight: 600;
+					color: #FFFFFF;
+					margin-right: 26rpx;
+				}
+
+				.image1 {
+					width: 40rpx;
+					height: 40rpx;
+					margin-right: 40rpx;
+				}
+
+				.image2 {
+					width: 128rpx;
+					height: 136rpx;
+				}
+			}
+
+			.updata-item1 {
+				flex: 1;
+				margin: 0 8rpx;
+				height: 168rpx;
+				background: linear-gradient(180deg, #FFCE7D 0%, #FFDCB3 100%);
+				border-radius: 24rpx;
+
+				// padding: 0 12rpx 0 20rpx;
+				.text {
+					font-size: 36rpx;
+					font-family: PingFangSC-Semibold, PingFang SC;
+					font-weight: 600;
+					color: #FFFFFF;
+					margin-right: 32rpx;
+				}
+
+				.image1 {
+					width: 40rpx;
+					height: 40rpx;
+					margin-right: 40rpx;
+				}
+
+				.image2 {
+					width: 118rpx;
+					height: 134rpx;
+				}
+			}
+
+		}
+
+		.con {
+			width: 680rpx;
+			margin: 24rpx auto;
+			padding: 32rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+			text-align: center;
+
+			.no {
+				width: 520rpx;
+				height: 340rpx;
+			}
+
+			.text {
+				font-size: 28rpx;
+				color: rgba(0, 0, 0, 0.35);
+				padding-bottom: 80rpx;
+			}
+		}
+
+		.list {
+			width: 680rpx;
+			margin: 24rpx auto;
+			box-sizing: border-box;
+			// padding: 0 20rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+			margin-top: 24rpx;
+
+			.h6 {
+				padding-top: 32rpx;
+				font-size: 34rpx;
+				color: $color;
+				font-weight: 700;
+				padding-left: 32rpx;
+			}
+
+			.li {
+				font-size: 28rpx;
+				color: rgba(0, 0, 0, 0.6);
+				padding: 24rpx 32rpx;
+
+				.flex {
+					margin-bottom: 14rpx;
+				}
+
+				.address {
+					view {
+						margin-right: 20rpx;
+					}
+				}
+
+				.class {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+
+					.box {
+						padding: 4rpx 16rpx;
+						background: rgba(22, 127, 255, 0.1);
+						color: #167FFF;
+						font-size: 24rpx;
+						margin-right: 16rpx;
+						border-radius: 24rpx;
+					}
+				}
+
+				.btn {
+					padding: 6rpx 24rpx;
+					background: #F02E2F;
+					color: #fff;
+					font-size: 28rpx;
+					border-radius: 24rpx;
+				}
+
+				.btn1 {
+					background-color: $color;
+				}
+
+				.one {
+					color: rgba(0, 0, 0, 0.8);
+
+					.type {
+						max-width: 280rpx;
+						white-space: nowrap;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						font-size: 32rpx;
+						color: rgba(0, 0, 0, 0.8);
+						margin-right: 24rpx;
+						font-weight: bold;
+					}
+
+					.name {
+						flex: 1;
+					}
+
+					.status {
+						position: relative;
+					}
+
+					.status1 {
+						color: $color;
+					}
+
+					.status2 {
+						color: #F02E2F;
+					}
+
+					.status::after {
+						content: '';
+						position: absolute;
+						left: -30rpx;
+						top: 50%;
+						transform: translateY(-50%);
+						width: 10rpx;
+						height: 10rpx;
+						border-radius: 50%;
+						background: #F0A22E;
+					}
+
+					.status1::after {
+						content: '';
+						position: absolute;
+						left: -30rpx;
+						top: 50%;
+						transform: translateY(-50%);
+						width: 10rpx;
+						height: 10rpx;
+						border-radius: 50%;
+						background: $color;
+					}
+
+					.status2::after {
+						content: '';
+						position: absolute;
+						left: -30rpx;
+						top: 50%;
+						transform: translateY(-50%);
+						width: 10rpx;
+						height: 10rpx;
+						border-radius: 50%;
+						background: #F02E2F;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 269 - 0
pages/index/calendarExpert.vue

@@ -0,0 +1,269 @@
+<template>
+	<view class="calendarExpert">
+		<u-toast ref="uToast" />
+		<view class="con" style="padding: 0;overflow: hidden;">
+		<u-tabs :list="list" :is-scroll="false" font-size="32" :show-bar="false" height="120" inactive-color="rgba(0,0,0,0.6)" active-color="rgba(0,0,0,0.8)" :current="current" @change="change"></u-tabs>
+		</view>
+		<view class="con" v-if="patientListZjArr.length==0">
+			<image class="no" src="../../static/img/no.png" mode=""></image>
+			<view class="text">列表内容为空</view>
+		</view>
+		<view class="list" v-else>
+			<view class="li" v-for="(item,index) in patientListZjArr" :key="index" @click="patientListZjMet(item)">
+				<view class="flex one">
+					<view class="type">{{item.title}}</view>
+					<view class="name">{{item.name}}</view>
+					<view class="status" v-if="item.type==0">未审核</view>
+					<view class="status status1" v-else-if="item.type==2">通过</view>
+					<view class="status status2" v-else>不通过</view>
+				</view>
+				<view class="flex address">
+					<view>{{item.city}}</view>
+					<view>{{item.hospital}}</view>
+				</view>
+				<view class="flex" style="justify-content: space-between;">
+					<view class="class" >
+						<view class="box">{{item.uploadingName}}</view><view class="box">{{item.caseName}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<tabBar :pagePath="'/pages/index/calendarExpert'"></tabBar>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				list: [{
+					name: '未审核'
+				}, {
+					name: '已审核'
+				}],
+				current: 0,
+				patientListZjArr:[],
+				type0List:[],
+				typeList:[]
+			}
+		},
+		onLoad(option) {
+			
+			this.patientListZj()
+		},
+		methods: {
+			
+			patientListZj(){
+				this.$http.patientListZj()
+				.then(res=>{
+					if(res.data.code==200){
+						// console.log(res.data.result)
+						// console.log(this.geshihua(res.data.result,'type'))
+						let list = this.geshihua(res.data.result,'type')
+						list.forEach(val=>{
+							if(val.type==0){
+								this.type0List = this.type0List.concat(val.data)
+							}else{
+								this.typeList = this.typeList.concat(val.data)
+							}
+						})
+						this.patientListZjArr = this.type0List
+						console.log(this.patientListZjArr)
+					}
+				})
+			},
+			// 数据分类
+			geshihua(data,type){
+				 // 1.拿到所有type和对应的下标
+				    var arr = [];   
+				    data.map((e,i)=>{
+				      let obj = {
+				        [type]:e[type],
+				        index:i
+				      }
+				      arr.push(obj)
+				    })
+				    // 2.归类
+				    let newarr = []
+				    arr.map((e,i)=>{
+				      // 数据初始化
+				      if(!i){
+				        let obj = {
+				          [type]:e[type],
+				          data:[]
+				        }
+				        newarr.push(obj)
+				      }
+				      let index = e.index
+				 
+				      // 如果没有这个数据,新加一个类
+				      // debugger
+				      for(let j=0;j<newarr.length;j++){
+				        // 如果已经有这个type,把数据添加进去即可
+				        if(newarr[j][type] == e[type]){
+				          newarr[j].data.push(data[index])
+				          break;
+				        }
+				        // 如果没有这个type,就新加一个
+				        else{
+				          // 循环到最后一个,如果没有,则加入这个type数据
+				          if(j==newarr.length-1){
+				            let obj = {
+				              [type]:e[type],
+				              data:[data[i]]
+				            }
+				            newarr.push(obj)
+				            break;
+				          }
+				        }
+				      }
+				    })
+				    return newarr
+			},
+			patientListZjMet(item){
+				uni.setStorageSync('patient',JSON.stringify(item));
+				uni.navigateTo({
+				  url: '/pages/doctor/uploadExpert?id='+item.id,
+				})
+			},
+			change(index) {
+				this.current = index;
+				if(this.current == 0){
+					
+				this.patientListZjArr = this.type0List
+				}else{
+				this.patientListZjArr = this.typeList
+					
+				}
+			}
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	.calendarExpert{
+		padding-top: 20rpx;
+		padding-bottom: 120rpx;
+		.con{
+			width:680rpx;
+			margin: 24rpx auto;
+			padding:32rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+			text-align: center;
+			.no{
+				width: 520rpx;
+				height: 340rpx;
+			}
+			.text{
+				font-size: 28rpx;
+				color: rgba(0,0,0,0.35);
+				padding-bottom: 80rpx;
+			}
+		}
+		.list{
+			width:680rpx;
+			margin: 24rpx auto;
+			box-sizing: border-box;
+			padding: 0 20rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+			margin-top: 24rpx;
+			.li{
+				font-size: 28rpx;
+				color: rgba(0,0,0,0.6);
+				padding: 24rpx 32rpx;
+				.flex{
+					margin-bottom: 14rpx;
+				}
+				.address{
+					view{
+						margin-right: 20rpx;
+					}
+				}
+				.class{
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					.box{
+						padding: 4rpx 16rpx;
+						background: rgba(22,127,255,0.1);
+						color: #167FFF;
+						font-size: 24rpx;
+						margin-right: 16rpx;
+						border-radius: 24rpx;
+					}
+				}
+				.btn{
+					padding: 6rpx 24rpx;
+					background: #F02E2F;
+					color: #fff;
+					font-size: 28rpx;
+					border-radius: 24rpx;
+				}
+				.btn1{
+					background-color: $color;
+				}
+				.one{
+					color: rgba(0,0,0,0.8);			
+					.type{
+						max-width: 280rpx;
+						white-space:nowrap;
+						overflow:hidden;
+						text-overflow:ellipsis;
+						font-size: 32rpx;
+						color: rgba(0,0,0,0.8);
+						margin-right: 24rpx;
+						font-weight: bold;			
+					}
+					.name{
+						flex: 1;
+					}
+					.status{
+						position: relative;
+					}
+					.status1{
+						color: $color;
+					}
+					.status2{
+						color: #F02E2F;
+					}
+					.status::after{
+						content: '';
+						position: absolute;
+						left: -30rpx;
+						top: 50%;
+						transform: translateY(-50%);
+						width: 10rpx;
+						height: 10rpx;
+						border-radius: 50%;
+						background: #F0A22E;
+					}
+					.status1::after{
+						content: '';
+						position: absolute;
+						left: -30rpx;
+						top: 50%;
+						transform: translateY(-50%);
+						width: 10rpx;
+						height: 10rpx;
+						border-radius: 50%;
+						background: $color;
+					}
+					.status2::after{
+						content: '';
+						position: absolute;
+						left: -30rpx;
+						top: 50%;
+						transform: translateY(-50%);
+						width: 10rpx;
+						height: 10rpx;
+						border-radius: 50%;
+						background: #F02E2F;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 63 - 0
pages/index/guide.vue

@@ -0,0 +1,63 @@
+<template>
+	<view class="guide">
+		<view class="con" v-html="data">
+		</view>
+		<tabBar :pagePath="'/pages/index/guide'"></tabBar>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+			data:{},
+			}
+		},
+		onLoad(option) {
+			this.configInfo()
+			this.getUserInfo()
+		},
+		methods: {
+			configInfo(){
+				this.$http.configInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						console.log(res)
+						let str = res.data.result.czzn
+						this.data=str.replace(/\<img/gi, '<img style="max-width:100%;height:auto" ').replace(/\<p>/gi, '<p style="word-break:break-all;">');
+					}
+				})
+			},
+			getUserInfo(){
+				this.$http.getUserInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						// 是否信息完整
+						if(res.data.result.completeInformation != 2){
+							uni.navigateTo({
+							  url: '/pages/login/perfect',
+							})
+						}
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.guide{
+		padding-bottom: 120rpx;
+		.headImg{
+			width: 100%;
+			height: 350rpx;
+		}
+		.con{
+			width:680rpx;
+			margin: 24rpx auto;
+			padding:32rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+		}
+	}
+</style>

+ 81 - 0
pages/index/index.vue

@@ -0,0 +1,81 @@
+<template>
+	<view class="index">
+		<view class="headImg">
+			<image :src="data.bjt" mode=""></image>
+		</view>
+		<view class="con" v-html="xmjs">
+			111
+		</view>
+		<tabBar :pagePath="'/pages/index/index'"></tabBar>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+			data:{},
+			current: 0,
+			list: [
+				
+			  ],
+			xmjs:''
+			}
+		},
+		onLoad(option) {
+			this.configInfo()
+			this.getUserInfo()
+		},
+		methods: {
+			getUserInfo(){
+				this.$http.getUserInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						// 是否信息完整
+						if(res.data.result.completeInformation != 2){
+							uni.navigateTo({
+							  url: '/pages/login/perfect',
+							})
+						}
+					}
+				})
+			},
+			configInfo(){
+				this.$http.configInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						console.log(res)
+						this.data=res.data.result
+						let str = res.data.result.xmjs
+						this.xmjs = str.replace(/\<img/gi, '<img style="max-width:100%;height:auto" ').replace(/\<p>/gi, '<p style="word-break:break-all;">');
+						// console.log(this.data)
+					}
+				})
+			},
+			onChange(index) {
+			      uniLinkTo(this.vuex_tab_page[index], 'tab')
+			    }
+		}
+	}
+</script>
+
+<style lang="scss">
+	.index{
+		padding-bottom: 120rpx;
+		.headImg{
+			width: 100%;
+			height: 350rpx;
+			image{
+				width: 100%;
+				height: 100%;
+			}
+		}
+		.con{
+			width:680rpx;
+			margin: 24rpx auto;
+			padding:32rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+		}
+	}
+</style>

+ 157 - 0
pages/index/my.vue

@@ -0,0 +1,157 @@
+<template>
+	<view class="my">
+		<u-navbar back-text=" " title=" " :background="{backgroundColor: 'transparent'}"></u-navbar>
+		<image class="myBGC" src="../../static/img/myBGC.png" mode=""></image>
+		<view class="info flex">
+			<view class="text">
+				<view class="name">
+					{{data.name}}
+				</view>
+				<view>{{data.dwname}}</view>
+			</view>
+			<image :src="data.avatar" mode=""></image>
+		</view>
+		<view class="con" >
+			<view class="li flex">
+				<view class="label">科室名称</view>
+				<text>{{data.ksname}}</text>				
+			</view>
+			<view class="li flex">
+				<view class="label">联系方式</view>
+				<text>{{data.phone}}</text>				
+			</view>
+			<view class="li flex">
+				<view class="label">身份证号</view>
+				<text>{{data.card}}</text>				
+			</view>
+			<!-- <view class="li flex">
+				<view class="label">本月问卷提交次数</view>
+				<text>{{count}}</text>				
+			</view> -->
+		</view>
+		<view class="con" >
+			<view class="li flex" @click="password()">
+				<image src="../../static/img/my1.png" mode=""></image>
+				<text>修改密码</text>				
+			</view>
+			<view class="li flex" @click="out()">
+				<image src="../../static/img/my2.png" mode=""></image>
+				<text>退出登录</text>				
+			</view>
+		</view>
+		<u-modal v-model="show" @confirm="confirm" :show-cancel-button="true" @cancel="cancel" content="您确定要退出登录状态吗?"></u-modal>
+		<tabBar :pagePath="'/pages/index/my'"></tabBar>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				show:false,
+				data:{},
+				// count:0
+			}
+		},
+		onLoad(option) {
+			this.getUserInfo()
+		},
+		// onShow() {
+		// 	this.$http.getTopicNum().then(res => {
+		// 		this.count = res.data.result
+		// 	})
+		// },
+		methods: {
+			password(){
+				uni.navigateTo({
+				  url: '/pages/user/password',
+				})
+			},
+			out(){
+				this.show = true
+			},
+			confirm(){
+				uni.removeStorageSync('token');
+				uni.reLaunch({
+					url: '/pages/login/login'
+				});
+			},
+			cancel(){
+				this.show = false
+			},
+			getUserInfo(){
+				this.$http.getUserInfo().then(res=>{
+					if(res.data.code==200){
+						console.log(res)
+						this.data=res.data.result
+						// console.log(this.data)
+					}
+				})
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	.my{
+		height: 100%;
+		position: relative;
+		background: #F5F8FF;
+		.myBGC{
+			position: absolute;
+			width: 100%;
+			top: 0;
+			height: 600rpx;
+		}
+		.info{
+			position: relative;
+			z-index: 1;
+			width:680rpx;
+			margin: 24rpx auto;
+			.text{
+				flex: 1;
+				color: rgba(0,0,0,0.6);
+				font-size: 28rpx;
+				.name{
+					font-size: 44rpx;
+					font-family: PingFang SC-Semibold, PingFang SC;
+					font-weight: 600;
+					color: rgba(0,0,0,0.8);
+					margin-bottom: 10rpx;
+				}
+			}
+			image{
+				width: 120rpx;
+				height: 120rpx;
+				opacity: 1;
+				border: 1px solid #FFFFFF;
+				border-radius: 50%;
+				box-shadow: 0px 0px 20rpx 0px rgba(0,0,0,0.05), inset 0px 2rpx 2rpx 0px rgba(255,255,255,0.25);
+			}
+		}
+		.con{
+			z-index: 1;
+			position: relative;
+			width:680rpx;
+			margin: 24rpx auto;
+			padding: 32rpx 32rpx 0;
+			background: linear-gradient(180deg, #F2F9FB 0%, #FFFFFF 100%);
+			box-shadow: 0px 0px 20rpx 0px rgba(0,0,0,0.05), inset 0px 2rpx 2rpx 0px rgba(255,255,255,0.25);
+			border-radius: 24rpx;
+			.li{
+				padding-bottom: 32rpx;
+				font-size: 28rpx;
+				color: rgba(0,0,0,0.6);
+				.label{
+					margin-right: 20rpx;
+				}
+				image{
+					width: 40rpx;
+					height: 40rpx;
+					margin-right: 20rpx;
+				}
+			}
+		}
+	}
+</style>

+ 1262 - 0
pages/index/wenjuan.vue

@@ -0,0 +1,1262 @@
+<template>
+	<view class="wenjuan">
+		<image src="../../static/img/bg@2x.png" style="position: fixed;z-index: -1;width: 750rpx;top: 0;left: 0;" mode="widthFix"></image>
+		<view class="wenjian-tips u-flex-col">
+			<text class="text">问卷调查</text>
+			<view class="u-flex u-col-top" style="margin-bottom: 10rpx;">
+				<text class="text1" style="width: 150rpx;">目的和内容:</text>
+				<text class="text1" style="flex: 1;">了解我国消化系统门诊常见疾病发生以及相关情况;</text>
+			</view>
+			<view class="u-flex u-col-top" style="margin-bottom: 10rpx;">
+				<text class="text1" style="width: 150rpx;">保密承诺:</text>
+				<text class="text1" style="flex: 1;">我们保证任何情况下不会以任何方式泄露您的个人信息和调查结果;</text>
+			</view>
+			<view class="u-flex u-col-top" style="margin-bottom: 10rpx;">
+				<text class="text1" style="width: 150rpx;">自愿原则:</text>
+				<text class="text1" style="flex: 1;">本调查为自愿,您可以拒绝参加或随时退出。</text>
+			</view>
+		</view>
+		<view class="wenjuan-header">
+			<view class="u-flex u-row-between header-time" @click="showtime = true">
+				<text class="title">本次门诊时间</text>
+				<view class="u-flex u-col-center">
+					<input type="text" class="wj-time" :disabled="true" v-model="time.year">
+					<text class="title">年</text>
+					<input type="text" class="wj-time" :disabled="true" v-model="time.month">
+					<text class="title">月</text>
+					<input type="text" class="wj-time" :disabled="true" v-model="time.day">
+					<text class="title">日</text>
+				</view>
+			</view>
+			<!-- <view class="u-flex u-row-between header-text">
+				<view class="u-flex">
+					<text class="title">医院</text>
+					<input type="text" class="input1" v-model="yiyuan">
+				</view>
+				<view class="u-flex">
+					<text class="title">手机号</text>
+					<input type="text" class="input1" v-model="name">
+				</view>
+			</view> -->
+			<view class="u-flex u-row-between header-count">
+				<text class="title">本次门诊人数</text>
+				<view class="u-flex">
+					<input type="number" class="input1" style="margin-right: 0;" v-model="count">
+					<text class="title">人</text>
+				</view>
+			</view>
+			<view class="header-start" @click="start">
+				开始
+			</view>
+			<view class="row-tips">
+				*此问卷针对您本次门诊所有患者的基础信息和疾病情况进行调研
+			</view>
+		</view>
+		<u-popup mode="right" height="100vh" v-model="show1">
+			<view class="timu-box">
+				<image src="../../static/img/bg@2x.png" style="position: fixed;top: 0;left: 0;width: 750rpx;height: 100vh;z-index: -1;" mode=""></image>
+				<view class="timu-bg">
+					<view class="timu-type">
+						基本信息
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							1、本次门诊患者群中,男性和女性患者比例分别是多少?
+						</view>
+						<view class="daan-box u-flex u-row-between">
+							<view class="daan-item u-flex" v-for="(item,index) in daan1" :key="index" @click="open(1,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu1[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							2、本次门诊患者群中,医疗保险分布情况如何?
+						</view>
+						<view class="daan-box u-flex u-flex-wrap u-row-between">
+							<view class="daan-item u-flex" v-for="(item,index) in daan2" :key="index" @click="open(2,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu2[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							3、本次门诊患者群中,年龄层次分布如何?
+						</view>
+						<view class="daan-box u-flex u-flex-wrap u-row-between">
+							<view class="daan-item u-flex" v-for="(item,index) in daan3" :key="index" @click="open(3,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu3[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							4、本次门诊患者疾病分类及具体比例分别是多少?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan4" :key="index" @click="open(4,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu4[index]" class="timu-input" :disabled="true">
+								<text class="title1">%</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="u-flex u-row-between timu-tijiao">
+					<text class="text1" @click="show1 = false">上一页</text>
+					<text class="text2" @click="tijiao(1)">下一页</text>
+				</view>
+			</view>
+		</u-popup>
+		<u-popup mode="right" height="100vh" v-model="show2">
+			<view class="timu-box">
+				<image src="../../static/img/bg@2x.png" style="position: fixed;top: 0;left: 0;width: 750rpx;height: 100vh;z-index: -1;" mode=""></image>
+				<view class="timu-bg">
+					<view class="timu-type">
+						腹胀患者调研
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							5、本次门诊就诊患者群中,有腹胀症状(包括患者主诉)的患者比例有多少?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" @click="open(5,0)">
+								<text class="title u-flex-1"></text>
+								<input type="text" v-model="timu5[0]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							6、以上有腹胀症状的患者中,新发腹胀和有腹胀病史的患者比例分别占比多少?
+						</view>
+						<view class="daan-box u-flex u-row-between">
+							<view class="daan-item u-flex" v-for="(item,index) in daan6" :key="index" @click="open(6,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu6[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							7、对于有腹胀病史患者,曾经或者正在进行的治疗方法有哪些?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan7" :key="index" @click="open(7,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu7[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							8、对于新发腹胀患者,您的治疗一线药物是?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan8" :key="index" @click="open(8,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu8[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							9、哪种腹胀类型的患者,您会优先考虑祛泡剂?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item" style="width: 100%;height: 128rpx;">
+								<textarea v-model="timu9" class="timu-input" style="width: 100%;height: 128rpx;padding: 10rpx 0;box-sizing: border-box;"></textarea>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							10、您认为祛泡剂对这类患者最大的优势是什么?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item" style="width: 100%;height: 128rpx;">
+								<textarea v-model="timu10" class="timu-input" style="width: 100%;height: 128rpx;padding: 10rpx 0;box-sizing: border-box;"></textarea>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="u-flex u-row-between timu-tijiao">
+					<text class="text1" @click="show2 = false">上一页</text>
+					<text class="text2" @click="tijiao(2)" v-if="changeshowbg(1) || changeshowbg(2)">下一页</text>
+					<text class="text2" @click="tijiao(4)" v-else>提交</text>
+				</view>
+			</view>
+		</u-popup>
+		<u-popup mode="right" height="100vh" v-model="show3">
+			<view class="timu-box">
+				<image src="../../static/img/bg@2x.png" style="position: fixed;top: 0;left: 0;width: 750rpx;height: 100vh;z-index: -1;" mode=""></image>
+				<view class="timu-bg">
+					<view class="timu-type">
+						腹痛患者调研
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							11、本次门诊中存在腹痛症状患者,以下部位腹痛发生的比例是?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan11" :key="index" @click="open(11,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu11[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							12、在本次门诊诊断肠易激综合征(IBS)时,您是否会考虑患者的IBS亚型?如考虑,不同亚型的占比都是多少?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;height: 128rpx;">
+								<view class="title u-flex-col">
+									<text>A. 不考虑,认为亚型与治疗方案关系不大 </text>
+									<u-radio-group v-model="daan12radio" @change="radioGroupChange12">
+										<u-radio name="是">
+											是
+										</u-radio>
+										<u-radio name="否">
+											否
+										</u-radio>
+									</u-radio-group>
+								</view>
+							</view>
+							<view class="daan-item u-flex" :style="{opacity: daan12radio == '是' ? '0.3' : 1,width: '100%'}" v-for="(item,index) in daan12" :key="index" @click="open(12,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu12[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							13、有证据表明解痉剂可改善肠易激综合征(IBS)的症状,对腹痛的疗效显著。若选择奥替溴铵治疗IBS相关的腹痛,您会考虑给以下哪些亚型的肠易激综合征(IBS)患者使用奥替溴铵?
+						</view>
+						<view class="daan-box">
+							<view :class="timu13[index] ? 'daan-item u-flex daan-item1' : 'daan-item u-flex'" :style="{opacity: check13(item,index),width: '100%'}" v-for="(item,index) in daan13" :key="index" @click="change13(item,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<!-- <input type="text" v-model="timu13[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text> -->
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							14、您认为奥替溴铵对肠易激综合征(IBS)患者最大的优势是什么?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item" :style="{opacity: timu13[5] ? 0.3 : 1,width: '100%',height: '128rpx'}">
+								<textarea v-model="timu14" :disabled="timu13[5]" class="timu-input" style="width: 100%;height: 128rpx;padding: 10rpx 0;box-sizing: border-box;"></textarea>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="u-flex u-row-between timu-tijiao">
+					<text class="text1" @click="show3 = false">上一页</text>
+					<text class="text2" @click="tijiao(3)" v-if="changeshowbg(2)">下一页</text>
+					<text class="text2" @click="tijiao(4)" v-else>提交</text>
+				</view>
+			</view>
+		</u-popup>
+		<u-popup mode="right" height="100vh" v-model="show4">
+			<view class="timu-box">
+				<image src="../../static/img/bg@2x.png" style="position: fixed;top: 0;left: 0;width: 750rpx;height: 100vh;z-index: -1;" mode=""></image>
+				<view class="timu-bg">
+					<view class="timu-type">
+						炎症性肠病患者调研
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							15、本次门诊溃疡性结肠炎(UC)患者群中,按疾病程度分类,分别占比多少?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan15" :key="index" @click="open(15,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu15[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							16、本次门诊溃疡性结肠炎(UC)患者就诊原因占比?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan16" :key="index" @click="open(16,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu16[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<view class="timu-row">
+						<view class="timu-text">
+							17、本次门诊针对溃疡性结肠炎(UC)患者所使用的不同治疗方案的占比?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex" style="width: 100%;" v-for="(item,index) in daan17" :key="index" @click="open(17,index)">
+								<text class="title u-flex-1">{{item}}</text>
+								<input type="text" v-model="timu17[index]" class="timu-input" :disabled="true">
+								<text class="title">%</text>
+							</view>
+						</view>
+					</view>
+					<!-- <view class="timu-row">
+						<view class="timu-text">
+							18、针对本次门诊中使用不同剂型美沙拉秦类药物的患者数?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item u-flex u-row-between" style="width: 100%;">
+								<text class="title" style="width:160rpx">A. 缓释片剂</text>
+								<view class="u-flex">
+									<text class="title">剂量</text>
+									<input type="number" v-model="timu181" style="width: 85rpx;" class="timu-input">
+								</view>
+								<view class="u-flex" @click="open(18,0)">
+									<text class="title">人数</text>
+									<input type="text" v-model="timu18[0]" style="width: 85rpx;" class="timu-input" :disabled="true">
+									<text class="title">%</text>
+								</view>
+							</view>
+							<view class="daan-item u-flex u-row-between" style="width: 100%;">
+								<text class="title" style="width:160rpx">B. 缓释颗粒</text>
+								<view class="u-flex">
+									<text class="title">剂量</text>
+									<input type="number" v-model="timu182" style="width: 85rpx;" class="timu-input">
+								</view>
+								<view class="u-flex" @click="open(18,1)">
+									<text class="title">人数</text>
+									<input type="text" v-model="timu18[1]" style="width: 85rpx;" class="timu-input" :disabled="true">
+									<text class="title">%</text>
+								</view>
+							</view>
+							<view class="daan-item u-flex u-row-between" style="width: 100%;">
+								<text class="title" style="width:160rpx">C. 栓剂</text>
+								<view class="u-flex">
+									<text class="title">剂量</text>
+									<input type="number" v-model="timu183" style="width: 85rpx;" class="timu-input">
+								</view>
+								<view class="u-flex" @click="open(18,2)">
+									<text class="title">人数</text>
+									<input type="text" v-model="timu18[2]" style="width: 85rpx;" class="timu-input" :disabled="true">
+									<text class="title">%</text>
+								</view>
+							</view>
+						</view>
+					</view> -->
+					<view class="timu-row">
+						<view class="timu-text">
+							18、您认为美沙拉秦对溃疡性结肠炎(UC)患者最大的优势是什么?
+						</view>
+						<view class="daan-box">
+							<view class="daan-item" style="width: 100%;height: 128rpx;">
+								<textarea v-model="timu18" class="timu-input" style="width: 100%;height: 128rpx;padding: 10rpx 0;box-sizing: border-box;"></textarea>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="u-flex u-row-between timu-tijiao">
+					<text class="text1" @click="show4 = false">上一页</text>
+					<text class="text2" @click="tijiao(4)">提交</text>
+				</view>
+			</view>
+		</u-popup>
+		<u-picker v-model="showtime" mode="time" :params="params" @confirm="changetime"></u-picker>
+		<u-picker mode="selector" v-model="shownum" :default-selector="defaulenum" :range="numlist" @confirm="changenum"></u-picker>
+	</view>
+</template>
+
+<script>
+	import timu from "../../common/timu.js"
+	var numlist = timu.getNumber(100)
+	export default {
+		data() {
+			return {
+				yiyuan: '',
+				name: '',
+				time: {
+					year: "",
+					month: "",
+					day: ""
+				},
+				count: '',
+
+				timu1: ['', ''],
+				daan1: ['男', '女'],
+
+				timu2: ['', '', '', ''],
+				daan2: ['A. 医保(新农合)', 'B. 商业保险', 'C. 自费', 'D. 其他'],
+
+				timu3: ['', '', '', ''],
+				daan3: ['A. 小于18岁', 'B. 18-40岁', 'C. 41-60岁', 'D. 61岁及以上'],
+
+				timu4: ['', '', '', '', '', '', '', ''],
+				daan4: ['A. 肠易激综合征(IBS)', 'B. 溃疡性结肠炎', 'C. 克罗恩病', 'D. 功能性消化不良', 'E. 功能性腹胀', 'F. 肠炎', 'G. 胃炎', 'H. 其他待查'],
+
+				timu5: [''],
+
+				timu6: ['', ''],
+				daan6: ['A. 新发腹胀', 'B. 有腹胀病史'],
+
+				timu7: ['', '', '', '', '', '', '', '', '', '', '', ''],
+				daan7: ['A. 饮食干预', 'B. 促动力药物', 'C. 消化酶', 'D. 益生菌', 'E. 祛泡剂', 'F. 抗生素', 'G. 神经调节剂', 'H. 解痉药', 'I. 中药治疗', 'J. 生物反馈治疗', 'K. 其他', 'L. 不干预'],
+
+				timu8: ['', '', '', '', '', '', '', ''],
+				daan8: ['A. 促动力药物', 'B. 消化酶', 'C. 益生菌', 'D. 祛泡剂', 'E. 抗生素', 'F. 神经调节剂', 'G. 解痉药', 'H. 中药治疗'],
+
+				timu9: '',
+				timu10: '',
+
+				timu11: ['', '', '', '', '', '', '', '', '', ''],
+				daan11: ['A. 中上腹', 'B. 左上腹', 'C. 右上腹', 'D. 下腹', 'E. 左下腹', 'F. 右下腹', 'G. 脐周', 'H. 全腹', 'I. 不固定', 'J. 说不清部位'],
+
+				daan12radio: '',
+				timu12: ['', '', '', ''],
+				daan12: ['B. IBS便秘型(IBS-C)', 'C. IBS腹泻型(IBS-D)', 'D. IBS混合型(IBS-M)', 'E. IBS不定型(IBS-U)'],
+
+				daan13radio1: '',
+				daan13radio2: '',
+				timu13: [false, false, false, false, false, false],
+				daan13: ['A. 不考虑亚型,只要有腹痛症状即使用', 'B. IBS便秘型(IBS-C)', 'C. IBS腹泻型(IBS-D)', 'D. IBS混合型(IBS-M)', 'E. IBS不定型(IBS-U)', 'F. 不使用奥替溴铵'],
+
+				timu14: '',
+
+				timu15: ['', '', ''],
+				daan15: ['A. 轻度', 'B. 轻中度', 'C. 中重度'],
+
+				timu16: ['', '', '', '', ''],
+				daan16: ['A. 疾病活动期', 'B. 便血', 'C. 长期用药出现副作用', 'D. 原治疗方案使用不方便', 'E. 维持期定期复诊'],
+
+				timu17: ['', '', '', '', '', ''],
+				daan17: ['A. 氨基水杨酸制剂', 'B. 激素类', 'C. 硫嘌呤类药物', 'D. 沙利度胺', 'E. 生物制剂类', 'F. 其他方案'],
+
+				// timu18: ['', '', ''],
+				// timu181: '',
+				// timu182: '',
+				// timu183: '',
+
+				timu18: '',
+
+				showtime: false,
+				params: {
+					year: true,
+					month: true,
+					day: true,
+					hour: false,
+					minute: false,
+					second: false
+				},
+				shownum: false,
+				numlist: [],
+				defaulenum: [0],
+				timuindex: '',
+				daanindex: '',
+
+				show1: false,
+				show2: false,
+				show3: false,
+				show4: false,
+			}
+		},
+		onLoad() {
+			this.getuser()
+		},
+		methods: {
+			getuser() {
+				this.$http.getUserInfo().then(res => {
+					if (res.data.code == 200) {
+						// this.userdata = res.data.result
+						this.name = res.data.result.phone || ''
+						this.yiyuan = res.data.result.dwname || ''
+					}
+				})
+			},
+			change13(item, index) {
+				if (this.check13(item, index) == 1) {
+					this.timu13[index] = this.timu13[index] ? false : true
+					this.timu13 = JSON.parse(JSON.stringify(this.timu13))
+				}
+				if (index == 0 && this.timu13[index]) {
+					this.timu13 = JSON.parse(JSON.stringify([true, false, false, false, false, false]))
+				}
+				if (index == 5 && this.timu13[index]) {
+					this.timu13 = JSON.parse(JSON.stringify([false, false, false, false, false, true]))
+					this.timu14 = ''
+				}
+			},
+			check13(item, index) {
+				if (index != 0 && index != 5) {
+					if (this.timu13[0] || this.timu13[5]) {
+						return 0.3
+					} else {
+						return 1
+					}
+				} else {
+					if (this.timu13[0] && index == 5) {
+						return 0.3
+					}
+					if (this.timu13[5] && index == 0) {
+						return 0.3
+					}
+					return 1
+				}
+			},
+			start() {
+				if (!this.time.year) {
+					this.$u.toast("请选择门诊时间")
+					return
+				}
+				// if (!this.yiyuan) {
+				// 	this.$u.toast("请输入医院名字")
+				// 	return
+				// }
+				// if (!this.name) {
+				// 	this.$u.toast("请输入手机号")
+				// 	return
+				// }
+				if (!this.count) {
+					this.$u.toast("请输入门诊人数")
+					return
+				}
+				this.show1 = true
+			},
+			tijiao(type) {
+				var topic = []
+				var topicTwo = []
+				var b = []
+				var bTwo = []
+				// --------------------------------------------------第一部分--------------------------------------------------
+				var timu1bili = 0
+				this.timu1.forEach((val, key) => {
+					timu1bili = timu1bili + Number(val)
+					b.push(`${this.daan1[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				if (timu1bili < 100) {
+					this.$u.toast("第1题比例加起来需为100%")
+					return
+				}
+				topic.push({
+					topicT: '1、本次门诊患者群中,男性和女性患者比例分别是多少?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '1、本次门诊患者群中,男性和女性患者比例分别是多少?',
+					topicD: bTwo.join(",")
+				})
+
+				var timu2bili = 0
+				b = []
+				bTwo = []
+				this.timu2.forEach((val, key) => {
+					timu2bili = timu2bili + Number(val)
+					b.push(`${this.daan2[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				if (timu2bili < 100) {
+					this.$u.toast("第2题比例加起来需为100%")
+					return
+				}
+				topic.push({
+					topicT: '2、本次门诊患者群中,医疗保险分布情况如何?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '2、本次门诊患者群中,医疗保险分布情况如何?',
+					topicD: bTwo.join(",")
+				})
+
+				var timu3bili = 0
+				b = []
+				bTwo = []
+				this.timu3.forEach((val, key) => {
+					timu3bili = timu3bili + Number(val)
+					b.push(`${this.daan3[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				if (timu3bili < 100) {
+					this.$u.toast("第3题比例加起来需为100%")
+					return
+				}
+				topic.push({
+					topicT: '3、本次门诊患者群中,年龄层次分布如何?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '3、本次门诊患者群中,年龄层次分布如何?',
+					topicD: bTwo.join(",")
+				})
+
+				b = []
+				bTwo = []
+				this.timu4.forEach((val, key) => {
+					b.push(`${this.daan4[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				topic.push({
+					topicT: '4、本次门诊患者疾病分类及具体比例分别是多少?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '4、本次门诊患者疾病分类及具体比例分别是多少?',
+					topicD: bTwo.join(",")
+				})
+
+				if (type == 1) {
+					this.show2 = true
+					return
+				}
+
+				// --------------------------------------------------第二部分--------------------------------------------------
+				topic.push({
+					topicT: '5、本次门诊就诊患者群中,有腹胀症状(包括患者主诉)的患者比例有多少?',
+					topicD: `${this.timu5[0] || 0}%`
+				})
+				topicTwo.push({
+					topicT: '5、本次门诊就诊患者群中,有腹胀症状(包括患者主诉)的患者比例有多少?',
+					topicD: `${this.timu5[0] || 0}%`
+				})
+
+				var timu6bili = 0
+				b = []
+				bTwo = []
+				this.timu6.forEach((val, key) => {
+					timu6bili = timu6bili + Number(val)
+					b.push(`${this.daan6[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				if (timu6bili < 100) {
+					this.$u.toast("第6题比例加起来需为100%")
+					return
+				}
+				topic.push({
+					topicT: '6、以上有腹胀症状的患者中,新发腹胀和有腹胀病史的患者比例分别占比多少?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '6、以上有腹胀症状的患者中,新发腹胀和有腹胀病史的患者比例分别占比多少?',
+					topicD: bTwo.join(",")
+				})
+
+				b = []
+				bTwo = []
+				this.timu7.forEach((val, key) => {
+					b.push(`${this.daan7[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				topic.push({
+					topicT: '7、对于有腹胀病史患者,曾经或者正在进行的治疗方法有哪些?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '7、对于有腹胀病史患者,曾经或者正在进行的治疗方法有哪些?',
+					topicD: bTwo.join(",")
+				})
+
+				b = []
+				bTwo = []
+				this.timu8.forEach((val, key) => {
+					b.push(`${this.daan8[key]}${val || 0}%`)
+					bTwo.push(`${val || 0}%`)
+				})
+				topic.push({
+					topicT: '8、对于新发腹胀患者,您的治疗一线药物是?',
+					topicD: b.join(",")
+				})
+				topicTwo.push({
+					topicT: '8、对于新发腹胀患者,您的治疗一线药物是?',
+					topicD: bTwo.join(",")
+				})
+
+				if (!this.timu9) {
+					this.$u.toast("请填写第9题")
+					return
+				}
+				var timu9 = "";
+				for (var i = 0; i < this.timu9.length; i++) {
+					timu9 = timu9 + (this.timu9[i] == "\n" ? '' : this.timu9[i])
+				}
+				topic.push({
+					topicT: '9、哪种腹胀类型的患者,您会优先考虑祛泡剂?',
+					topicD: timu9
+				})
+				topicTwo.push({
+					topicT: '9、哪种腹胀类型的患者,您会优先考虑祛泡剂?',
+					topicD: timu9
+				})
+
+				if (!this.timu10) {
+					this.$u.toast("请填写第10题")
+					return
+				}
+				var timu10 = "";
+				for (var i = 0; i < this.timu10.length; i++) {
+					timu10 = timu10 + (this.timu10[i] == "\n" ? '' : this.timu10[i])
+				}
+				topic.push({
+					topicT: '10、您认为祛泡剂对这类患者最大的优势是什么?',
+					topicD: timu10
+				})
+				topicTwo.push({
+					topicT: '10、您认为祛泡剂对这类患者最大的优势是什么?',
+					topicD: timu10
+				})
+
+				if (type == 2) {
+					if (this.changeshowbg(1)) {
+						this.show3 = true
+						return
+					}
+					if (this.changeshowbg(2)) {
+						this.show4 = true
+						return
+					}
+					return
+				}
+
+				// --------------------------------------------------第三部分--------------------------------------------------
+
+				if (Number(this.timu4[0]) > 0) {
+					b = []
+					bTwo = []
+					this.timu11.forEach((val, key) => {
+						b.push(`${this.daan11[key]}${val || 0}%`)
+						bTwo.push(`${val || 0}%`)
+					})
+					topic.push({
+						topicT: '11、本次门诊中存在腹痛症状患者,以下部位腹痛发生的比例是?',
+						topicD: b.join(",")
+					})
+					topicTwo.push({
+						topicT: '11、本次门诊中存在腹痛症状患者,以下部位腹痛发生的比例是?',
+						topicD: bTwo.join(",")
+					})
+
+					b = []
+					bTwo = []
+					if (!this.daan12radio) {
+						this.$u.toast("请选择第12题")
+						return
+					}
+					b.push(`A 不考虑,认为亚型与治疗方案关系不大 (${this.daan12radio})`)
+					bTwo.push(`${this.daan12radio}`)
+					if (this.daan12radio == '否') {
+						var timu12bili = 0
+						this.timu12.forEach((val, key) => {
+							timu12bili = timu12bili + Number(val)
+							b.push(`${this.daan12[key]}${val || 0}%`)
+							bTwo.push(`${val || 0}%`)
+						})
+						if (timu12bili < 100) {
+							this.$u.toast("第12题比例加起来需为100%")
+							return
+						}
+					}
+					topic.push({
+						topicT: '12、在本次门诊诊断肠易激综合征(IBS)时,您是否会考虑患者的IBS亚型?如考虑,不同亚型的占比都是多少?',
+						topicD: b.join(",")
+					})
+					topicTwo.push({
+						topicT: '12、在本次门诊诊断肠易激综合征(IBS)时,您是否会考虑患者的IBS亚型?如考虑,不同亚型的占比都是多少?',
+						topicD: bTwo.join(",")
+					})
+
+					b = []
+					bTwo = []
+					if (!this.timu13[0] && !this.timu13[5]) {
+						if (this.timu13.indexOf(true) == -1) {
+							this.$u.toast("请选择第13题")
+							return
+						}
+					}
+					this.timu13.forEach((val, key) => {
+						b.push(`${this.daan13[key]}${val ? '是' : '否'}`)
+						bTwo.push(`${val ? '是' : '否'}`)
+					})
+
+					topic.push({
+						topicT: '13、有证据表明解痉剂可改善肠易激综合征(IBS)的症状,对腹痛的疗效显著。若选择奥替溴铵治疗IBS相关的腹痛,您会考虑给以下哪些亚型的肠易激综合征(IBS)患者使用奥替溴铵?',
+						topicD: b.join(",")
+					})
+					topicTwo.push({
+						topicT: '13、有证据表明解痉剂可改善肠易激综合征(IBS)的症状,对腹痛的疗效显著。若选择奥替溴铵治疗IBS相关的腹痛,您会考虑给以下哪些亚型的肠易激综合征(IBS)患者使用奥替溴铵?',
+						topicD: bTwo.join(",")
+					})
+
+					if (!this.timu14 && !this.timu13[5]) {
+						this.$u.toast("请填写第14题")
+						return
+					}
+					var timu14 = "";
+					for (var i = 0; i < this.timu14.length; i++) {
+						timu14 = timu14 + (this.timu14[i] == "\n" ? '' : this.timu14[i])
+					}
+					topic.push({
+						topicT: '14、您认为奥替溴铵对肠易激综合征(IBS)患者最大的优势是什么?',
+						topicD: timu14
+					})
+					topicTwo.push({
+						topicT: '14、您认为奥替溴铵对肠易激综合征(IBS)患者最大的优势是什么?',
+						topicD: timu14
+					})
+
+					if (type == 3) {
+						if (this.changeshowbg(2)) {
+							this.show4 = true
+							return
+						}
+						return
+					}
+				}
+
+				// --------------------------------------------------第四部分--------------------------------------------------
+
+				if ((Number(this.timu4[1]) + Number(this.timu4[2])) > 0) {
+					b = []
+					bTwo = []
+					var timu15bili = 0
+					this.timu15.forEach((val, key) => {
+						timu15bili = timu15bili + Number(val)
+						b.push(`${this.daan15[key]}${val || 0}%`)
+						bTwo.push(`${val || 0}%`)
+					})
+					if (timu15bili < 100) {
+						this.$u.toast("第15题比例加起来需为100%")
+						return
+					}
+					topic.push({
+						topicT: '15、本次门诊溃疡性结肠炎(UC)患者群中,按疾病程度分类,分别占比多少?',
+						topicD: b.join(",")
+					})
+					topicTwo.push({
+						topicT: '15、本次门诊溃疡性结肠炎(UC)患者群中,按疾病程度分类,分别占比多少?',
+						topicD: bTwo.join(",")
+					})
+
+					b = []
+					bTwo = []
+					this.timu16.forEach((val, key) => {
+						b.push(`${this.daan16[key]}${val || 0}%`)
+						bTwo.push(`${val || 0}%`)
+					})
+					topic.push({
+						topicT: '16、本次门诊溃疡性结肠炎(UC)患者就诊原因占比?',
+						topicD: b.join(",")
+					})
+					topicTwo.push({
+						topicT: '16、本次门诊溃疡性结肠炎(UC)患者就诊原因占比?',
+						topicD: bTwo.join(",")
+					})
+
+					b = []
+					bTwo = []
+					this.timu17.forEach((val, key) => {
+						b.push(`${this.daan17[key]}${val || 0}%`)
+						bTwo.push(`${val || 0}%`)
+					})
+					topic.push({
+						topicT: '17、本次门诊针对溃疡性结肠炎(UC)患者所使用的不同治疗方案的占比?',
+						topicD: b.join(",")
+					})
+					topicTwo.push({
+						topicT: '17、本次门诊针对溃疡性结肠炎(UC)患者所使用的不同治疗方案的占比?',
+						topicD: bTwo.join(",")
+					})
+
+					// b = []
+					// bTwo = []
+					// if (!this.timu181 || Number(this.timu18[0]) == 0 || !this.timu182 || Number(this.timu18[1]) == 0 || !this.timu183 || Number(this.timu18[2]) == 0) {
+					// 	this.$u.toast("请完善第18题")
+					// 	return
+					// }
+					// b.push(`A. 缓释片剂 剂量 ${this.timu181} 人数 ${this.timu18[0]}`)
+					// bTwo.push(`${this.timu181}|${this.timu18[0]}`)
+					// b.push(`B. 缓释颗粒 剂量 ${this.timu182} 人数 ${this.timu18[1]}`)
+					// bTwo.push(`${this.timu182}|${this.timu18[1]}`)
+					// b.push(`C. 栓剂 剂量 ${this.timu183} 人数 ${this.timu18[2]}`)
+					// bTwo.push(`${this.timu183}|${this.timu18[2]}`)
+					// topic.push({
+					// 	topicT: '18、针对本次门诊中使用不同剂型美沙拉秦类药物的患者数?',
+					// 	topicD: b.join(",")
+					// })
+					// topicTwo.push({
+					// 	topicT: '18、针对本次门诊中使用不同剂型美沙拉秦类药物的患者数?',
+					// 	topicD: bTwo.join(",")
+					// })
+
+					if (!this.timu18) {
+						this.$u.toast("请填写第18题")
+						return
+					}
+					var timu18 = "";
+					for (var i = 0; i < this.timu18.length; i++) {
+						timu18 = timu18 + (this.timu18[i] == "\n" ? '' : this.timu18[i])
+					}
+					topic.push({
+						topicT: '18、您认为美沙拉秦对溃疡性结肠炎(UC)患者最大的优势是什么?',
+						topicD: timu18
+					})
+					topicTwo.push({
+						topicT: '18、您认为美沙拉秦对溃疡性结肠炎(UC)患者最大的优势是什么?',
+						topicD: timu18
+					})
+				}
+				// --------------------------------------------------完成--------------------------------------------------
+				uni.showLoading({
+					mask: true,
+					title: "请稍后"
+				})
+				this.$http.topicAdd({
+					topicTop: `本次门诊时间 ${this.time.year} 年 ${this.time.month} 月 ${this.time.day} 日   本次门诊人数 ${this.count}(人)`,
+					topic: JSON.stringify(topic),
+					topicTwo: JSON.stringify(topicTwo),
+					phone: this.name,
+					hospital: this.yiyuan,
+					outpatientServiceTime: `${this.time.year}-${this.time.month}-${this.time.day}`
+				}).then(res => {
+					uni.hideLoading()
+					this.$u.toast(res.data.message)
+					if (res.data.code == 200) {
+						setTimeout(() => {
+							uni.navigateBack()
+						}, 800)
+					}
+				})
+			},
+			changeshowbg(type) {
+				if (type == 1) {
+					if (this.timu4[0] > 0) {
+						return true
+					} else {
+						return false
+					}
+				}
+				if (type == 2) {
+					if ((Number(this.timu4[1]) + Number(this.timu4[2])) > 0) {
+						return true
+					} else {
+						return false
+					}
+				}
+			},
+			checkradio131() {
+				if (this.daan13radio2 == '是') {
+					return true
+				}
+				return false
+			},
+			radioGroupChange12(e) {
+				if (e == '是') {
+					this.timu12 = ['', '', '', '']
+				}
+			},
+			changenum(e) {
+				if ([1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 15, 16, 17, 18].includes(this.timuindex)) {
+					if (this.timuindex == 18) {
+						this[`timu${this.timuindex}`][this.daanindex] = e[0] + 1
+					} else if (this.timuindex == 1 || this.timuindex == 6) {
+						this[`timu${this.timuindex}`][this.daanindex] = e[0]
+						if (this.daanindex == 0) {
+							this[`timu${this.timuindex}`][1] = 100 - e[0]
+						} else {
+							this[`timu${this.timuindex}`][0] = 100 - e[0]
+						}
+					} else {
+						this[`timu${this.timuindex}`][this.daanindex] = e[0]
+					}
+				}
+
+				if (this.timuindex == 12) {
+					this.daan12radio = '否'
+				}
+			},
+			open(index, type) {
+				this.timuindex = index
+				this.daanindex = type
+				if ([2, 3, 12, 15].includes(this.timuindex)) {
+					var num = 0
+					this[`timu${index}`].forEach((val, key) => {
+						if (type != key) {
+							num = num + Number(val)
+						}
+					})
+					this.numlist = timu.getNumber(100 - num)
+				} else {
+					this.numlist = JSON.parse(JSON.stringify(numlist))
+					if (index == 18) {
+						this.numlist.shift()
+					}
+				}
+
+				if (this[`timu${index}`][type]) {
+					if (index == 18) {
+						this.defaulenum = [this[`timu${index}`][type] - 1]
+					} else {
+						this.defaulenum = [this[`timu${index}`][type]]
+					}
+				} else {
+					this.defaulenum = [0]
+				}
+				if (index == 12 && this.daan12radio == '是') {
+					return
+				}
+				this.shownum = true
+			},
+			changetime(e) {
+				this.time = e
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.wenjuan {
+		padding: 1rpx 0;
+
+		.tijiao-btn {
+			width: 686rpx;
+			line-height: 96rpx;
+			background: #167FFF;
+			border-radius: 48rpx;
+			text-align: center;
+			margin: 40rpx auto;
+			font-size: 32rpx;
+			font-family: PingFangSC-Regular, PingFang SC;
+			font-weight: 400;
+			color: #FFFFFF;
+		}
+
+		.timu-box {
+			position: relative;
+			z-index: 1;
+			width: 750rpx;
+			padding: 1rpx 0;
+
+			.timu-tijiao {
+				width: 750rpx;
+				height: 166rpx;
+				background: #FFFFFF;
+				box-shadow: 0px -2px 0px 0px rgba(0, 0, 0, 0.03);
+				padding: 10rpx 30rpx 0 30rpx;
+
+				.text1 {
+					width: 330rpx;
+					line-height: 88rpx;
+					background: rgba(22, 127, 255, 0.2);
+					border-radius: 12rpx;
+					font-size: 32rpx;
+					font-family: PingFangSC-Medium, PingFang SC;
+					font-weight: 500;
+					color: #167FFF;
+					text-align: center;
+					margin-right: 30rpx;
+				}
+
+				.text2 {
+					flex: 1;
+					line-height: 88rpx;
+					background: #167FFF;
+					border-radius: 12rpx;
+					text-align: center;
+					font-size: 32rpx;
+					font-family: PingFangSC-Medium, PingFang SC;
+					font-weight: 500;
+					color: #FFFFFF;
+				}
+			}
+
+			.timu-bg {
+				width: 690rpx;
+				background: rgba(255, 255, 255, 0.6);
+				border-radius: 20rpx;
+				border: 2rpx solid #FFFFFF;
+				backdrop-filter: blur(4px);
+				margin: 20rpx auto;
+				padding: 26rpx 20rpx;
+
+				.timu-type {
+					padding: 0 12rpx;
+					line-height: 44rpx;
+					background: #167FFF;
+					border-radius: 6rpx;
+					font-size: 24rpx;
+					font-family: PingFangSC-Regular, PingFang SC;
+					font-weight: 400;
+					color: #FFFFFF;
+					margin-bottom: 32rpx;
+					display: inline-block;
+				}
+			}
+
+			.timu-row {
+				position: relative;
+				padding: 1rpx 0;
+
+				.timu-text {
+					font-size: 28rpx;
+					font-family: PingFangSC-Medium, PingFang SC;
+					font-weight: 500;
+					color: #333333;
+					margin-bottom: 24rpx;
+				}
+
+				.daan-box {
+					.daan-item {
+						width: 312rpx;
+						margin-bottom: 26rpx;
+						height: 68rpx;
+						border-radius: 12rpx;
+						border: 1rpx solid #167FFF;
+						padding: 0 20rpx;
+
+						.timu-input {
+							width: 44rpx;
+						}
+
+						.title {
+							font-size: 28rpx;
+							font-family: PingFangSC-Regular, PingFang SC;
+							font-weight: 400;
+							color: rgba(0, 0, 0, 0.8);
+						}
+					}
+
+					.daan-item1 {
+						background-color: #167FFF;
+						color: #fff;
+
+						.title {
+							color: #fff;
+						}
+					}
+
+				}
+			}
+		}
+
+
+		input {
+			border-bottom: 1rpx solid #333333;
+			height: 30rpx;
+			font-size: 24rpx;
+			// padding: 0 10rpx;
+			box-sizing: border-box;
+			text-align: center;
+		}
+
+		.wenjuan-header {
+			width: 690rpx;
+			// height: 580rpx;
+			background: rgba(255, 255, 255, 0.6);
+			border-radius: 20rpx;
+			border: 2rpx solid #FFFFFF;
+			backdrop-filter: blur(4px);
+			margin: 0 auto;
+			padding: 88rpx 60rpx 74rpx 60rpx;
+
+			.row-tips {
+				width: 400rpx;
+				text-align: center;
+				font-size: 20rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 400;
+				color: #131415;
+				margin: 44rpx auto 0 auto;
+			}
+
+			.header-start {
+				width: 450rpx;
+				line-height: 84rpx;
+				background: #167FFF;
+				border-radius: 16rpx;
+				text-align: center;
+				font-size: 34rpx;
+				font-family: PingFangSC-Medium, PingFang SC;
+				font-weight: 500;
+				color: #FFFFFF;
+				margin: 0 auto;
+			}
+
+			.header-count {
+				margin-bottom: 112rpx;
+
+				.title {
+					font-size: 26rpx;
+					font-family: PingFangSC-Regular, PingFang SC;
+					font-weight: 400;
+					color: #131415;
+				}
+
+				.input1 {
+					width: 150rpx;
+				}
+			}
+
+			.header-text {
+				margin-bottom: 44rpx;
+
+				.title {
+					font-size: 26rpx;
+					font-family: PingFangSC-Regular, PingFang SC;
+					font-weight: 400;
+					color: #131415;
+				}
+
+				.input1 {
+					width: 150rpx;
+				}
+			}
+
+			.header-time {
+				margin-bottom: 44rpx;
+
+				.title {
+					font-size: 26rpx;
+					font-family: PingFangSC-Regular, PingFang SC;
+					font-weight: 400;
+					color: #131415;
+				}
+
+				.wj-time {
+					width: 84rpx;
+				}
+			}
+		}
+
+
+		.wenjian-tips {
+			padding: 0 50rpx;
+			font-size: 22rpx;
+			margin-bottom: 34rpx;
+
+			.text {
+				font-size: 60rpx;
+				font-family: HYYaKuHei-85J, HYYaKuHei;
+				font-weight: normal;
+				color: #167FFF;
+				margin: 54rpx 0 30rpx 0;
+			}
+
+			.text1 {
+				font-size: 24rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 400;
+				color: #167FFF;
+			}
+
+		}
+	}
+</style>

+ 50 - 0
pages/login/agreement.vue

@@ -0,0 +1,50 @@
+<template>
+	<view class="agreement">
+		<view class="con" v-html="data">
+			111
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				data:''
+			}
+		},
+		onLoad(option) {
+			this.configInfo()
+		},
+		methods: {
+			configInfo(){
+				this.$http.configInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						console.log(res)
+						this.data=res.data.result.ysxy
+						// this.data=res.data.result
+						// console.log(this.data.result.ysxy)
+					}
+				})
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.agreement{
+		height: 100%;
+		.con{
+			width:680rpx;
+			margin: 24rpx auto;
+			padding:32rpx;
+			background-color: #fff;
+			border-radius: 24rpx;
+		}
+	}
+</style>

+ 264 - 0
pages/login/forget.vue

@@ -0,0 +1,264 @@
+<template>
+	<view class="forget">
+		<u-toast ref="uToast" />
+		<u-navbar back-text=" " title="修改密码" :background="{backgroundColor: 'transparent'}"></u-navbar>
+		<image class="bgc" src="../../static/img/group.png" mode=""></image>
+		<view class="con">
+			<view class="h2">修改密码</view>
+			<view class="form">
+				<view class="input">
+					<u-input v-model="phone" maxlength="11" :clearable="false" type="number" :border="border" placeholder="请输入手机号" />
+				</view>
+				<view class="input sms flex">
+					<u-input v-model="ver_code" :clearable="false" type="text" :border="border" placeholder="请输入验证码" />
+					<view class="wrap" >
+						<u-toast ref="uToast"></u-toast>
+						<u-verification-code :seconds="seconds" start-text="获取验证码" @end="end" @start="start" ref="uCode" 
+						@change="codeChange"></u-verification-code>
+						<view class="tips" @tap="getCode" >{{tips}}</view>
+					</view>
+				</view>
+				<view class="input">
+					<u-input v-model="password" :clearable="false" type="password" :border="border" placeholder="请输入密码" />
+				</view>
+				<view class="input">
+					<u-input v-model="confirm_password" :clearable="false" type="password" :border="border" placeholder="请重新输入密码" />
+				</view>
+			</view>
+			<view class="btn" @click="ForgetPassword()">
+				修改并登陆
+			</view>
+		</view>
+			<view class="agreement">
+				<u-checkbox-group @change="checkboxGroupChange">
+					<u-checkbox 
+						v-model="checked"	
+						shape="circle"
+						active-color="#167FFF"
+					><view class="text">已阅读并同意<text @click="agreement">《病历评比用户隐私协议》</text></view></u-checkbox>
+				</u-checkbox-group>
+			</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				phone:'',
+				ver_code:'',
+				password:'',
+				confirm_password:"",
+				checked:true,
+				seconds: 60,
+				tips: '获取验证码',
+			}
+		},
+		onLoad(option) {
+		},
+		methods: {
+			agreement(){
+				uni.navigateTo({
+				  url: '/pages/login/agreement',
+				})
+			},
+			checkboxGroupChange(e) {
+				console.log(this.checked);
+			},
+			codeChange(text) {
+					this.tips = text;
+				},
+				// 获取短信
+				getCode() {
+					
+					console.log('发送验证码')
+					if(this.$refs.uCode.canGetCode) {
+						// 模拟向后端请求验证码
+						// uni.showLoading({
+						// 	title: '正在获取验证码'
+						// })
+						console.log(this.phone)
+						if(this.phone==''){
+							this.$refs.uToast.show({
+								title: '请输入手机号',
+								type: 'error ',
+							})
+							return;
+						}
+						if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phone))) {
+							this.$refs.uToast.show({
+								title: '请输入正确的手机号格式',
+								type: 'warning ',
+							})
+							return;
+						}
+						this.$http.smsSend({phone:this.phone,event:'forgetpwd'})
+						.then(res=>{
+							if(res.data.code==200){
+								this.$u.toast('验证码已发送');
+								this.$refs.uCode.start();
+							}else{
+								this.$refs.uToast.show({
+									title: res.data.msg,
+									type: 'error ',
+								})
+							}
+						})
+						// setTimeout(() => {
+						// 	uni.hideLoading();
+						// 	// 这里此提示会被this.start()方法中的提示覆盖
+						// 	this.$u.toast('验证码已发送');
+						// 	// 通知验证码组件内部开始倒计时
+						// 	this.$refs.uCode.start();
+						// }, 2000);
+					} else {
+						this.$u.toast('倒计时结束后再发送');
+					}
+				},
+			// 登录
+			ForgetPassword(){
+				if(this.phone==''){
+					this.$refs.uToast.show({
+						title: '请输入手机号',
+						type: 'error ',
+					})
+					return;
+				}
+				if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phone))) {
+					this.$refs.uToast.show({
+						title: '请输入正确的手机号格式',
+						type: 'warning ',
+					})
+					return;
+				}
+				if(this.ver_code==''){
+					this.$refs.uToast.show({
+						title: '请输入验证码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(this.password==''){
+					this.$refs.uToast.show({
+						title: '请输入密码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(this.confirm_password==''){
+					this.$refs.uToast.show({
+						title: '请输入确认密码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(this.confirm_password!=this.password){
+					this.$refs.uToast.show({
+						title: '两次输入密码不一致',
+						type: 'error ',
+					})
+					return;
+				}
+				if(!this.checked){
+					this.$refs.uToast.show({
+						title: '请选中并阅读隐私协议',
+						type: 'error ',
+					})
+					return;
+				}
+				this.$http.ForgetPassword({
+				  confirm_password: this.confirm_password,
+				  password: this.password,
+				  phone: this.phone,
+				  ver_code: this.ver_code
+				})
+				.then(res=>{
+					console.log(res)
+					if(res.data.code==200){
+						uni.setStorageSync('token', res.data.result.token);
+						this.getUserInfo()
+					}else{
+						this.$refs.uToast.show({
+							title: res.data.message,
+							type: 'error ',
+						})
+					}
+				})
+			},
+			getUserInfo(){
+				this.$http.getUserInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						// 是否信息完整
+						if(res.data.result.completeInformation != 2){
+							uni.navigateTo({
+							  url: '/pages/login/perfect',
+							})
+						}else{
+							uni.navigateTo({
+							  url: '/pages/index/index',
+							})
+						}
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.forget{
+		position: relative;
+		height: 100%;
+		overflow: hidden;
+		box-sizing: border-box;
+		padding: 0 100rpx;
+		.bgc{
+			position: absolute;
+			left:0;
+			top:0;
+			width: 100%;
+			height: 100%;
+		}
+		.con{
+			position: relative;
+			height: 100%;
+			color: #000;
+			.h2{
+				font-size: 56rpx;
+				padding-top: 80rpx;
+			}
+			.form{
+				margin-top: 80rpx;
+			}
+			.u-scroll-box{
+				display: block;
+				.u-tab-item{
+					margin-right: 60rpx;
+				}
+			}
+			.btn{
+				margin-top: 100rpx;
+				height: 80rpx;
+				text-align: center;
+				line-height: 80rpx;
+				background: $color;
+				border-radius: 40rpx;
+				font-size: 28rpx;
+				color:#fff;
+			}
+		}
+		.agreement{
+			position: absolute;
+			bottom:70rpx;
+			text-align: center;
+			font-size: 24rpx;
+			text{
+				color: $color;
+			}
+		}
+	}
+</style>

+ 207 - 0
pages/login/login.vue

@@ -0,0 +1,207 @@
+<template>
+	<view class="login">
+		<u-toast ref="uToast" />
+		<image class="bgc" src="../../static/img/group.png" mode=""></image>
+		<view class="con">
+			<view class="h2">你好,</view>
+			<view class="h3">欢迎使用病例收集及调研问卷项目</view>
+			<u-tabs :list="list" bar-width="140" font-size="36" :gutter="50" bg-color="transparent" :is-scroll="false" :current="current" @change="change"></u-tabs>
+			<view class="form">
+				<view class="input">
+					<u-input v-model="phone" maxlength="11" :clearable="false" type="number" :border="border" placeholder="请输入账号" />
+				</view>
+				<view class="input">
+					<u-input v-model="password" :clearable="false" type="password" :border="border" placeholder="请输入密码" />
+				</view>
+				<view class="forget" @click="forget()">
+					忘记密码
+				</view>
+			</view>
+			<view class="btn" @click="passwordLogin()">
+				登录
+			</view>
+		</view>
+			<view class="agreement flex">
+				<u-checkbox-group @change="checkboxGroupChange">
+					<u-checkbox 
+						v-model="checked"	
+						shape="circle"
+						active-color="#167FFF"
+					></u-checkbox>
+				</u-checkbox-group>
+				<view class="text">已阅读并同意<text @click="agreement()">《病例收集及调研问卷项目用户隐私协议》</text></view>
+			</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				list: [{
+					name: '医生登录'
+				}, {
+					name: '专家登录'
+				}],
+				current: 0,
+				phone:'',
+				password:'',
+				checked:false
+			}
+		},
+		methods: {
+			change(index) {
+				this.current = index;
+			},
+			checkboxGroupChange(e) {
+				console.log(this.checked);
+			},
+			// 登录
+			passwordLogin(){
+				if(this.phone==''){
+					this.$refs.uToast.show({
+						title: '请输入手机号',
+						type: 'error ',
+					})
+					return;
+				}
+				// if(!this.$checkMobile(this.phone)){
+				// 	this.$refs.uToast.show({
+				// 		title: '请输入正确手机号的格式',
+				// 		type: 'error ',
+				// 	})
+				// 	return;
+				// }
+				if(this.password==''){
+					this.$refs.uToast.show({
+						title: '请输入密码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(!this.checked){
+					this.$refs.uToast.show({
+						title: '请选中并阅读隐私协议',
+						type: 'error ',
+					})
+					return;
+				}
+				this.$http.passwordLogin({sf:parseFloat(this.current)+1,phone:this.phone,password:this.password})
+				.then(res=>{
+					if(res.data.code==200){
+						uni.setStorageSync('token', res.data.result.token);
+						this.getUserInfo()
+					}else{
+						this.$refs.uToast.show({
+							title: res.data.message,
+							type: 'error ',
+						})
+					}
+				})
+			},
+			getUserInfo(){
+				this.$http.getUserInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						// 是否信息完整
+						uni.setStorageSync('doctorAndSpecialist', res.data.result.doctorAndSpecialist);
+						if(res.data.result.completeInformation != 2){
+							uni.navigateTo({
+							  url: '/pages/login/perfect',
+							})
+						}else{
+							uni.navigateTo({
+							  url: '/pages/index/index',
+							})
+						}
+					}
+				})
+			},
+			agreement(){
+				uni.navigateTo({
+				  url: '/pages/login/agreement',
+				})
+			},
+			forget(){
+				uni.navigateTo({
+				  url: '/pages/login/forget',
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.login{
+		position: relative;
+		height: 100%;
+		overflow: hidden;
+		box-sizing: border-box;
+		padding: 0 100rpx;
+		.bgc{
+			position: absolute;
+			left:0;
+			top:0;
+			width: 100%;
+			height: 100%;
+		}
+		.con{
+			position: relative;
+			height: 100%;
+			color: #000;
+			.h2{
+				font-size: 56rpx;
+				padding-top: 220rpx;
+			}
+			.h3{
+				font-size: 32rpx;
+				margin-top: 20rpx;
+				margin-bottom:100rpx;
+			}
+			.form{
+				margin-top: 30rpx;
+			}
+			.u-scroll-box{
+				display: block;
+				.u-tab-item{
+					margin-right: 60rpx;
+				}
+			}
+			.forget{
+				text-align: right;
+				font-size: 28rpx;
+				color: rgba(0,0,0,0.35);
+				margin-top: 16rpx;
+			}
+			.btn{
+				margin-top: 100rpx;
+				height: 80rpx;
+				text-align: center;
+				line-height: 80rpx;
+				background: $color;
+				border-radius: 40rpx;
+				font-size: 28rpx;
+				color:#fff;
+			}
+		}
+		.agreement{
+			position: absolute;
+			bottom:70rpx;
+			text-align: center;
+			font-size: 24rpx;
+			.u-checkbox__label{
+				display: none;
+			}			
+			.text{
+				font-size: 24rpx;
+				padding-left: 8rpx;
+				text{
+					color: $color;
+				}
+			}
+		}
+	}
+</style>

+ 329 - 0
pages/login/perfect.vue

@@ -0,0 +1,329 @@
+<template>
+	<view class="perfect">
+		<u-toast ref="uToast" />
+		<view class="head">
+			完善信息
+			<view class="msg">温馨提示:输入姓名要和开户人姓名保持一致。</view>
+		</view>
+		<view class="con">
+			<view class="form">
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li1.png" mode=""></image>
+					</view>
+					<u-input v-model="name" :clearable="false" type="text" :border="border" placeholder="请输入姓名" />
+				</view>
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li2.png" mode=""></image>
+					</view>
+					<u-input v-model="card" maxlength="18" :clearable="false" type="text" :border="border" placeholder="请输入身份证号" />
+				</view>
+				<!-- <view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li3.png" mode=""></image>
+					</view>
+					<u-input v-model="phone" :clearable="false" type="number" :border="border" placeholder="请输入联系电话" />
+				</view> -->
+				<!-- 				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li4.png" mode=""></image>
+					</view>
+					<u-input v-model="contactInformation" :clearable="false" type="text" :border="border" placeholder="请输入联系方式" />
+				</view> -->
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li5.png" mode=""></image>
+					</view>
+					<u-input v-model="dwname" :clearable="false" type="text" :border="border" placeholder="请输入单位名称" />
+				</view>
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li6.png" mode=""></image>
+					</view>
+					<u-input v-model="ksname" :clearable="false" type="text" :border="border" placeholder="请输入科室名称" />
+				</view>
+				<view class="updata">
+					<view class="text">请上传医师执照或工牌照片 </view>
+					<u-upload ref="uUpload1" :show-progress="false" :file-list="fileList" @on-change="progress1" :action="action" max-count="1"></u-upload>
+				</view>
+			</view>
+			<view class="form form1">
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li7.png" mode=""></image>
+					</view>
+					<u-input v-model="backCard" :clearable="false" type="number" :border="border" placeholder="请输入银行账户卡号" />
+				</view>
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li1.png" mode=""></image>
+					</view>
+					<u-input v-model="cardholderName" :clearable="false" type="text" :border="border" placeholder="请输入持卡人姓名" />
+				</view>
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li8.png" mode=""></image>
+					</view>
+					<u-input v-model="cardholderPhone" maxlength="11" :clearable="false" type="number" :border="border" placeholder="请输入银行预留手机号" />
+				</view>
+				<view class="input flex">
+					<view class="icon">
+						<image src="../../static/img/li4.png" mode=""></image>
+					</view>
+					<u-input v-model="backName" :clearable="false" type="text" :border="border" placeholder="请输入开户行" />
+				</view>
+			</view>
+		</view>
+		<view class="btn" @click="completeInformation()">
+			<view class="btn_con">完成</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				// 演示地址,请勿直接使用
+				action: 'http://www.example.com/upload',
+				fileList: [],
+				name: '',
+				card: '',
+				phone: '',
+				contactInformation: '',
+				dwname: '',
+				ksname: '',
+				backCard: '',
+				backName: '',
+				cardholderName: '',
+				cardholderPhone: '',
+				data: {
+					id: '',
+					name: '',
+					card: '',
+					phone: '',
+					contactInformation: '',
+					dwname: '',
+					ksname: '',
+					backCard: '',
+					backName: '',
+					cardholderName: '',
+					cardholderPhone: '',
+					cardPic: '',
+					completeInformation: "2"
+				},
+			}
+		},
+		onLoad(option) {
+			this.action = this.$httpUrl + '/api/uploadLocality'
+			this.getUserInfo()
+		},
+		methods: {
+			progress1(res, index, lists, name) {
+				// console.log(JSON.parse(res.data))
+				if (JSON.parse(res.data).code == 0) {
+					this.$refs.uToast.show({
+						title: JSON.parse(res.data).msg,
+						type: 'error ',
+					})
+					this.$refs.uUpload1.clear()
+				}
+			},
+			getUserInfo() {
+				this.$http.getUserInfo()
+					.then(res => {
+						if (res.data.code == 200) {
+							let data = res.data.result
+							console.log()
+							if (data.cardPic) {
+								this.fileList = [{ url: data.cardPic, }]
+							}
+							this.data.id = data.id
+							this.name = data.name
+							this.card = data.card
+							// console.log(this.card);
+							this.phone = data.phone
+							this.contactInformation = data.contactInformation
+							this.dwname = data.dwname
+							this.ksname = data.ksname
+							this.backCard = data.backCard
+							this.backName = data.backName
+							this.cardholderName = data.cardholderName
+							this.cardholderPhone = data.cardholderPhone
+						}
+					})
+			},
+			completeInformation() {
+				let files1 = [];
+				// 通过filter,筛选出上传进度为100的文件(因为某些上传失败的文件,进度值不为100,这个是可选的操作)
+				files1 = this.$refs.uUpload1.lists.filter(val => {
+					return val.progress == 100;
+				})
+				// console.log(files1)
+				let values = []
+				if (files1.length != 0) {
+					if (files1[0].response) {
+						values = files1.map(item => item.response.result)
+					} else {
+						values = files1.map(item => item.url)
+					}
+				}
+				// if(files1.length==0){
+				// 	this.$refs.uToast.show({ title: '上传执照或工牌', type: 'error ', })
+				// 	return;
+				// }
+				if (!this.name) {
+					this.$refs.uToast.show({ title: '请输入姓名', type: 'error ', })
+					return;
+				}
+				// console.log(this.card);
+				if (this.card) {
+					if (!this.$checkId(this.card)) {
+						this.$refs.uToast.show({
+							title: '请输入正确身份证格式',
+							type: 'error ',
+						})
+						return;
+					}
+				}
+
+				// if(this.phone==''){
+				// 	this.$refs.uToast.show({ title: '请输入手机号', type: 'error ', })
+				// 	return;
+				// }
+				// if(!this.$checkMobile(this.phone)){
+				// 	this.$refs.uToast.show({
+				// 		title: '请输入正确手机号的格式',
+				// 		type: 'error ',
+				// 	})
+				// 	return;
+				// }
+				// if(this.contactInformation==''){
+				// 	this.$refs.uToast.show({ title: '请输入联系方式', type: 'error ', })
+				// 	return;
+				// }
+				// if (!this.dwname) {
+				// 	this.$refs.uToast.show({ title: '请输入单位名称', type: 'error ', })
+				// 	return;
+				// }
+				// if (!this.ksname) {
+				// 	this.$refs.uToast.show({ title: '请输入科室名称', type: 'error ', })
+				// 	return;
+				// }
+				// return
+				this.data.name = this.name
+				this.data.card = this.card
+				this.data.phone = this.phone
+				this.data.contactInformation = this.contactInformation
+				this.data.dwname = this.dwname
+				this.data.ksname = this.ksname
+				this.data.backCard = this.backCard
+				this.data.backName = this.backName
+				this.data.cardholderName = this.cardholderName
+				this.data.cardholderPhone = this.cardholderPhone
+				this.data.cardPic = values.join(',')
+
+				this.$http.completeInformation(this.data)
+					.then(res => {
+						if (res.data.code == 200) {
+							this.$refs.uToast.show({
+								title: '保存完成',
+							})
+							uni.navigateTo({
+								url: '/pages/index/index',
+							})
+						}
+					})
+			}
+		}
+	}
+
+</script>
+
+<style lang="scss">
+	.perfect {
+		position: relative;
+		padding-bottom: 30rpx;
+
+		.head {
+			box-sizing: border-box;
+			height: 432rpx;
+			background: linear-gradient(220deg, #24B8FF 0%, $color 100%);
+			border-radius: 0px 0px 120rpx 0px;
+			padding: 160rpx 48rpx 0;
+			font-size: 56rpx;
+			color: #fff;
+
+			.msg {
+				font-size: 24rpx;
+				color: #fff;
+				margin-top: 12rpx;
+			}
+		}
+
+		.con {
+			position: relative;
+			top: -140rpx;
+			padding: 0 48rpx;
+
+			.form {
+				box-sizing: border-box;
+				background-color: #fff;
+				border-radius: 24rpx;
+				padding: 0 48rpx;
+
+				.icon {
+					margin-right: 20rpx;
+
+					image {
+						width: 40rpx;
+						height: 40rpx;
+					}
+				}
+			}
+
+			.form1 {
+				margin-top: 24rpx;
+
+				.input:last-child {
+					border-bottom: 0;
+				}
+
+			}
+
+			.u-add-tips {
+				display: none;
+			}
+
+			.updata {
+				.text {
+					font-size: 28rpx;
+					color: rgba(0, 0, 0, 0.35);
+					margin: 36rpx 0;
+				}
+			}
+		}
+
+		.btn {
+			position: fixed;
+			bottom: 0;
+			text-align: center;
+			z-index: 11;
+			width: 100%;
+			padding: 12rpx 0;
+			background-color: #fff;
+
+			.btn_con {
+				width: 680rpx;
+				margin: 0 auto;
+				font-size: 32rpx;
+				padding: 30rpx 0;
+				background-color: $color;
+				border-radius: 48rpx;
+				color: #fff;
+			}
+		}
+	}
+
+</style>

+ 259 - 0
pages/user/password.vue

@@ -0,0 +1,259 @@
+<template>
+	<view class="password">
+		<u-toast ref="uToast" />
+		<view class="con">
+			<view class="form">
+				<view class="input flex">
+					<view class="label">手机号</view>
+					<u-input v-model="phone" maxlength="11" :clearable="false" type="number" :border="border" placeholder="请输入手机号" />
+				</view>
+				<view class="input sms flex">
+					<view class="label">验证码</view>
+					<u-input v-model="ver_code" :clearable="false" type="text" :border="border" placeholder="请输入验证码" />
+					<view class="wrap" >
+						<u-toast ref="uToast"></u-toast>
+						<u-verification-code :seconds="seconds" start-text="发送验证码" @end="end" @start="start" ref="uCode" 
+						@change="codeChange"></u-verification-code>
+						<view class="tips" @tap="getCode" >{{tips}}</view>
+					</view>
+				</view>
+				<view class="input flex">
+					<view class="label">密码</view>
+					<u-input v-model="password" :clearable="false" type="password" :border="border" placeholder="请输入密码" />
+				</view>
+				<view class="input flex">
+					<view class="label">确认密码</view>
+					<u-input v-model="confirm_password" :clearable="false" type="password" :border="border" placeholder="请重新输入密码" />
+				</view>
+			</view>
+			
+		</view>
+		<view class="btn" @click="ForgetPassword()">
+			提交
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				phone:'',
+				ver_code:'',
+				password:'',
+				confirm_password:"",
+				checked:true,
+				seconds: 60,
+				tips: '获取验证码',
+			}
+		},
+		onLoad(option) {
+			this.getUserInfo()
+		},
+		methods: {
+			agreement(){
+				uni.navigateTo({
+				  url: '/pages/login/agreement',
+				})
+			},
+			checkboxGroupChange(e) {
+				console.log(this.checked);
+			},
+			codeChange(text) {
+					this.tips = text;
+				},
+				// 获取短信
+				getCode() {
+					
+					console.log('发送验证码')
+					if(this.$refs.uCode.canGetCode) {
+						// 模拟向后端请求验证码
+						// uni.showLoading({
+						// 	title: '正在获取验证码'
+						// })
+						console.log(this.phone)
+						if(this.phone==''){
+							this.$refs.uToast.show({
+								title: '请输入手机号',
+								type: 'error ',
+							})
+							return;
+						}
+						if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phone))) {
+							this.$refs.uToast.show({
+								title: '请输入正确的手机号格式',
+								type: 'warning ',
+							})
+							return;
+						}
+						this.$http.smsSend({phone:this.phone,event:'forgetpwd'})
+						.then(res=>{
+							if(res.data.code==200){
+								this.$u.toast('验证码已发送');
+								this.$refs.uCode.start();
+							}else{
+								this.$refs.uToast.show({
+									title: res.data.msg,
+									type: 'error ',
+								})
+							}
+						})
+						// setTimeout(() => {
+						// 	uni.hideLoading();
+						// 	// 这里此提示会被this.start()方法中的提示覆盖
+						// 	this.$u.toast('验证码已发送');
+						// 	// 通知验证码组件内部开始倒计时
+						// 	this.$refs.uCode.start();
+						// }, 2000);
+					} else {
+						this.$u.toast('倒计时结束后再发送');
+					}
+				},
+			// 登录
+			ForgetPassword(){
+				if(this.phone==''){
+					this.$refs.uToast.show({
+						title: '请输入手机号',
+						type: 'error ',
+					})
+					return;
+				}
+				if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(this.phone))) {
+					this.$refs.uToast.show({
+						title: '请输入正确的手机号格式',
+						type: 'warning ',
+					})
+					return;
+				}
+				if(this.ver_code==''){
+					this.$refs.uToast.show({
+						title: '请输入验证码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(this.password==''){
+					this.$refs.uToast.show({
+						title: '请输入密码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(this.confirm_password==''){
+					this.$refs.uToast.show({
+						title: '请输入确认密码',
+						type: 'error ',
+					})
+					return;
+				}
+				if(this.confirm_password!=this.password){
+					this.$refs.uToast.show({
+						title: '两次输入密码不一致',
+						type: 'error ',
+					})
+					return;
+				}
+				this.$http.ForgetPassword({
+				  confirm_password: this.confirm_password,
+				  password: this.password,
+				  phone: this.phone,
+				  ver_code: this.ver_code
+				})
+				.then(res=>{
+					console.log(res)
+					if(res.data.code==200){
+						this.$refs.uToast.show({
+							title: '修改成功',
+						})
+						uni.navigateTo({
+						  url: '/pages/index/my',
+						})
+					}else{
+						this.$refs.uToast.show({
+							title: res.data.message,
+							type: 'error ',
+						})
+					}
+				})
+			},
+			getUserInfo(){
+				this.$http.getUserInfo()
+				.then(res=>{
+					if(res.data.code==200){
+						// 是否信息完整
+						if(res.data.result.card==null){
+							uni.navigateTo({
+							  url: '/pages/login/perfect',
+							})
+						}
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.password{
+		position: relative;
+		height: 100%;
+		overflow: hidden;
+		box-sizing: border-box;
+		.bgc{
+			position: absolute;
+			left:0;
+			top:0;
+			width: 100%;
+			height: 100%;
+		}
+		.con{
+			position: relative;
+			height: 100%;
+			color: #000;
+			background-color: #fff;
+			.h2{
+				font-size: 56rpx;
+				padding-top: 80rpx;
+			}
+			.form{
+				margin-top: 20rpx;
+			}
+			.u-scroll-box{
+				display: block;
+				.u-tab-item{
+					margin-right: 60rpx;
+				}
+			}
+		}
+			.btn{
+				width: 570rpx;
+				margin: 100rpx auto 0;
+				height: 80rpx;
+				text-align: center;
+				line-height: 80rpx;
+				background: $color;
+				border-radius: 40rpx;
+				font-size: 28rpx;
+				color:#fff;
+			}
+		.input{
+			padding: 26rpx 32rpx;
+			.label{
+				width: 200rpx;
+				color: rgba(0,0,0,0.8);
+			}
+			u-input{
+				flex: 1;
+			}
+		}
+		.agreement{
+			position: absolute;
+			bottom:70rpx;
+			text-align: center;
+			font-size: 24rpx;
+			text{
+				color: $color;
+			}
+		}
+	}
+</style>

BIN
static/img/Frame@2x(6).png


BIN
static/img/Frame@2x(7).png


BIN
static/img/P.png


BIN
static/img/V.png


BIN
static/img/bg@2x.png


BIN
static/img/bingli.png


BIN
static/img/calendar.png


BIN
static/img/calendar_acitve.png


BIN
static/img/group.png


BIN
static/img/guide.png


BIN
static/img/guide_active.png


BIN
static/img/index.png


BIN
static/img/index_active.png


BIN
static/img/jia.png


BIN
static/img/li1.png


BIN
static/img/li2.png


BIN
static/img/li3.png


BIN
static/img/li4.png


BIN
static/img/li5.png


BIN
static/img/li6.png


BIN
static/img/li7.png


BIN
static/img/li8.png


BIN
static/img/my.png


BIN
static/img/my1.png


BIN
static/img/my2.png


BIN
static/img/myBGC.png


BIN
static/img/my_acitve.png


BIN
static/img/no.png


BIN
static/img/null.png


BIN
static/img/r.png


BIN
static/img/right.png


BIN
static/img/suc.png


BIN
static/img/up.png


BIN
static/img/upButton.png


BIN
static/img/upData_up.png


BIN
static/img/up_r.png


BIN
static/img/wenjuan.png


BIN
static/img/下载.jfif


+ 7 - 0
uni.scss

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

+ 21 - 0
uview-ui/LICENSE

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

+ 106 - 0
uview-ui/README.md

@@ -0,0 +1,106 @@
+<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</h3>
+<h3 align="center">多平台快速开发的UI框架</h3>
+
+
+## 说明
+
+uView UI,是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
+
+## 特性
+
+- 兼容安卓,iOS,微信小程序,H5,QQ小程序,百度小程序,支付宝小程序,头条小程序
+- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用
+- 众多贴心的JS利器,让您飞镖在手,召之即来,百步穿杨
+- 众多的常用页面和布局,让您专注逻辑,事半功倍
+- 详尽的文档支持,现代化的演示效果
+- 按需引入,精简打包体积
+
+
+## 安装
+
+```bash
+# npm方式安装
+npm i uview-ui
+```
+
+## 快速上手
+
+1. `main.js`引入uView库
+```js
+// main.js
+import uView from 'uview-ui';
+Vue.use(uView);
+```
+
+2. `App.vue`引入基础样式(注意style标签需声明scss属性支持)
+```css
+/* App.vue */
+<style lang="scss">
+@import "uview-ui/index.scss";
+</style>
+```
+
+3. `uni.scss`引入全局scss变量文件
+```css
+/* uni.scss */
+@import "uview-ui/theme.scss";
+```
+
+4. `pages.json`配置easycom规则(按需引入)
+
+```js
+// pages.json
+{
+	"easycom": {
+		// npm安装的方式不需要前面的"@/",下载安装的方式需要"@/"
+		// npm安装方式
+		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+		// 下载安装方式
+		// "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
+	},
+	// 此为本身已有的内容
+	"pages": [
+		// ......
+	]
+}
+```
+
+请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容 
+
+## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<u-button>按钮</u-button>
+</template>
+```
+
+请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容 
+
+## 链接
+
+- [官方文档](https://uviewui.com/)
+- [更新日志](https://uviewui.com/components/changelog.html)
+- [升级指南](https://uviewui.com/components/changelog.html)
+- [关于我们](https://uviewui.com/cooperation/about.html)
+
+## 预览
+
+您可以通过**微信**扫码,查看最佳的演示效果。
+<br>
+<br>
+<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
+
+<!-- ## 捐赠uView的研发
+
+uView文档和源码全部开源免费,如果您认为uView帮到了您的开发工作,您可以捐赠uView的研发工作,捐赠无门槛,哪怕是一杯可乐也好(相信这比打赏主播更有意义)。
+
+<img src="https://uviewui.com/common/wechat.png" width="220" >
+<img style="margin-left: 100px;" src="https://uviewui.com/common/alipay.png" width="220" >
+ -->
+## 版权信息
+uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。

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

@@ -0,0 +1,190 @@
+<template>
+	<u-popup mode="bottom" :border-radius="borderRadius" :popup="false" v-model="value" :maskCloseAble="maskCloseAble"
+	    length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="popupClose" :z-index="uZIndex">
+		<view class="u-tips u-border-bottom" v-if="tips.text" :style="[tipsStyle]">
+			{{tips.text}}
+		</view>
+		<block v-for="(item, index) in list" :key="index">
+			<view 
+				@touchmove.stop.prevent 
+				@tap="itemClick(index)" 
+				:style="[itemStyle(index)]" 
+				class="u-action-sheet-item u-line-1" 
+				:class="[index < list.length - 1 ? 'u-border-bottom' : '']"
+				:hover-stay-time="150"
+			>
+				<text>{{item.text}}</text>
+				<text class="u-action-sheet-item__subtext u-line-1" v-if="item.subText">{{item.subText}}</text>
+			</view>
+		</block>
+		<view class="u-gab" v-if="cancelBtn">
+		</view>
+		<view @touchmove.stop.prevent class="u-actionsheet-cancel u-action-sheet-item" hover-class="u-hover-class"
+		    :hover-stay-time="150" v-if="cancelBtn" @tap="close">{{cancelText}}</view>
+	</u-popup>
+</template>
+
+<script>
+	/**
+	 * actionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://www.uviewui.com/components/actionSheet.html
+	 * @property {Array<Object>} list 按钮的文字数组,见官方文档示例
+	 * @property {Object} tips 顶部的提示文字,见官方文档示例
+	 * @property {String} cancel-text 取消按钮的提示文字
+	 * @property {Boolean} cancel-btn 是否显示底部的取消按钮(默认true)
+	 * @property {Number String} border-radius 弹出部分顶部左右的圆角值,单位rpx(默认0)
+	 * @property {Boolean} mask-close-able 点击遮罩是否可以关闭(默认true)
+	 * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
+	 * @property {Number String} z-index z-index值(默认1075)
+	 * @property {String} cancel-text 取消按钮的提示文字
+	 * @event {Function} click 点击ActionSheet列表项时触发
+	 * @event {Function} close 点击取消按钮时触发
+	 * @example <u-action-sheet :list="list" @click="click" v-model="show"></u-action-sheet>
+	 */
+	export default {
+		name: "u-action-sheet",
+		props: {
+			// 点击遮罩是否可以关闭actionsheet
+			maskCloseAble: {
+				type: Boolean,
+				default: true
+			},
+			// 按钮的文字数组,可以自定义颜色和字体大小,字体单位为rpx
+			list: {
+				type: Array,
+				default () {
+					// 如下
+					// return [{
+					// 	text: '确定',
+					// 	color: '',
+					// 	fontSize: ''
+					// }]
+					return [];
+				}
+			},
+			// 顶部的提示文字
+			tips: {
+				type: Object,
+				default () {
+					return {
+						text: '',
+						color: '',
+						fontSize: '26'
+					}
+				}
+			},
+			// 底部的取消按钮
+			cancelBtn: {
+				type: Boolean,
+				default: true
+			},
+			// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
+			safeAreaInsetBottom: {
+				type: Boolean,
+				default: false
+			},
+			// 通过双向绑定控制组件的弹出与收起
+			value: {
+				type: Boolean,
+				default: false
+			},
+			// 弹出的顶部圆角值
+			borderRadius: {
+				type: [String, Number],
+				default: 0
+			},
+			// 弹出的z-index值
+			zIndex: {
+				type: [String, Number],
+				default: 0
+			},
+			// 取消按钮的文字提示
+			cancelText: {
+				type: String,
+				default: '取消'
+			}
+		},
+		computed: {
+			// 顶部提示的样式
+			tipsStyle() {
+				let style = {};
+				if (this.tips.color) style.color = this.tips.color;
+				if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx';
+				return style;
+			},
+			// 操作项目的样式
+			itemStyle() {
+				return (index) => {
+					let style = {};
+					if (this.list[index].color) style.color = this.list[index].color;
+					if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx';
+					// 选项被禁用的样式
+					if (this.list[index].disabled) style.color = '#c0c4cc';
+					return style;
+				}
+			},
+			uZIndex() {
+				// 如果用户有传递z-index值,优先使用
+				return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
+			}
+		},
+		methods: {
+			// 点击取消按钮
+			close() {
+				// 发送input事件,并不会作用于父组件,而是要设置组件内部通过props传递的value参数
+				// 这是一个vue发送事件的特殊用法
+				this.popupClose();
+				this.$emit('close');
+			},
+			// 弹窗关闭
+			popupClose() {
+				this.$emit('input', false);
+			},
+			// 点击某一个item
+			itemClick(index) {
+				// disabled的项禁止点击
+				if(this.list[index].disabled) return;
+				this.$emit('click', index);
+				this.$emit('input', false);
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+
+	.u-tips {
+		font-size: 26rpx;
+		text-align: center;
+		padding: 34rpx 0;
+		line-height: 1;
+		color: $u-tips-color;
+	}
+
+	.u-action-sheet-item {
+		@include vue-flex;;
+		line-height: 1;
+		justify-content: center;
+		align-items: center;
+		font-size: 32rpx;
+		padding: 34rpx 0;
+		flex-direction: column;
+	}
+	
+	.u-action-sheet-item__subtext {
+		font-size: 24rpx;
+		color: $u-tips-color;
+		margin-top: 20rpx;
+	}
+
+	.u-gab {
+		height: 12rpx;
+		background-color: rgb(234, 234, 236);
+	}
+
+	.u-actionsheet-cancel {
+		color: $u-main-color;
+	}
+</style>

+ 256 - 0
uview-ui/components/u-alert-tips/u-alert-tips.vue

@@ -0,0 +1,256 @@
+<template>
+	<view class="u-alert-tips" v-if="show" :class="[
+		!show ? 'u-close-alert-tips': '',
+		type ? 'u-alert-tips--bg--' + type + '-light' : '',
+		type ? 'u-alert-tips--border--' + type + '-disabled' : '',
+	]" :style="{
+		backgroundColor: bgColor,
+		borderColor: borderColor
+	}">
+		<view class="u-icon-wrap">
+			<u-icon v-if="showIcon" :name="uIcon" :size="description ? 40 : 32" class="u-icon" :color="uIconType" :custom-style="iconStyle"></u-icon>
+		</view>
+		<view class="u-alert-content" @tap.stop="click">
+			<view class="u-alert-title" :style="[uTitleStyle]">
+				{{title}}
+			</view>
+			<view v-if="description" class="u-alert-desc" :style="[descStyle]">
+				{{description}}
+			</view>
+		</view>
+		<view class="u-icon-wrap">
+			<u-icon @click="close" v-if="closeAble && !closeText" hoverClass="u-type-error-hover-color" name="close" color="#c0c4cc"
+			 :size="22" class="u-close-icon" :style="{
+				top: description ? '18rpx' : '24rpx'
+			}"></u-icon>
+		</view>
+		<text v-if="closeAble && closeText" class="u-close-text" :style="{
+			top: description ? '18rpx' : '24rpx'
+		}">{{closeText}}</text>
+	</view>
+</template>
+
+<script>
+	/**
+	 * alertTips 警告提示
+	 * @description 警告提示,展现需要关注的信息
+	 * @tutorial https://uviewui.com/components/alertTips.html
+	 * @property {String} title 显示的标题文字
+	 * @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
+	 * @property {String} type 关闭按钮(默认为叉号icon图标)
+	 * @property {String} icon 图标名称
+	 * @property {Object} icon-style 图标的样式,对象形式
+	 * @property {Object} title-style 标题的样式,对象形式
+	 * @property {Object} desc-style 描述的样式,对象形式
+	 * @property {String} close-able 用文字替代关闭图标,close-able为true时有效
+	 * @property {Boolean} show-icon 是否显示左边的辅助图标
+	 * @property {Boolean} show 显示或隐藏组件
+	 * @event {Function} click 点击组件时触发
+	 * @event {Function} close 点击关闭按钮时触发
+	 */
+	export default {
+		name: 'u-alert-tips',
+		props: {
+			// 显示文字
+			title: {
+				type: String,
+				default: ''
+			},
+			// 主题,success/warning/info/error
+			type: {
+				type: String,
+				default: 'warning'
+			},
+			// 辅助性文字
+			description: {
+				type: String,
+				default: ''
+			},
+			// 是否可关闭
+			closeAble: {
+				type: Boolean,
+				default: false
+			},
+			// 关闭按钮自定义文本
+			closeText: {
+				type: String,
+				default: ''
+			},
+			// 是否显示图标
+			showIcon: {
+				type: Boolean,
+				default: false
+			},
+			// 文字颜色,如果定义了color值,icon会失效
+			color: {
+				type: String,
+				default: ''
+			},
+			// 背景颜色
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			// 边框颜色
+			borderColor: {
+				type: String,
+				default: ''
+			},
+			// 是否显示
+			show: {
+				type: Boolean,
+				default: true
+			},
+			// 左边显示的icon
+			icon: {
+				type: String,
+				default: ''
+			},
+			// icon的样式
+			iconStyle: {
+				type: Object,
+				default() {
+					return {}
+				}
+			},
+			// 标题的样式
+			titleStyle: {
+				type: Object,
+				default() {
+					return {}
+				}
+			},
+			// 描述文字的样式
+			descStyle: {
+				type: Object,
+				default() {
+					return {}
+				}
+			},
+		},
+		data() {
+			return {
+			}
+		},
+		computed: {
+			uTitleStyle() {
+				let style = {};
+				// 如果有描述文字的话,标题进行加粗
+				style.fontWeight = this.description ? 500 : 'normal';
+				// 将用户传入样式对象和style合并,传入的优先级比style高,同属性会被覆盖
+				return this.$u.deepMerge(style, this.titleStyle);
+			},
+			uIcon() {
+				// 如果有设置icon名称就使用,否则根据type主题,推定一个默认的图标
+				return this.icon ? this.icon : this.$u.type2icon(this.type);
+			},
+			uIconType() {
+				// 如果有设置图标的样式,优先使用,没有的话,则用type的样式
+				return Object.keys(this.iconStyle).length ? '' : this.type;
+			}
+		},
+		methods: {
+			// 点击内容
+			click() {
+				this.$emit('click');
+			},
+			// 点击关闭按钮
+			close() {
+				this.$emit('close');
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+	
+	.u-alert-tips {
+		@include vue-flex;
+		align-items: center;
+		padding: 16rpx 30rpx;
+		border-radius: 8rpx;
+		position: relative;
+		transition: all 0.3s linear;
+		border: 1px solid #fff;
+		
+		&--bg--primary-light {
+			background-color: $u-type-primary-light;
+		}
+		
+		&--bg--info-light {
+			background-color: $u-type-info-light;
+		}
+		
+		&--bg--success-light {
+			background-color: $u-type-success-light;
+		}
+		
+		&--bg--warning-light {
+			background-color: $u-type-warning-light;
+		}
+		
+		&--bg--error-light {
+			background-color: $u-type-error-light;
+		}
+		
+		&--border--primary-disabled {
+			border-color: $u-type-primary-disabled;
+		}
+		
+		&--border--success-disabled {
+			border-color: $u-type-success-disabled;
+		}
+		
+		&--border--error-disabled {
+			border-color: $u-type-error-disabled;
+		}
+		
+		&--border--warning-disabled {
+			border-color: $u-type-warning-disabled;
+		}
+		
+		&--border--info-disabled {
+			border-color: $u-type-info-disabled;
+		}
+	}
+
+	.u-close-alert-tips {
+		opacity: 0;
+		visibility: hidden;
+	}
+
+	.u-icon {
+		margin-right: 16rpx;
+	}
+
+	.u-alert-title {
+		font-size: 28rpx;
+		color: $u-main-color;
+	}
+
+	.u-alert-desc {
+		font-size: 26rpx;
+		text-align: left;
+		color: $u-content-color;
+	}
+
+	.u-close-icon {
+		position: absolute;
+		top: 20rpx;
+		right: 20rpx;
+	}
+
+	.u-close-hover {
+		color: red;
+	}
+	
+	.u-close-text {
+		font-size: 24rpx;
+		color: $u-tips-color;
+		position: absolute;
+		top: 20rpx;
+		right: 20rpx;
+		line-height: 1;
+	}
+</style>

+ 290 - 0
uview-ui/components/u-avatar-cropper/u-avatar-cropper.vue

@@ -0,0 +1,290 @@
+<template>
+	<view class="content">
+		<view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }">
+			<canvas
+				class="cropper"
+				:disable-scroll="true"
+				@touchstart="touchStart"
+				@touchmove="touchMove"
+				@touchend="touchEnd"
+				:style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }"
+				canvas-id="cropper"
+				id="cropper"
+			></canvas>
+			<canvas
+				class="cropper"
+				:disable-scroll="true"
+				:style="{
+					position: 'fixed',
+					top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
+					left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
+					width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
+					height: `${cropperOpt.height * cropperOpt.pixelRatio}`
+				}"
+				canvas-id="targetId"
+				id="targetId"
+			></canvas>
+		</view>
+		<view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px' }">
+			<!-- #ifdef H5 -->
+			<view class="upload" @tap="uploadTap">选择图片</view>
+			<!-- #endif -->
+			<!-- #ifndef H5 -->
+			<view class="upload" @tap="uploadTap">重新选择</view>
+			<!-- #endif -->
+			<view class="getCropperImage" @tap="getCropperImage(false)">确定</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import WeCropper from './weCropper.js';
+export default {
+	props: {
+		// 裁剪矩形框的样式,其中可包含的属性为lineWidth-边框宽度(单位rpx),color: 边框颜色,
+		// mask-遮罩颜色,一般设置为一个rgba的透明度,如"rgba(0, 0, 0, 0.35)"
+		boundStyle: {
+			type: Object,
+			default() {
+				return {
+					lineWidth: 4,
+					borderColor: 'rgb(245, 245, 245)',
+					mask: 'rgba(0, 0, 0, 0.35)'
+				};
+			}
+		}
+		// // 裁剪框宽度,单位rpx
+		// rectWidth: {
+		// 	type: [String, Number],
+		// 	default: 400
+		// },
+		// // 裁剪框高度,单位rpx
+		// rectHeight: {
+		// 	type: [String, Number],
+		// 	default: 400
+		// },
+		// // 输出图片宽度,单位rpx
+		// destWidth: {
+		// 	type: [String, Number],
+		// 	default: 400
+		// },
+		// // 输出图片高度,单位rpx
+		// destHeight: {
+		// 	type: [String, Number],
+		// 	default: 400
+		// },
+		// // 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可
+		// fileType: {
+		// 	type: String,
+		// 	default: 'jpg',
+		// },
+		// // 生成的图片质量
+		// // H5上无效,目前不考虑使用此参数
+		// quality: {
+		// 	type: [Number, String],
+		// 	default: 1
+		// }
+	},
+	data() {
+		return {
+			// 底部导航的高度
+			bottomNavHeight: 50,
+			originWidth: 200,
+			width: 0,
+			height: 0,
+			cropperOpt: {
+				id: 'cropper',
+				targetId: 'targetCropper',
+				pixelRatio: 1,
+				width: 0,
+				height: 0,
+				scale: 2.5,
+				zoom: 8,
+				cut: {
+					x: (this.width - this.originWidth) / 2,
+					y: (this.height - this.originWidth) / 2,
+					width: this.originWidth,
+					height: this.originWidth
+				},
+				boundStyle: {
+					lineWidth: uni.upx2px(this.boundStyle.lineWidth),
+					mask: this.boundStyle.mask,
+					color: this.boundStyle.borderColor
+				}
+			},
+			// 裁剪框和输出图片的尺寸,高度默认等于宽度
+			// 输出图片宽度,单位px
+			destWidth: 200,
+			// 裁剪框宽度,单位px
+			rectWidth: 200,
+			// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
+			fileType: 'jpg',
+			src: '', // 选择的图片路径,用于在点击确定时,判断是否选择了图片
+		};
+	},
+	onLoad(option) {
+		let rectInfo = uni.getSystemInfoSync();
+		this.width = rectInfo.windowWidth;
+		this.height = rectInfo.windowHeight - this.bottomNavHeight;
+		this.cropperOpt.width = this.width;
+		this.cropperOpt.height = this.height;
+		this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
+
+		if (option.destWidth) this.destWidth = option.destWidth;
+		if (option.rectWidth) {
+			let rectWidth = Number(option.rectWidth);
+			this.cropperOpt.cut = {
+				x: (this.width - rectWidth) / 2,
+				y: (this.height - rectWidth) / 2,
+				width: rectWidth,
+				height: rectWidth
+			};
+		}
+		this.rectWidth = option.rectWidth;
+		if (option.fileType) this.fileType = option.fileType;
+		// 初始化
+		this.cropper = new WeCropper(this.cropperOpt)
+			.on('ready', ctx => {
+				// wecropper is ready for work!
+			})
+			.on('beforeImageLoad', ctx => {
+				// before picture loaded, i can do something
+			})
+			.on('imageLoad', ctx => {
+				// picture loaded
+			})
+			.on('beforeDraw', (ctx, instance) => {
+				// before canvas draw,i can do something
+			});
+		// 设置导航栏样式,以免用户在page.json中没有设置为黑色背景
+		uni.setNavigationBarColor({
+			frontColor: '#ffffff',
+			backgroundColor: '#000000'
+		});
+		uni.chooseImage({
+			count: 1, // 默认9
+			sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
+			sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
+			success: res => {
+				this.src = res.tempFilePaths[0];
+				//  获取裁剪图片资源后,给data添加src属性及其值
+				this.cropper.pushOrign(this.src);
+			}
+		});
+	},
+	methods: {
+		touchStart(e) {
+			this.cropper.touchStart(e);
+		},
+		touchMove(e) {
+			this.cropper.touchMove(e);
+		},
+		touchEnd(e) {
+			this.cropper.touchEnd(e);
+		},
+		getCropperImage(isPre = false) {
+			if(!this.src) return this.$u.toast('请先选择图片再裁剪');
+
+			let cropper_opt = {
+				destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值
+				destWidth: Number(this.destWidth),
+				fileType: this.fileType
+			};
+			this.cropper.getCropperImage(cropper_opt, (path, err) => {
+				if (err) {
+					uni.showModal({
+						title: '温馨提示',
+						content: err.message
+					});
+				} else {
+					if (isPre) {
+						uni.previewImage({
+							current: '', // 当前显示图片的 http 链接
+							urls: [path] // 需要预览的图片 http 链接列表
+						});
+					} else {
+						uni.$emit('uAvatarCropper', path);
+						this.$u.route({
+							type: 'back'
+						});
+					}
+				}
+			});
+		},
+		uploadTap() {
+			const self = this;
+			uni.chooseImage({
+				count: 1, // 默认9
+				sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
+				sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
+				success: (res) => {
+					self.src = res.tempFilePaths[0];
+					//  获取裁剪图片资源后,给data添加src属性及其值
+
+					self.cropper.pushOrign(this.src);
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style scoped lang="scss">
+@import '../../libs/css/style.components.scss';
+
+.content {
+	background: rgba(255, 255, 255, 1);
+}
+
+.cropper {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	z-index: 11;
+}
+
+.cropper-buttons {
+	background-color: #000000;
+	color: #eee;
+}
+
+.cropper-wrapper {
+	position: relative;
+	@include vue-flex;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: center;
+	width: 100%;
+	background-color: #000;
+}
+
+.cropper-buttons {
+	width: 100vw;
+	@include vue-flex;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: center;
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	font-size: 28rpx;
+}
+
+.cropper-buttons .upload,
+.cropper-buttons .getCropperImage {
+	width: 50%;
+	text-align: center;
+}
+
+.cropper-buttons .upload {
+	text-align: left;
+	padding-left: 50rpx;
+}
+
+.cropper-buttons .getCropperImage {
+	text-align: right;
+	padding-right: 50rpx;
+}
+</style>

+ 1265 - 0
uview-ui/components/u-avatar-cropper/weCropper.js

@@ -0,0 +1,1265 @@
+/**
+ * we-cropper v1.3.9
+ * (c) 2020 dlhandsome
+ * @license MIT
+ */
+(function(global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+		typeof define === 'function' && define.amd ? define(factory) :
+		(global.WeCropper = factory());
+}(this, (function() {
+	'use strict';
+
+	var device = void 0;
+	var TOUCH_STATE = ['touchstarted', 'touchmoved', 'touchended'];
+
+	function firstLetterUpper(str) {
+		return str.charAt(0).toUpperCase() + str.slice(1)
+	}
+
+	function setTouchState(instance) {
+		var arg = [],
+			len = arguments.length - 1;
+		while (len-- > 0) arg[len] = arguments[len + 1];
+
+		TOUCH_STATE.forEach(function(key, i) {
+			if (arg[i] !== undefined) {
+				instance[key] = arg[i];
+			}
+		});
+	}
+
+	function validator(instance, o) {
+		Object.defineProperties(instance, o);
+	}
+
+	function getDevice() {
+		if (!device) {
+			device = uni.getSystemInfoSync();
+		}
+		return device
+	}
+
+	var tmp = {};
+
+	var ref = getDevice();
+	var pixelRatio = ref.pixelRatio;
+
+	var DEFAULT = {
+		id: {
+			default: 'cropper',
+			get: function get() {
+				return tmp.id
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'string') {
+					console.error(("id:" + value + " is invalid"));
+				}
+				tmp.id = value;
+			}
+		},
+		width: {
+			default: 750,
+			get: function get() {
+				return tmp.width
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'number') {
+					console.error(("width:" + value + " is invalid"));
+				}
+				tmp.width = value;
+			}
+		},
+		height: {
+			default: 750,
+			get: function get() {
+				return tmp.height
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'number') {
+					console.error(("height:" + value + " is invalid"));
+				}
+				tmp.height = value;
+			}
+		},
+		pixelRatio: {
+			default: pixelRatio,
+			get: function get() {
+				return tmp.pixelRatio
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'number') {
+					console.error(("pixelRatio:" + value + " is invalid"));
+				}
+				tmp.pixelRatio = value;
+			}
+		},
+		scale: {
+			default: 2.5,
+			get: function get() {
+				return tmp.scale
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'number') {
+					console.error(("scale:" + value + " is invalid"));
+				}
+				tmp.scale = value;
+			}
+		},
+		zoom: {
+			default: 5,
+			get: function get() {
+				return tmp.zoom
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'number') {
+					console.error(("zoom:" + value + " is invalid"));
+				} else if (value < 0 || value > 10) {
+					console.error("zoom should be ranged in 0 ~ 10");
+				}
+				tmp.zoom = value;
+			}
+		},
+		src: {
+			default: '',
+			get: function get() {
+				return tmp.src
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'string') {
+					console.error(("src:" + value + " is invalid"));
+				}
+				tmp.src = value;
+			}
+		},
+		cut: {
+			default: {},
+			get: function get() {
+				return tmp.cut
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'object') {
+					console.error(("cut:" + value + " is invalid"));
+				}
+				tmp.cut = value;
+			}
+		},
+		boundStyle: {
+			default: {},
+			get: function get() {
+				return tmp.boundStyle
+			},
+			set: function set(value) {
+				if (typeof(value) !== 'object') {
+					console.error(("boundStyle:" + value + " is invalid"));
+				}
+				tmp.boundStyle = value;
+			}
+		},
+		onReady: {
+			default: null,
+			get: function get() {
+				return tmp.ready
+			},
+			set: function set(value) {
+				tmp.ready = value;
+			}
+		},
+		onBeforeImageLoad: {
+			default: null,
+			get: function get() {
+				return tmp.beforeImageLoad
+			},
+			set: function set(value) {
+				tmp.beforeImageLoad = value;
+			}
+		},
+		onImageLoad: {
+			default: null,
+			get: function get() {
+				return tmp.imageLoad
+			},
+			set: function set(value) {
+				tmp.imageLoad = value;
+			}
+		},
+		onBeforeDraw: {
+			default: null,
+			get: function get() {
+				return tmp.beforeDraw
+			},
+			set: function set(value) {
+				tmp.beforeDraw = value;
+			}
+		}
+	};
+
+	var ref$1 = getDevice();
+	var windowWidth = ref$1.windowWidth;
+
+	function prepare() {
+		var self = this;
+
+		// v1.4.0 版本中将不再自动绑定we-cropper实例
+		self.attachPage = function() {
+			var pages = getCurrentPages();
+			// 获取到当前page上下文
+			var pageContext = pages[pages.length - 1];
+			// 把this依附在Page上下文的wecropper属性上,便于在page钩子函数中访问
+			Object.defineProperty(pageContext, 'wecropper', {
+				get: function get() {
+					console.warn(
+						'Instance will not be automatically bound to the page after v1.4.0\n\n' +
+						'Please use a custom instance name instead\n\n' +
+						'Example: \n' +
+						'this.mycropper = new WeCropper(options)\n\n' +
+						'// ...\n' +
+						'this.mycropper.getCropperImage()'
+					);
+					return self
+				},
+				configurable: true
+			});
+		};
+
+		self.createCtx = function() {
+			var id = self.id;
+			var targetId = self.targetId;
+
+			if (id) {
+				self.ctx = self.ctx || uni.createCanvasContext(id);
+				self.targetCtx = self.targetCtx || uni.createCanvasContext(targetId);
+			} else {
+				console.error("constructor: create canvas context failed, 'id' must be valuable");
+			}
+		};
+
+		self.deviceRadio = windowWidth / 750;
+	}
+
+	var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !==
+		'undefined' ? self : {};
+
+
+
+
+
+	function createCommonjsModule(fn, module) {
+		return module = {
+			exports: {}
+		}, fn(module, module.exports), module.exports;
+	}
+
+	var tools = createCommonjsModule(function(module, exports) {
+		/**
+		 * String type check
+		 */
+		exports.isStr = function(v) {
+			return typeof v === 'string';
+		};
+		/**
+		 * Number type check
+		 */
+		exports.isNum = function(v) {
+			return typeof v === 'number';
+		};
+		/**
+		 * Array type check
+		 */
+		exports.isArr = Array.isArray;
+		/**
+		 * undefined type check
+		 */
+		exports.isUndef = function(v) {
+			return v === undefined;
+		};
+
+		exports.isTrue = function(v) {
+			return v === true;
+		};
+
+		exports.isFalse = function(v) {
+			return v === false;
+		};
+		/**
+		 * Function type check
+		 */
+		exports.isFunc = function(v) {
+			return typeof v === 'function';
+		};
+		/**
+		 * Quick object check - this is primarily used to tell
+		 * Objects from primitive values when we know the value
+		 * is a JSON-compliant type.
+		 */
+		exports.isObj = exports.isObject = function(obj) {
+			return obj !== null && typeof obj === 'object'
+		};
+
+		/**
+		 * Strict object type check. Only returns true
+		 * for plain JavaScript objects.
+		 */
+		var _toString = Object.prototype.toString;
+		exports.isPlainObject = function(obj) {
+			return _toString.call(obj) === '[object Object]'
+		};
+
+		/**
+		 * Check whether the object has the property.
+		 */
+		var hasOwnProperty = Object.prototype.hasOwnProperty;
+		exports.hasOwn = function(obj, key) {
+			return hasOwnProperty.call(obj, key)
+		};
+
+		/**
+		 * Perform no operation.
+		 * Stubbing args to make Flow happy without leaving useless transpiled code
+		 * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/)
+		 */
+		exports.noop = function(a, b, c) {};
+
+		/**
+		 * Check if val is a valid array index.
+		 */
+		exports.isValidArrayIndex = function(val) {
+			var n = parseFloat(String(val));
+			return n >= 0 && Math.floor(n) === n && isFinite(val)
+		};
+	});
+
+	var tools_7 = tools.isFunc;
+	var tools_10 = tools.isPlainObject;
+
+	var EVENT_TYPE = ['ready', 'beforeImageLoad', 'beforeDraw', 'imageLoad'];
+
+	function observer() {
+		var self = this;
+
+		self.on = function(event, fn) {
+			if (EVENT_TYPE.indexOf(event) > -1) {
+				if (tools_7(fn)) {
+					event === 'ready' ?
+						fn(self) :
+						self[("on" + (firstLetterUpper(event)))] = fn;
+				}
+			} else {
+				console.error(("event: " + event + " is invalid"));
+			}
+			return self
+		};
+	}
+
+	function wxPromise(fn) {
+		return function(obj) {
+			var args = [],
+				len = arguments.length - 1;
+			while (len-- > 0) args[len] = arguments[len + 1];
+
+			if (obj === void 0) obj = {};
+			return new Promise(function(resolve, reject) {
+				obj.success = function(res) {
+					resolve(res);
+				};
+				obj.fail = function(err) {
+					reject(err);
+				};
+				fn.apply(void 0, [obj].concat(args));
+			})
+		}
+	}
+
+	function draw(ctx, reserve) {
+		if (reserve === void 0) reserve = false;
+
+		return new Promise(function(resolve) {
+			ctx.draw(reserve, resolve);
+		})
+	}
+
+	var getImageInfo = wxPromise(uni.getImageInfo);
+
+	var canvasToTempFilePath = wxPromise(uni.canvasToTempFilePath);
+
+	var base64 = createCommonjsModule(function(module, exports) {
+		/*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */
+		(function(root) {
+
+			// Detect free variables `exports`.
+			var freeExports = 'object' == 'object' && exports;
+
+			// Detect free variable `module`.
+			var freeModule = 'object' == 'object' && module &&
+				module.exports == freeExports && module;
+
+			// Detect free variable `global`, from Node.js or Browserified code, and use
+			// it as `root`.
+			var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
+			if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
+				root = freeGlobal;
+			}
+
+			/*--------------------------------------------------------------------------*/
+
+			var InvalidCharacterError = function(message) {
+				this.message = message;
+			};
+			InvalidCharacterError.prototype = new Error;
+			InvalidCharacterError.prototype.name = 'InvalidCharacterError';
+
+			var error = function(message) {
+				// Note: the error messages used throughout this file match those used by
+				// the native `atob`/`btoa` implementation in Chromium.
+				throw new InvalidCharacterError(message);
+			};
+
+			var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+			// http://whatwg.org/html/common-microsyntaxes.html#space-character
+			var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;
+
+			// `decode` is designed to be fully compatible with `atob` as described in the
+			// HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
+			// The optimized base64-decoding algorithm used is based on @atk’s excellent
+			// implementation. https://gist.github.com/atk/1020396
+			var decode = function(input) {
+				input = String(input)
+					.replace(REGEX_SPACE_CHARACTERS, '');
+				var length = input.length;
+				if (length % 4 == 0) {
+					input = input.replace(/==?$/, '');
+					length = input.length;
+				}
+				if (
+					length % 4 == 1 ||
+					// http://whatwg.org/C#alphanumeric-ascii-characters
+					/[^+a-zA-Z0-9/]/.test(input)
+				) {
+					error(
+						'Invalid character: the string to be decoded is not correctly encoded.'
+					);
+				}
+				var bitCounter = 0;
+				var bitStorage;
+				var buffer;
+				var output = '';
+				var position = -1;
+				while (++position < length) {
+					buffer = TABLE.indexOf(input.charAt(position));
+					bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
+					// Unless this is the first of a group of 4 characters…
+					if (bitCounter++ % 4) {
+						// …convert the first 8 bits to a single ASCII character.
+						output += String.fromCharCode(
+							0xFF & bitStorage >> (-2 * bitCounter & 6)
+						);
+					}
+				}
+				return output;
+			};
+
+			// `encode` is designed to be fully compatible with `btoa` as described in the
+			// HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
+			var encode = function(input) {
+				input = String(input);
+				if (/[^\0-\xFF]/.test(input)) {
+					// Note: no need to special-case astral symbols here, as surrogates are
+					// matched, and the input is supposed to only contain ASCII anyway.
+					error(
+						'The string to be encoded contains characters outside of the ' +
+						'Latin1 range.'
+					);
+				}
+				var padding = input.length % 3;
+				var output = '';
+				var position = -1;
+				var a;
+				var b;
+				var c;
+				var buffer;
+				// Make sure any padding is handled outside of the loop.
+				var length = input.length - padding;
+
+				while (++position < length) {
+					// Read three bytes, i.e. 24 bits.
+					a = input.charCodeAt(position) << 16;
+					b = input.charCodeAt(++position) << 8;
+					c = input.charCodeAt(++position);
+					buffer = a + b + c;
+					// Turn the 24 bits into four chunks of 6 bits each, and append the
+					// matching character for each of them to the output.
+					output += (
+						TABLE.charAt(buffer >> 18 & 0x3F) +
+						TABLE.charAt(buffer >> 12 & 0x3F) +
+						TABLE.charAt(buffer >> 6 & 0x3F) +
+						TABLE.charAt(buffer & 0x3F)
+					);
+				}
+
+				if (padding == 2) {
+					a = input.charCodeAt(position) << 8;
+					b = input.charCodeAt(++position);
+					buffer = a + b;
+					output += (
+						TABLE.charAt(buffer >> 10) +
+						TABLE.charAt((buffer >> 4) & 0x3F) +
+						TABLE.charAt((buffer << 2) & 0x3F) +
+						'='
+					);
+				} else if (padding == 1) {
+					buffer = input.charCodeAt(position);
+					output += (
+						TABLE.charAt(buffer >> 2) +
+						TABLE.charAt((buffer << 4) & 0x3F) +
+						'=='
+					);
+				}
+
+				return output;
+			};
+
+			var base64 = {
+				'encode': encode,
+				'decode': decode,
+				'version': '0.1.0'
+			};
+
+			// Some AMD build optimizers, like r.js, check for specific condition patterns
+			// like the following:
+			if (
+				typeof undefined == 'function' &&
+				typeof undefined.amd == 'object' &&
+				undefined.amd
+			) {
+				undefined(function() {
+					return base64;
+				});
+			} else if (freeExports && !freeExports.nodeType) {
+				if (freeModule) { // in Node.js or RingoJS v0.8.0+
+					freeModule.exports = base64;
+				} else { // in Narwhal or RingoJS v0.7.0-
+					for (var key in base64) {
+						base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
+					}
+				}
+			} else { // in Rhino or a web browser
+				root.base64 = base64;
+			}
+
+		}(commonjsGlobal));
+	});
+
+	function makeURI(strData, type) {
+		return 'data:' + type + ';base64,' + strData
+	}
+
+	function fixType(type) {
+		type = type.toLowerCase().replace(/jpg/i, 'jpeg');
+		var r = type.match(/png|jpeg|bmp|gif/)[0];
+		return 'image/' + r
+	}
+
+	function encodeData(data) {
+		var str = '';
+		if (typeof data === 'string') {
+			str = data;
+		} else {
+			for (var i = 0; i < data.length; i++) {
+				str += String.fromCharCode(data[i]);
+			}
+		}
+		return base64.encode(str)
+	}
+
+	/**
+	 * 获取图像区域隐含的像素数据
+	 * @param canvasId canvas标识
+	 * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
+	 * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
+	 * @param width 将要被提取的图像数据矩形区域的宽度
+	 * @param height 将要被提取的图像数据矩形区域的高度
+	 * @param done 完成回调
+	 */
+	function getImageData(canvasId, x, y, width, height, done) {
+		uni.canvasGetImageData({
+			canvasId: canvasId,
+			x: x,
+			y: y,
+			width: width,
+			height: height,
+			success: function success(res) {
+				done(res, null);
+			},
+			fail: function fail(res) {
+				done(null, res);
+			}
+		});
+	}
+
+	/**
+	 * 生成bmp格式图片
+	 * 按照规则生成图片响应头和响应体
+	 * @param oData 用来描述 canvas 区域隐含的像素数据 { data, width, height } = oData
+	 * @returns {*} base64字符串
+	 */
+	function genBitmapImage(oData) {
+		//
+		// BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
+		// BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx
+		//
+		var biWidth = oData.width;
+		var biHeight = oData.height;
+		var biSizeImage = biWidth * biHeight * 3;
+		var bfSize = biSizeImage + 54; // total header size = 54 bytes
+
+		//
+		//  typedef struct tagBITMAPFILEHEADER {
+		//  	WORD bfType;
+		//  	DWORD bfSize;
+		//  	WORD bfReserved1;
+		//  	WORD bfReserved2;
+		//  	DWORD bfOffBits;
+		//  } BITMAPFILEHEADER;
+		//
+		var BITMAPFILEHEADER = [
+			// WORD bfType -- The file type signature; must be "BM"
+			0x42, 0x4D,
+			// DWORD bfSize -- The size, in bytes, of the bitmap file
+			bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,
+			// WORD bfReserved1 -- Reserved; must be zero
+			0, 0,
+			// WORD bfReserved2 -- Reserved; must be zero
+			0, 0,
+			// DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
+			54, 0, 0, 0
+		];
+
+		//
+		//  typedef struct tagBITMAPINFOHEADER {
+		//  	DWORD biSize;
+		//  	LONG  biWidth;
+		//  	LONG  biHeight;
+		//  	WORD  biPlanes;
+		//  	WORD  biBitCount;
+		//  	DWORD biCompression;
+		//  	DWORD biSizeImage;
+		//  	LONG  biXPelsPerMeter;
+		//  	LONG  biYPelsPerMeter;
+		//  	DWORD biClrUsed;
+		//  	DWORD biClrImportant;
+		//  } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
+		//
+		var BITMAPINFOHEADER = [
+			// DWORD biSize -- The number of bytes required by the structure
+			40, 0, 0, 0,
+			// LONG biWidth -- The width of the bitmap, in pixels
+			biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,
+			// LONG biHeight -- The height of the bitmap, in pixels
+			biHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,
+			// WORD biPlanes -- The number of planes for the target device. This value must be set to 1
+			1, 0,
+			// WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap
+			// has a maximum of 2^24 colors (16777216, Truecolor)
+			24, 0,
+			// DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed
+			0, 0, 0, 0,
+			// DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps
+			biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,
+			// LONG biXPelsPerMeter, unused
+			0, 0, 0, 0,
+			// LONG biYPelsPerMeter, unused
+			0, 0, 0, 0,
+			// DWORD biClrUsed, the number of color indexes of palette, unused
+			0, 0, 0, 0,
+			// DWORD biClrImportant, unused
+			0, 0, 0, 0
+		];
+
+		var iPadding = (4 - ((biWidth * 3) % 4)) % 4;
+
+		var aImgData = oData.data;
+
+		var strPixelData = '';
+		var biWidth4 = biWidth << 2;
+		var y = biHeight;
+		var fromCharCode = String.fromCharCode;
+
+		do {
+			var iOffsetY = biWidth4 * (y - 1);
+			var strPixelRow = '';
+			for (var x = 0; x < biWidth; x++) {
+				var iOffsetX = x << 2;
+				strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) +
+					fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) +
+					fromCharCode(aImgData[iOffsetY + iOffsetX]);
+			}
+
+			for (var c = 0; c < iPadding; c++) {
+				strPixelRow += String.fromCharCode(0);
+			}
+
+			strPixelData += strPixelRow;
+		} while (--y)
+
+		var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);
+
+		return strEncoded
+	}
+
+	/**
+	 * 转换为图片base64
+	 * @param canvasId canvas标识
+	 * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
+	 * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
+	 * @param width 将要被提取的图像数据矩形区域的宽度
+	 * @param height 将要被提取的图像数据矩形区域的高度
+	 * @param type 转换图片类型
+	 * @param done 完成回调
+	 */
+	function convertToImage(canvasId, x, y, width, height, type, done) {
+		if (done === void 0) done = function() {};
+
+		if (type === undefined) {
+			type = 'png';
+		}
+		type = fixType(type);
+		if (/bmp/.test(type)) {
+			getImageData(canvasId, x, y, width, height, function(data, err) {
+				var strData = genBitmapImage(data);
+				tools_7(done) && done(makeURI(strData, 'image/' + type), err);
+			});
+		} else {
+			console.error('暂不支持生成\'' + type + '\'类型的base64图片');
+		}
+	}
+
+	var CanvasToBase64 = {
+		convertToImage: convertToImage,
+		// convertToPNG: function (width, height, done) {
+		//   return convertToImage(width, height, 'png', done)
+		// },
+		// convertToJPEG: function (width, height, done) {
+		//   return convertToImage(width, height, 'jpeg', done)
+		// },
+		// convertToGIF: function (width, height, done) {
+		//   return convertToImage(width, height, 'gif', done)
+		// },
+		convertToBMP: function(ref, done) {
+			if (ref === void 0) ref = {};
+			var canvasId = ref.canvasId;
+			var x = ref.x;
+			var y = ref.y;
+			var width = ref.width;
+			var height = ref.height;
+			if (done === void 0) done = function() {};
+
+			return convertToImage(canvasId, x, y, width, height, 'bmp', done)
+		}
+	};
+
+	function methods() {
+		var self = this;
+
+		var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度
+		var boundHeight = self.height; // 裁剪框默认高度,即整个画布高度
+
+		var id = self.id;
+		var targetId = self.targetId;
+		var pixelRatio = self.pixelRatio;
+
+		var ref = self.cut;
+		var x = ref.x;
+		if (x === void 0) x = 0;
+		var y = ref.y;
+		if (y === void 0) y = 0;
+		var width = ref.width;
+		if (width === void 0) width = boundWidth;
+		var height = ref.height;
+		if (height === void 0) height = boundHeight;
+
+		self.updateCanvas = function(done) {
+			if (self.croperTarget) {
+				//  画布绘制图片
+				self.ctx.drawImage(
+					self.croperTarget,
+					self.imgLeft,
+					self.imgTop,
+					self.scaleWidth,
+					self.scaleHeight
+				);
+			}
+			tools_7(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);
+
+			self.setBoundStyle(self.boundStyle); //	设置边界样式
+
+			self.ctx.draw(false, done);
+			return self
+		};
+
+		self.pushOrigin = self.pushOrign = function(src) {
+			self.src = src;
+
+			tools_7(self.onBeforeImageLoad) && self.onBeforeImageLoad(self.ctx, self);
+
+			return getImageInfo({
+					src: src
+				})
+				.then(function(res) {
+					var innerAspectRadio = res.width / res.height;
+					var customAspectRadio = width / height;
+
+					self.croperTarget = res.path;
+
+					if (innerAspectRadio < customAspectRadio) {
+						self.rectX = x;
+						self.baseWidth = width;
+						self.baseHeight = width / innerAspectRadio;
+						self.rectY = y - Math.abs((height - self.baseHeight) / 2);
+					} else {
+						self.rectY = y;
+						self.baseWidth = height * innerAspectRadio;
+						self.baseHeight = height;
+						self.rectX = x - Math.abs((width - self.baseWidth) / 2);
+					}
+
+					self.imgLeft = self.rectX;
+					self.imgTop = self.rectY;
+					self.scaleWidth = self.baseWidth;
+					self.scaleHeight = self.baseHeight;
+
+					self.update();
+
+					return new Promise(function(resolve) {
+						self.updateCanvas(resolve);
+					})
+				})
+				.then(function() {
+					tools_7(self.onImageLoad) && self.onImageLoad(self.ctx, self);
+				})
+		};
+
+		self.removeImage = function() {
+			self.src = '';
+			self.croperTarget = '';
+			return draw(self.ctx)
+		};
+
+		self.getCropperBase64 = function(done) {
+			if (done === void 0) done = function() {};
+
+			CanvasToBase64.convertToBMP({
+				canvasId: id,
+				x: x,
+				y: y,
+				width: width,
+				height: height
+			}, done);
+		};
+
+		self.getCropperImage = function(opt, fn) {
+			var customOptions = opt;
+
+			var canvasOptions = {
+				canvasId: id,
+				x: x,
+				y: y,
+				width: width,
+				height: height
+			};
+
+			var task = function() {
+				return Promise.resolve();
+			};
+
+			if (
+				tools_10(customOptions) &&
+				customOptions.original
+			) {
+				// original mode
+				task = function() {
+					self.targetCtx.drawImage(
+						self.croperTarget,
+						self.imgLeft * pixelRatio,
+						self.imgTop * pixelRatio,
+						self.scaleWidth * pixelRatio,
+						self.scaleHeight * pixelRatio
+					);
+
+					canvasOptions = {
+						canvasId: targetId,
+						x: x * pixelRatio,
+						y: y * pixelRatio,
+						width: width * pixelRatio,
+						height: height * pixelRatio
+					};
+
+					return draw(self.targetCtx)
+				};
+			}
+
+			return task()
+				.then(function() {
+					if (tools_10(customOptions)) {
+						canvasOptions = Object.assign({}, canvasOptions, customOptions);
+					}
+
+					if (tools_7(customOptions)) {
+						fn = customOptions;
+					}
+
+					var arg = canvasOptions.componentContext ?
+						[canvasOptions, canvasOptions.componentContext] :
+						[canvasOptions];
+
+					return canvasToTempFilePath.apply(null, arg)
+				})
+				.then(function(res) {
+					var tempFilePath = res.tempFilePath;
+
+					return tools_7(fn) ?
+						fn.call(self, tempFilePath, null) :
+						tempFilePath
+				})
+				.catch(function(err) {
+					if (tools_7(fn)) {
+						fn.call(self, null, err);
+					} else {
+						throw err
+					}
+				})
+		};
+	}
+
+	/**
+	 * 获取最新缩放值
+	 * @param oldScale 上一次触摸结束后的缩放值
+	 * @param oldDistance 上一次触摸结束后的双指距离
+	 * @param zoom 缩放系数
+	 * @param touch0 第一指touch对象
+	 * @param touch1 第二指touch对象
+	 * @returns {*}
+	 */
+	var getNewScale = function(oldScale, oldDistance, zoom, touch0, touch1) {
+		var xMove, yMove, newDistance;
+		// 计算二指最新距离
+		xMove = Math.round(touch1.x - touch0.x);
+		yMove = Math.round(touch1.y - touch0.y);
+		newDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
+
+		return oldScale + 0.001 * zoom * (newDistance - oldDistance)
+	};
+
+	function update() {
+		var self = this;
+
+		if (!self.src) {
+			return
+		}
+
+		self.__oneTouchStart = function(touch) {
+			self.touchX0 = Math.round(touch.x);
+			self.touchY0 = Math.round(touch.y);
+		};
+
+		self.__oneTouchMove = function(touch) {
+			var xMove, yMove;
+			// 计算单指移动的距离
+			if (self.touchended) {
+				return self.updateCanvas()
+			}
+			xMove = Math.round(touch.x - self.touchX0);
+			yMove = Math.round(touch.y - self.touchY0);
+
+			var imgLeft = Math.round(self.rectX + xMove);
+			var imgTop = Math.round(self.rectY + yMove);
+
+			self.outsideBound(imgLeft, imgTop);
+
+			self.updateCanvas();
+		};
+
+		self.__twoTouchStart = function(touch0, touch1) {
+			var xMove, yMove, oldDistance;
+
+			self.touchX1 = Math.round(self.rectX + self.scaleWidth / 2);
+			self.touchY1 = Math.round(self.rectY + self.scaleHeight / 2);
+
+			// 计算两指距离
+			xMove = Math.round(touch1.x - touch0.x);
+			yMove = Math.round(touch1.y - touch0.y);
+			oldDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
+
+			self.oldDistance = oldDistance;
+		};
+
+		self.__twoTouchMove = function(touch0, touch1) {
+			var oldScale = self.oldScale;
+			var oldDistance = self.oldDistance;
+			var scale = self.scale;
+			var zoom = self.zoom;
+
+			self.newScale = getNewScale(oldScale, oldDistance, zoom, touch0, touch1);
+
+			//  设定缩放范围
+			self.newScale <= 1 && (self.newScale = 1);
+			self.newScale >= scale && (self.newScale = scale);
+
+			self.scaleWidth = Math.round(self.newScale * self.baseWidth);
+			self.scaleHeight = Math.round(self.newScale * self.baseHeight);
+			var imgLeft = Math.round(self.touchX1 - self.scaleWidth / 2);
+			var imgTop = Math.round(self.touchY1 - self.scaleHeight / 2);
+
+			self.outsideBound(imgLeft, imgTop);
+
+			self.updateCanvas();
+		};
+
+		self.__xtouchEnd = function() {
+			self.oldScale = self.newScale;
+			self.rectX = self.imgLeft;
+			self.rectY = self.imgTop;
+		};
+	}
+
+	var handle = {
+		//  图片手势初始监测
+		touchStart: function touchStart(e) {
+			var self = this;
+			var ref = e.touches;
+			var touch0 = ref[0];
+			var touch1 = ref[1];
+
+			if (!self.src) {
+				return
+			}
+
+			setTouchState(self, true, null, null);
+
+			// 计算第一个触摸点的位置,并参照改点进行缩放
+			self.__oneTouchStart(touch0);
+
+			// 两指手势触发
+			if (e.touches.length >= 2) {
+				self.__twoTouchStart(touch0, touch1);
+			}
+		},
+
+		//  图片手势动态缩放
+		touchMove: function touchMove(e) {
+			var self = this;
+			var ref = e.touches;
+			var touch0 = ref[0];
+			var touch1 = ref[1];
+
+			if (!self.src) {
+				return
+			}
+
+			setTouchState(self, null, true);
+
+			// 单指手势时触发
+			if (e.touches.length === 1) {
+				self.__oneTouchMove(touch0);
+			}
+			// 两指手势触发
+			if (e.touches.length >= 2) {
+				self.__twoTouchMove(touch0, touch1);
+			}
+		},
+
+		touchEnd: function touchEnd(e) {
+			var self = this;
+
+			if (!self.src) {
+				return
+			}
+
+			setTouchState(self, false, false, true);
+			self.__xtouchEnd();
+		}
+	};
+
+	function cut() {
+		var self = this;
+		var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度
+		var boundHeight = self.height;
+		// 裁剪框默认高度,即整个画布高度
+		var ref = self.cut;
+		var x = ref.x;
+		if (x === void 0) x = 0;
+		var y = ref.y;
+		if (y === void 0) y = 0;
+		var width = ref.width;
+		if (width === void 0) width = boundWidth;
+		var height = ref.height;
+		if (height === void 0) height = boundHeight;
+
+		/**
+		 * 设置边界
+		 * @param imgLeft 图片左上角横坐标值
+		 * @param imgTop 图片左上角纵坐标值
+		 */
+		self.outsideBound = function(imgLeft, imgTop) {
+			self.imgLeft = imgLeft >= x ?
+				x :
+				self.scaleWidth + imgLeft - x <= width ?
+				x + width - self.scaleWidth :
+				imgLeft;
+
+			self.imgTop = imgTop >= y ?
+				y :
+				self.scaleHeight + imgTop - y <= height ?
+				y + height - self.scaleHeight :
+				imgTop;
+		};
+
+		/**
+		 * 设置边界样式
+		 * @param color	边界颜色
+		 */
+		self.setBoundStyle = function(ref) {
+			if (ref === void 0) ref = {};
+			var color = ref.color;
+			if (color === void 0) color = '#04b00f';
+			var mask = ref.mask;
+			if (mask === void 0) mask = 'rgba(0, 0, 0, 0.3)';
+			var lineWidth = ref.lineWidth;
+			if (lineWidth === void 0) lineWidth = 1;
+
+			var half = lineWidth / 2;
+			var boundOption = [{
+					start: {
+						x: x - half,
+						y: y + 10 - half
+					},
+					step1: {
+						x: x - half,
+						y: y - half
+					},
+					step2: {
+						x: x + 10 - half,
+						y: y - half
+					}
+				},
+				{
+					start: {
+						x: x - half,
+						y: y + height - 10 + half
+					},
+					step1: {
+						x: x - half,
+						y: y + height + half
+					},
+					step2: {
+						x: x + 10 - half,
+						y: y + height + half
+					}
+				},
+				{
+					start: {
+						x: x + width - 10 + half,
+						y: y - half
+					},
+					step1: {
+						x: x + width + half,
+						y: y - half
+					},
+					step2: {
+						x: x + width + half,
+						y: y + 10 - half
+					}
+				},
+				{
+					start: {
+						x: x + width + half,
+						y: y + height - 10 + half
+					},
+					step1: {
+						x: x + width + half,
+						y: y + height + half
+					},
+					step2: {
+						x: x + width - 10 + half,
+						y: y + height + half
+					}
+				}
+			];
+
+			// 绘制半透明层
+			self.ctx.beginPath();
+			self.ctx.setFillStyle(mask);
+			self.ctx.fillRect(0, 0, x, boundHeight);
+			self.ctx.fillRect(x, 0, width, y);
+			self.ctx.fillRect(x, y + height, width, boundHeight - y - height);
+			self.ctx.fillRect(x + width, 0, boundWidth - x - width, boundHeight);
+			self.ctx.fill();
+
+			boundOption.forEach(function(op) {
+				self.ctx.beginPath();
+				self.ctx.setStrokeStyle(color);
+				self.ctx.setLineWidth(lineWidth);
+				self.ctx.moveTo(op.start.x, op.start.y);
+				self.ctx.lineTo(op.step1.x, op.step1.y);
+				self.ctx.lineTo(op.step2.x, op.step2.y);
+				self.ctx.stroke();
+			});
+		};
+	}
+
+	var version = "1.3.9";
+
+	var WeCropper = function WeCropper(params) {
+		var self = this;
+		var _default = {};
+
+		validator(self, DEFAULT);
+
+		Object.keys(DEFAULT).forEach(function(key) {
+			_default[key] = DEFAULT[key].default;
+		});
+		Object.assign(self, _default, params);
+
+		self.prepare();
+		self.attachPage();
+		self.createCtx();
+		self.observer();
+		self.cutt();
+		self.methods();
+		self.init();
+		self.update();
+
+		return self
+	};
+
+	WeCropper.prototype.init = function init() {
+		var self = this;
+		var src = self.src;
+
+		self.version = version;
+
+		typeof self.onReady === 'function' && self.onReady(self.ctx, self);
+
+		if (src) {
+			self.pushOrign(src);
+		} else {
+			self.updateCanvas();
+		}
+		setTouchState(self, false, false, false);
+
+		self.oldScale = 1;
+		self.newScale = 1;
+
+		return self
+	};
+
+	Object.assign(WeCropper.prototype, handle);
+
+	WeCropper.prototype.prepare = prepare;
+	WeCropper.prototype.observer = observer;
+	WeCropper.prototype.methods = methods;
+	WeCropper.prototype.cutt = cut;
+	WeCropper.prototype.update = update;
+
+	return WeCropper;
+
+})));

Plik diff jest za duży
+ 24 - 0
uview-ui/components/u-avatar/u-avatar.vue


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

@@ -0,0 +1,153 @@
+<template>
+	<view @tap="backToTop" class="u-back-top" :class="['u-back-top--mode--' + mode]" :style="[{
+		bottom: bottom + 'rpx',
+		right: right + 'rpx',
+		borderRadius: mode == 'circle' ? '10000rpx' : '8rpx',
+		zIndex: uZIndex,
+		opacity: opacity
+	}, customStyle]">
+		<view class="u-back-top__content" v-if="!$slots.default && !$slots.$default">
+			<u-icon @click="backToTop" :name="icon" :custom-style="iconStyle"></u-icon>
+			<view class="u-back-top__content__tips">
+				{{tips}}
+			</view>
+		</view>
+		<slot v-else />
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'u-back-top',
+		props: {
+			// 返回顶部的形状,circle-圆形,square-方形
+			mode: {
+				type: String,
+				default: 'circle'
+			},
+			// 自定义图标
+			icon: {
+				type: String,
+				default: 'arrow-upward'
+			},
+			// 提示文字
+			tips: {
+				type: String,
+				default: ''
+			},
+			// 返回顶部滚动时间
+			duration: {
+				type: [Number, String],
+				default: 100
+			},
+			// 滚动距离
+			scrollTop: {
+				type: [Number, String],
+				default: 0
+			},
+			// 距离顶部多少距离显示,单位rpx
+			top: {
+				type: [Number, String],
+				default: 400
+			},
+			// 返回顶部按钮到底部的距离,单位rpx
+			bottom: {
+				type: [Number, String],
+				default: 200
+			},
+			// 返回顶部按钮到右边的距离,单位rpx
+			right: {
+				type: [Number, String],
+				default: 40
+			},
+			// 层级
+			zIndex: {
+				type: [Number, String],
+				default: '9'
+			},
+			// 图标的样式,对象形式
+			iconStyle: {
+				type: Object,
+				default() {
+					return {
+						color: '#909399',
+						fontSize: '38rpx'
+					}
+				}
+			},
+			// 整个组件的样式
+			customStyle: {
+				type: Object,
+				default() {
+					return {}
+				}
+			}
+		},
+		watch: {
+			showBackTop(nVal, oVal) {
+				// 当组件的显示与隐藏状态发生跳变时,修改组件的层级和不透明度
+				// 让组件有显示和消失的动画效果,如果用v-if控制组件状态,将无设置动画效果
+				if(nVal) {
+					this.uZIndex = this.zIndex;
+					this.opacity = 1;
+				} else {
+					this.uZIndex = -1;
+					this.opacity = 0;
+				}
+			}
+		},
+		computed: {
+			showBackTop() {
+				// 由于scrollTop为页面的滚动距离,默认为px单位,这里将用于传入的top(rpx)值
+				// 转为px用于比较,如果滚动条到顶的距离大于设定的距离,就显示返回顶部的按钮
+				return this.scrollTop > uni.upx2px(this.top);
+			},
+		},
+		data() {
+			return {
+				// 不透明度,为了让组件有一个显示和隐藏的过渡动画
+				opacity: 0,
+				// 组件的z-index值,隐藏时设置为-1,就会看不到
+				uZIndex: -1
+			}
+		},
+		methods: {
+			backToTop() {
+				uni.pageScrollTo({
+					scrollTop: 0,
+					duration: this.duration
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+	
+	.u-back-top {
+		width: 80rpx;
+		height: 80rpx;
+		position: fixed;
+		z-index: 9;
+		@include vue-flex;
+		flex-direction: column;
+		justify-content: center;
+		background-color: #E1E1E1;
+		color: $u-content-color;
+		align-items: center;
+		transition: opacity 0.4s;
+		
+		&__content {
+			@include vue-flex;
+			flex-direction: column;
+			align-items: center;
+			
+			&__tips {
+				font-size: 24rpx;
+				transform: scale(0.8);
+				line-height: 1;
+			}
+		}
+	}
+</style>

+ 216 - 0
uview-ui/components/u-badge/u-badge.vue

@@ -0,0 +1,216 @@
+<template>
+	<view v-if="show" class="u-badge" :class="[
+			isDot ? 'u-badge-dot' : '', 
+			size == 'mini' ? 'u-badge-mini' : '',
+			type ? 'u-badge--bg--' + type : ''
+		]" :style="[{
+			top: offset[0] + 'rpx',
+			right: offset[1] + 'rpx',
+			fontSize: fontSize + 'rpx',
+			position: absolute ? 'absolute' : 'static',
+			color: color,
+			backgroundColor: bgColor
+		}, boxStyle]"
+	>
+		{{showText}}
+	</view>
+</template>
+
+<script>
+	/**
+	 * badge 角标
+	 * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+	 * @tutorial https://www.uviewui.com/components/badge.html
+	 * @property {String Number} count 展示的数字,大于 overflowCount 时显示为 ${overflowCount}+,为0且show-zero为false时隐藏
+	 * @property {Boolean} is-dot 不展示数字,只有一个小点(默认false)
+	 * @property {Boolean} absolute 组件是否绝对定位,为true时,offset参数才有效(默认true)
+	 * @property {String Number} overflow-count 展示封顶的数字值(默认99)
+	 * @property {String} type 使用预设的背景颜色(默认error)
+	 * @property {Boolean} show-zero 当数值为 0 时,是否展示 Badge(默认false)
+	 * @property {String} size Badge的尺寸,设为mini会得到小一号的Badge(默认default)
+	 * @property {Array} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,单位rpx。absolute为true时有效(默认[20, 20])
+	 * @property {String} color 字体颜色(默认#ffffff)
+	 * @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效
+	 * @property {Boolean} is-center 组件中心点是否和父组件右上角重合,优先级比offset高,如设置,offset参数会失效(默认false)
+	 * @example <u-badge type="error" count="7"></u-badge>
+	 */
+	export default {
+		name: 'u-badge',
+		props: {
+			// primary,warning,success,error,info
+			type: {
+				type: String,
+				default: 'error'
+			},
+			// default, mini
+			size: {
+				type: String,
+				default: 'default'
+			},
+			//是否是圆点
+			isDot: {
+				type: Boolean,
+				default: false
+			},
+			// 显示的数值内容
+			count: {
+				type: [Number, String],
+			},
+			// 展示封顶的数字值
+			overflowCount: {
+				type: Number,
+				default: 99
+			},
+			// 当数值为 0 时,是否展示 Badge
+			showZero: {
+				type: Boolean,
+				default: false
+			},
+			// 位置偏移
+			offset: {
+				type: Array,
+				default: () => {
+					return [20, 20]
+				}
+			},
+			// 是否开启绝对定位,开启了offset才会起作用
+			absolute: {
+				type: Boolean,
+				default: true
+			},
+			// 字体大小
+			fontSize: {
+				type: [String, Number],
+				default: '24'
+			},
+			// 字体演示
+			color: {
+				type: String,
+				default: '#ffffff'
+			},
+			// badge的背景颜色
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			// 是否让badge组件的中心点和父组件右上角重合,配置的话,offset将会失效
+			isCenter: {
+				type: Boolean,
+				default: false
+			}
+		},
+		computed: {
+			// 是否将badge中心与父组件右上角重合
+			boxStyle() {
+				let style = {};
+				if(this.isCenter) {
+					style.top = 0;
+					style.right = 0;
+					// Y轴-50%,意味着badge向上移动了badge自身高度一半,X轴50%,意味着向右移动了自身宽度一半
+					style.transform = "translateY(-50%) translateX(50%)";
+				} else {
+					style.top = this.offset[0] + 'rpx';
+					style.right = this.offset[1] + 'rpx';
+					style.transform = "translateY(0) translateX(0)";
+				}
+				// 如果尺寸为mini,后接上scal()
+				if(this.size == 'mini') {
+					style.transform = style.transform + " scale(0.8)";
+				}
+				return style;
+			},
+			// isDot类型时,不显示文字
+			showText() {
+				if(this.isDot) return '';
+				else {
+					if(this.count > this.overflowCount) return `${this.overflowCount}+`;
+					else return this.count;
+				}
+			},
+			// 是否显示组件
+			show() {
+				// 如果count的值为0,并且showZero设置为false,不显示组件
+				if(this.count == 0 && this.showZero == false) return false;
+				else return true;
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+	
+	.u-badge {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		line-height: 24rpx;
+		padding: 4rpx 8rpx;
+		border-radius: 100rpx;
+		z-index: 9;
+		
+		&--bg--primary {
+			background-color: $u-type-primary;
+		}
+		
+		&--bg--error {
+			background-color: $u-type-error;
+		}
+		
+		&--bg--success {
+			background-color: $u-type-success;
+		}
+		
+		&--bg--info {
+			background-color: $u-type-info;
+		}
+		
+		&--bg--warning {
+			background-color: $u-type-warning;
+		}
+	}
+	
+	.u-badge-dot {
+		height: 16rpx;
+		width: 16rpx;
+		border-radius: 100rpx;
+		line-height: 1;
+	}
+	
+	.u-badge-mini {
+		transform: scale(0.8);
+		transform-origin: center center;
+	}
+	
+	// .u-primary {
+	// 	background: $u-type-primary;
+	// 	color: #fff;
+	// }
+	
+	// .u-error {
+	// 	background: $u-type-error;
+	// 	color: #fff;
+	// }
+	
+	// .u-warning {
+	// 	background: $u-type-warning;
+	// 	color: #fff;
+	// }
+	
+	// .u-success {
+	// 	background: $u-type-success;
+	// 	color: #fff;
+	// }
+	
+	// .u-black {
+	// 	background: #585858;
+	// 	color: #fff;
+	// }
+	
+	.u-info {
+		background-color: $u-type-info;
+		color: #fff;
+	}
+</style>

+ 596 - 0
uview-ui/components/u-button/u-button.vue

@@ -0,0 +1,596 @@
+<template>
+	<button
+		id="u-wave-btn"
+		class="u-btn u-line-1 u-fix-ios-appearance"
+		:class="[
+			'u-size-' + size,
+			plain ? 'u-btn--' + type + '--plain' : '',
+			loading ? 'u-loading' : '',
+			shape == 'circle' ? 'u-round-circle' : '',
+			hairLine ? showHairLineBorder : 'u-btn--bold-border',
+			'u-btn--' + type,
+			disabled ? `u-btn--${type}--disabled` : '',
+		]"
+		:hover-start-time="Number(hoverStartTime)"
+		:hover-stay-time="Number(hoverStayTime)"
+		:disabled="disabled"
+		: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"
+		:style="[customStyle, {
+			overflow: ripple ? 'hidden' : 'visible'
+		}]"
+		@tap.stop="click($event)"
+		:hover-class="getHoverClass"
+		:loading="loading"
+	>
+		<slot></slot>
+		<view
+			v-if="ripple"
+			class="u-wave-ripple"
+			:class="[waveActive ? 'u-wave-active' : '']"
+			:style="{
+				top: rippleTop + 'px',
+				left: rippleLeft + 'px',
+				width: fields.targetWidth + 'px',
+				height: fields.targetWidth + 'px',
+				'background-color': rippleBgColor || 'rgba(0, 0, 0, 0.15)'
+			}"
+		></view>
+	</button>
+</template>
+
+<script>
+/**
+ * button 按钮
+ * @description Button 按钮
+ * @tutorial https://www.uviewui.com/components/button.html
+ * @property {String} size 按钮的大小
+ * @property {Boolean} ripple 是否开启点击水波纹效果
+ * @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效
+ * @property {String} type 按钮的样式类型
+ * @property {Boolean} plain 按钮是否镂空,背景色透明
+ * @property {Boolean} disabled 是否禁用
+ * @property {Boolean} hair-line 是否显示按钮的细边框(默认true)
+ * @property {Boolean} shape 按钮外观形状,见文档说明
+ * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
+ * @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+ * @property {String} open-type 开放能力
+ * @property {String} data-name 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+ * @property {String} hover-class 指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果(App-nvue 平台暂不支持)
+ * @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒
+ * @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒
+ * @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明
+ * @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 成功的回调
+ * @example <u-button>月落</u-button>
+ */
+export default {
+	name: 'u-button',
+	props: {
+		// 是否细边框
+		hairLine: {
+			type: Boolean,
+			default: true
+		},
+		// 按钮的预置样式,default,primary,error,warning,success
+		type: {
+			type: String,
+			default: 'default'
+		},
+		// 按钮尺寸,default,medium,mini
+		size: {
+			type: String,
+			default: 'default'
+		},
+		// 按钮形状,circle(两边为半圆),square(带圆角)
+		shape: {
+			type: String,
+			default: 'square'
+		},
+		// 按钮是否镂空
+		plain: {
+			type: Boolean,
+			default: false
+		},
+		// 是否禁止状态
+		disabled: {
+			type: Boolean,
+			default: false
+		},
+		// 是否加载中
+		loading: {
+			type: Boolean,
+			default: false
+		},
+		// 开放能力,具体请看uniapp稳定关于button组件部分说明
+		// https://uniapp.dcloud.io/component/button
+		openType: {
+			type: String,
+			default: ''
+		},
+		// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+		// 取值为submit(提交表单),reset(重置表单)
+		formType: {
+			type: String,
+			default: ''
+		},
+		// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
+		// 只微信小程序、QQ小程序有效
+		appParameter: {
+			type: String,
+			default: ''
+		},
+		// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
+		hoverStopPropagation: {
+			type: Boolean,
+			default: false
+		},
+		// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
+		lang: {
+			type: String,
+			default: 'en'
+		},
+		// 会话来源,open-type="contact"时有效。只微信小程序有效
+		sessionFrom: {
+			type: String,
+			default: ''
+		},
+		// 会话内消息卡片标题,open-type="contact"时有效
+		// 默认当前标题,只微信小程序有效
+		sendMessageTitle: {
+			type: String,
+			default: ''
+		},
+		// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
+		// 默认当前分享路径,只微信小程序有效
+		sendMessagePath: {
+			type: String,
+			default: ''
+		},
+		// 会话内消息卡片图片,open-type="contact"时有效
+		// 默认当前页面截图,只微信小程序有效
+		sendMessageImg: {
+			type: String,
+			default: ''
+		},
+		// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
+		// 用户点击后可以快速发送小程序消息,open-type="contact"时有效
+		showMessageCard: {
+			type: Boolean,
+			default: false
+		},
+		// 手指按(触摸)按钮时按钮时的背景颜色
+		hoverBgColor: {
+			type: String,
+			default: ''
+		},
+		// 水波纹的背景颜色
+		rippleBgColor: {
+			type: String,
+			default: ''
+		},
+		// 是否开启水波纹效果
+		ripple: {
+			type: Boolean,
+			default: false
+		},
+		// 按下的类名
+		hoverClass: {
+			type: String,
+			default: ''
+		},
+		// 自定义样式,对象形式
+		customStyle: {
+			type: Object,
+			default() {
+				return {};
+			}
+		},
+		// 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+		dataName: {
+			type: String,
+			default: ''
+		},
+		// 节流,一定时间内只能触发一次
+		throttleTime: {
+			type: [String, Number],
+			default: 1000
+		},
+		// 按住后多久出现点击态,单位毫秒
+		hoverStartTime: {
+			type: [String, Number],
+			default: 20
+		},
+		// 手指松开后点击态保留时间,单位毫秒
+		hoverStayTime: {
+			type: [String, Number],
+			default: 150
+		},
+	},
+	computed: {
+		// 当没有传bgColor变量时,按钮按下去的颜色类名
+		getHoverClass() {
+			// 如果开启水波纹效果,则不启用hover-class效果
+			if (this.loading || this.disabled || this.ripple || this.hoverClass) return '';
+			let hoverClass = '';
+			hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover';
+			return hoverClass;
+		},
+		// 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象
+		showHairLineBorder() {
+			if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) {
+				return '';
+			} else {
+				return 'u-hairline-border';
+			}
+		}
+	},
+	data() {
+		return {
+			rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离
+			rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离
+			fields: {}, // 波纹按钮节点信息
+			waveActive: false // 激活水波纹
+		};
+	},
+	methods: {
+		// 按钮点击
+		click(e) {
+			// 进行节流控制,每this.throttle毫秒内,只在开始处执行
+			this.$u.throttle(() => {
+				// 如果按钮时disabled和loading状态,不触发水波纹效果
+				if (this.loading === true || this.disabled === true) return;
+				// 是否开启水波纹效果
+				if (this.ripple) {
+					// 每次点击时,移除上一次的类,再次添加,才能触发动画效果
+					this.waveActive = false;
+					this.$nextTick(function() {
+						this.getWaveQuery(e);
+					});
+				}
+				this.$emit('click', e);
+			}, this.throttleTime);
+		},
+		// 查询按钮的节点信息
+		getWaveQuery(e) {
+			this.getElQuery().then(res => {
+				// 查询返回的是一个数组节点
+				let data = res[0];
+				// 查询不到节点信息,不操作
+				if (!data.width || !data.width) return;
+				// 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边
+				// 最终的方形(变换后的圆形)才能覆盖整个按钮
+				data.targetWidth = data.height > data.width ? data.height : data.width;
+				if (!data.targetWidth) return;
+				this.fields = data;
+				let touchesX = '',
+					touchesY = '';
+				// #ifdef MP-BAIDU
+				touchesX = e.changedTouches[0].clientX;
+				touchesY = e.changedTouches[0].clientY;
+				// #endif
+				// #ifdef MP-ALIPAY
+				touchesX = e.detail.clientX;
+				touchesY = e.detail.clientY;
+				// #endif
+				// #ifndef MP-BAIDU || MP-ALIPAY
+				touchesX = e.touches[0].clientX;
+				touchesY = e.touches[0].clientY;
+				// #endif
+				// 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top
+				// 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置
+				// 总的来说,就是把水波纹的矩形(变换后的圆形)的中心点,移动到我们的触摸点位置
+				this.rippleTop = touchesY - data.top - data.targetWidth / 2;
+				this.rippleLeft = touchesX - data.left - data.targetWidth / 2;
+				this.$nextTick(() => {
+					this.waveActive = true;
+				});
+			});
+		},
+		// 获取节点信息
+		getElQuery() {
+			return new Promise(resolve => {
+				let queryInfo = '';
+				// 获取元素节点信息,请查看uniapp相关文档
+				// https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect
+				queryInfo = uni.createSelectorQuery().in(this);
+				//#ifdef MP-ALIPAY
+				queryInfo = uni.createSelectorQuery();
+				//#endif
+				queryInfo.select('.u-btn').boundingClientRect();
+				queryInfo.exec(data => {
+					resolve(data);
+				});
+			});
+		},
+		// 下面为对接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);
+		}
+	}
+};
+</script>
+
+<style scoped lang="scss">
+@import '../../libs/css/style.components.scss';
+.u-btn::after {
+	border: none;
+}
+
+.u-btn {
+	position: relative;
+	border: 0;
+	//border-radius: 10rpx;
+	/* #ifndef APP-NVUE */
+	display: inline-flex;		
+	/* #endif */
+	// 避免边框某些场景可能被“裁剪”,不能设置为hidden
+	overflow: visible;
+	line-height: 1;
+	@include vue-flex;
+	align-items: center;
+	justify-content: center;
+	cursor: pointer;
+	padding: 0 40rpx;
+	z-index: 1;
+	box-sizing: border-box;
+	transition: all 0.15s;
+	
+	&--bold-border {
+		border: 1px solid #ffffff;
+	}
+	
+	&--default {
+		color: $u-content-color;
+		border-color: #c0c4cc;
+		background-color: #ffffff;
+	}
+	
+	&--primary {
+		color: #ffffff;
+		border-color: $u-type-primary;
+		background-color: $u-type-primary;
+	}
+	
+	&--success {
+		color: #ffffff;
+		border-color: $u-type-success;
+		background-color: $u-type-success;
+	}
+	
+	&--error {
+		color: #ffffff;
+		border-color: $u-type-error;
+		background-color: $u-type-error;
+	}
+	
+	&--warning {
+		color: #ffffff;
+		border-color: $u-type-warning;
+		background-color: $u-type-warning;
+	}
+	
+	&--default--disabled {
+		color: #ffffff;
+		border-color: #e4e7ed;
+		background-color: #ffffff;
+	}
+	
+	&--primary--disabled {
+		color: #ffffff!important;
+		border-color: $u-type-primary-disabled!important;
+		background-color: $u-type-primary-disabled!important;
+	}
+	
+	&--success--disabled {
+		color: #ffffff!important;
+		border-color: $u-type-success-disabled!important;
+		background-color: $u-type-success-disabled!important;
+	}
+	
+	&--error--disabled {
+		color: #ffffff!important;
+		border-color: $u-type-error-disabled!important;
+		background-color: $u-type-error-disabled!important;
+	}
+	
+	&--warning--disabled {
+		color: #ffffff!important;
+		border-color: $u-type-warning-disabled!important;
+		background-color: $u-type-warning-disabled!important;
+	}
+	
+	&--primary--plain {
+		color: $u-type-primary!important;
+		border-color: $u-type-primary-disabled!important;
+		background-color: $u-type-primary-light!important;
+	}
+	
+	&--success--plain {
+		color: $u-type-success!important;
+		border-color: $u-type-success-disabled!important;
+		background-color: $u-type-success-light!important;
+	}
+	
+	&--error--plain {
+		color: $u-type-error!important;
+		border-color: $u-type-error-disabled!important;
+		background-color: $u-type-error-light!important;
+	}
+	
+	&--warning--plain {
+		color: $u-type-warning!important;
+		border-color: $u-type-warning-disabled!important;
+		background-color: $u-type-warning-light!important;
+	}
+}
+
+.u-hairline-border:after {
+	content: ' ';
+	position: absolute;
+	pointer-events: none;
+	// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
+	box-sizing: border-box;
+	// 中心点作为变形(scale())的原点
+	-webkit-transform-origin: 0 0;
+	transform-origin: 0 0;
+	left: 0;
+	top: 0;
+	width: 199.8%;
+	height: 199.7%;
+	-webkit-transform: scale(0.5, 0.5);
+	transform: scale(0.5, 0.5);
+	border: 1px solid currentColor;
+	z-index: 1;
+}
+
+.u-wave-ripple {
+	z-index: 0;
+	position: absolute;
+	border-radius: 100%;
+	background-clip: padding-box;
+	pointer-events: none;
+	user-select: none;
+	transform: scale(0);
+	opacity: 1;
+	transform-origin: center;
+}
+
+.u-wave-ripple.u-wave-active {
+	opacity: 0;
+	transform: scale(2);
+	transition: opacity 1s linear, transform 0.4s linear;
+}
+
+.u-round-circle {
+	border-radius: 100rpx;
+}
+
+.u-round-circle::after {
+	border-radius: 100rpx;
+}
+
+.u-loading::after {
+	background-color: hsla(0, 0%, 100%, 0.35);
+}
+
+.u-size-default {
+	font-size: 30rpx;
+	height: 80rpx;
+	line-height: 80rpx;
+}
+
+.u-size-medium {
+	/* #ifndef APP-NVUE */
+	display: inline-flex;		
+	/* #endif */
+	width: auto;
+	font-size: 26rpx;
+	height: 70rpx;
+	line-height: 70rpx;
+	padding: 0 80rpx;
+}
+
+.u-size-mini {
+	/* #ifndef APP-NVUE */
+	display: inline-flex;		
+	/* #endif */
+	width: auto;
+	font-size: 22rpx;
+	padding-top: 1px;
+	height: 50rpx;
+	line-height: 50rpx;
+	padding: 0 20rpx;
+}
+
+.u-primary-plain-hover {
+	color: #ffffff !important;
+	background: $u-type-primary-dark !important;
+}
+
+.u-default-plain-hover {
+	color: $u-type-primary-dark !important;
+	background: $u-type-primary-light !important;
+}
+
+.u-success-plain-hover {
+	color: #ffffff !important;
+	background: $u-type-success-dark !important;
+}
+
+.u-warning-plain-hover {
+	color: #ffffff !important;
+	background: $u-type-warning-dark !important;
+}
+
+.u-error-plain-hover {
+	color: #ffffff !important;
+	background: $u-type-error-dark !important;
+}
+
+.u-info-plain-hover {
+	color: #ffffff !important;
+	background: $u-type-info-dark !important;
+}
+
+.u-default-hover {
+	color: $u-type-primary-dark !important;
+	border-color: $u-type-primary-dark !important;
+	background-color: $u-type-primary-light !important;
+}
+
+.u-primary-hover {
+	background: $u-type-primary-dark !important;
+	color: #fff;
+}
+
+.u-success-hover {
+	background: $u-type-success-dark !important;
+	color: #fff;
+}
+
+.u-info-hover {
+	background: $u-type-info-dark !important;
+	color: #fff;
+}
+
+.u-warning-hover {
+	background: $u-type-warning-dark !important;
+	color: #fff;
+}
+
+.u-error-hover {
+	background: $u-type-error-dark !important;
+	color: #fff;
+}
+</style>

+ 639 - 0
uview-ui/components/u-calendar/u-calendar.vue

@@ -0,0 +1,639 @@
+<template>
+	<u-popup closeable :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto"
+	 :safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" :border-radius="borderRadius" :closeable="closeable">
+		<view class="u-calendar">
+			<view class="u-calendar__header">
+				<view class="u-calendar__header__text" v-if="!$slots['tooltip']">
+					{{toolTip}}
+				</view>
+				<slot v-else name="tooltip" />
+			</view>
+			<view class="u-calendar__action u-flex u-row-center">
+				<view class="u-calendar__action__icon">
+					<u-icon v-if="changeYear" name="arrow-left-double" :color="yearArrowColor" @click="changeYearHandler(0)"></u-icon>
+				</view>
+				<view class="u-calendar__action__icon">
+					<u-icon v-if="changeMonth" name="arrow-left" :color="monthArrowColor" @click="changeMonthHandler(0)"></u-icon>
+				</view>
+				<view class="u-calendar__action__text">{{ showTitle }}</view>
+				<view class="u-calendar__action__icon">
+					<u-icon v-if="changeMonth" name="arrow-right" :color="monthArrowColor" @click="changeMonthHandler(1)"></u-icon>
+				</view>
+				<view class="u-calendar__action__icon">
+					<u-icon v-if="changeYear" name="arrow-right-double" :color="yearArrowColor" @click="changeYearHandler(1)"></u-icon>
+				</view>
+			</view>
+			<view class="u-calendar__week-day">
+				<view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">{{item}}</view>
+			</view>
+			<view class="u-calendar__content">
+				<!-- 前置空白部分 -->
+				<block v-for="(item, index) in weekdayArr" :key="index">
+					<view class="u-calendar__content__item"></view>
+				</block>
+				<view class="u-calendar__content__item" :class="{
+					'u-hover-class':openDisAbled(year,month,index+1),
+					'u-calendar__content--start-date': (mode == 'range' && startDate==`${year}-${month}-${index+1}`) || mode== 'date',
+					'u-calendar__content--end-date':(mode== 'range' && endDate==`${year}-${month}-${index+1}`) || mode == 'date'
+				}" :style="{backgroundColor: getColor(index,1)}" v-for="(item, index) in daysArr" :key="index"
+				 @tap="dateClick(index)">
+					<view class="u-calendar__content__item__inner" :style="{color: getColor(index,2)}">
+						<view>{{ index + 1 }}</view>
+					</view>
+					<view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && startDate==`${year}-${month}-${index+1}` && startDate!=endDate">{{startText}}</view>
+					<view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && endDate==`${year}-${month}-${index+1}`">{{endText}}</view>
+				</view>
+				<view class="u-calendar__content__bg-month">{{month}}</view>
+			</view>
+			<view class="u-calendar__bottom">
+				<view class="u-calendar__bottom__choose">
+					<text>{{mode == 'date' ? activeDate : startDate}}</text>
+					<text v-if="endDate">至{{endDate}}</text>
+				</view>
+				<view class="u-calendar__bottom__btn">
+					<u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button>
+				</view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+<script>
+	/**
+	 * calendar 日历
+	 * @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。
+	 * @tutorial http://uviewui.com/components/calendar.html
+	 * @property {String} mode 选择日期的模式,date-为单个日期,range-为选择日期范围
+	 * @property {Boolean} v-model 布尔值变量,用于控制日历的弹出与收起
+	 * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
+	 * @property {Boolean} change-year 是否显示顶部的切换年份方向的按钮(默认true)
+	 * @property {Boolean} change-month 是否显示顶部的切换月份方向的按钮(默认true)
+	 * @property {String Number} max-year 可切换的最大年份(默认2050)
+	 * @property {String Number} min-year 可切换的最小年份(默认1950)
+	 * @property {String Number} min-date 最小可选日期(默认1950-01-01)
+	 * @property {String Number} max-date 最大可选日期(默认当前日期)
+	 * @property {String Number} 弹窗顶部左右两边的圆角值,单位rpx(默认20)
+	 * @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭日历(默认true)
+	 * @property {String} month-arrow-color 月份切换按钮箭头颜色(默认#606266)
+	 * @property {String} year-arrow-color 年份切换按钮箭头颜色(默认#909399)
+	 * @property {String} color 日期字体的默认颜色(默认#303133)
+	 * @property {String} active-bg-color 起始/结束日期按钮的背景色(默认#2979ff)
+	 * @property {String Number} z-index 弹出时的z-index值(默认10075)
+	 * @property {String} active-color 起始/结束日期按钮的字体颜色(默认#ffffff)
+	 * @property {String} range-bg-color 起始/结束日期之间的区域的背景颜色(默认rgba(41,121,255,0.13))
+	 * @property {String} range-color 选择范围内字体颜色(默认#2979ff)
+	 * @property {String} start-text 起始日期底部的提示文字(默认 '开始')
+	 * @property {String} end-text 结束日期底部的提示文字(默认 '结束')
+	 * @property {String} btn-type 底部确定按钮的主题(默认 'primary')
+	 * @property {String} toolTip 顶部提示文字,如设置名为tooltip的slot,此参数将失效(默认 '选择日期')
+	 * @property {Boolean} closeable 是否显示右上角的关闭图标(默认true)
+	 * @example <u-calendar v-model="show" :mode="mode"></u-calendar>
+	 */
+	
+	export default {
+		name: 'u-calendar',
+		props: {
+			safeAreaInsetBottom: {
+				type: Boolean,
+				default: false
+			},
+			// 是否允许通过点击遮罩关闭Picker
+			maskCloseAble: {
+				type: Boolean,
+				default: true
+			},
+			// 通过双向绑定控制组件的弹出与收起
+			value: {
+				type: Boolean,
+				default: false
+			},
+			// 弹出的z-index值
+			zIndex: {
+				type: [String, Number],
+				default: 0
+			},
+			// 是否允许切换年份
+			changeYear: {
+				type: Boolean,
+				default: true
+			},
+			// 是否允许切换月份
+			changeMonth: {
+				type: Boolean,
+				default: true
+			},
+			// date-单个日期选择,range-开始日期+结束日期选择
+			mode: {
+				type: String,
+				default: 'date'
+			},
+			// 可切换的最大年份
+			maxYear: {
+				type: [Number, String],
+				default: 2050
+			},
+			// 可切换的最小年份
+			minYear: {
+				type: [Number, String],
+				default: 1950
+			},
+			// 最小可选日期(不在范围内日期禁用不可选)
+			minDate: {
+				type: [Number, String],
+				default: '1950-01-01'
+			},
+			/**
+			 * 最大可选日期
+			 * 默认最大值为今天,之后的日期不可选
+			 * 2030-12-31
+			 * */
+			maxDate: {
+				type: [Number, String],
+				default: ''
+			},
+			// 弹窗顶部左右两边的圆角值
+			borderRadius: {
+				type: [String, Number],
+				default: 20
+			},
+			// 月份切换按钮箭头颜色
+			monthArrowColor: {
+				type: String,
+				default: '#606266'
+			},
+			// 年份切换按钮箭头颜色
+			yearArrowColor: {
+				type: String,
+				default: '#909399'
+			},
+			// 默认日期字体颜色
+			color: {
+				type: String,
+				default: '#303133'
+			},
+			// 选中|起始结束日期背景色
+			activeBgColor: {
+				type: String,
+				default: '#2979ff'
+			},
+			// 选中|起始结束日期字体颜色
+			activeColor: {
+				type: String,
+				default: '#ffffff'
+			},
+			// 范围内日期背景色
+			rangeBgColor: {
+				type: String,
+				default: 'rgba(41,121,255,0.13)'
+			}, 
+			// 范围内日期字体颜色
+			rangeColor: {
+				type: String,
+				default: '#2979ff'
+			},
+			// mode=range时生效,起始日期自定义文案
+			startText: {
+				type: String,
+				default: '开始'
+			},
+			// mode=range时生效,结束日期自定义文案
+			endText: {
+				type: String,
+				default: '结束'
+			},
+			//按钮样式类型
+			btnType: {
+				type: String,
+				default: 'primary'
+			},
+			// 当前选中日期带选中效果
+			isActiveCurrent: {
+				type: Boolean,
+				default: true
+			},
+			// 切换年月是否触发事件 mode=date时生效
+			isChange: {
+				type: Boolean,
+				default: false
+			},
+			// 是否显示右上角的关闭图标
+			closeable: {
+				type: Boolean,
+				default: true
+			},
+			// 顶部的提示文字
+			toolTip: {
+				type: String,
+				default: '选择日期'
+			}
+		},
+		data() {
+			return {
+				// 星期几,值为1-7
+				weekday: 1, 
+				weekdayArr:[],
+				// 当前月有多少天
+				days: 0, 
+				daysArr:[],
+				showTitle: '',
+				year: 2020,
+				month: 0,
+				day: 0,
+				startYear: 0,
+				startMonth: 0,
+				startDay: 0,
+				endYear: 0,
+				endMonth: 0,
+				endDay: 0,
+				today: '',
+				activeDate: '',
+				startDate: '',
+				endDate: '',
+				isStart: true,
+				min: null,
+				max: null,
+				weekDayZh: ['日', '一', '二', '三', '四', '五', '六']
+			};
+		},
+		computed: {
+			dataChange() {
+				return `${this.mode}-${this.minDate}-${this.maxDate}`;
+			},
+			uZIndex() {
+				// 如果用户有传递z-index值,优先使用
+				return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
+			}
+		},
+		watch: {
+			dataChange(val) {
+				this.init()
+			}
+		},
+		created() {
+			this.init()
+		},
+		methods: {
+			getColor(index, type) {
+				let color = type == 1 ? '' : this.color;
+				let day = index + 1
+				let date = `${this.year}-${this.month}-${day}`
+				let timestamp = new Date(date.replace(/\-/g, '/')).getTime();
+				let start = this.startDate.replace(/\-/g, '/')
+				let end = this.endDate.replace(/\-/g, '/')
+				if ((this.isActiveCurrent && this.activeDate == date) || this.startDate == date || this.endDate == date) {
+					color = type == 1 ? this.activeBgColor : this.activeColor;
+				} else if (this.endDate && timestamp > new Date(start).getTime() && timestamp < new Date(end).getTime()) {
+					color = type == 1 ? this.rangeBgColor : this.rangeColor;
+				}
+				return color;
+			},
+			init() {
+				let now = new Date();
+				this.year = now.getFullYear();
+				this.month = now.getMonth() + 1;
+				this.day = now.getDate();
+				this.today = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
+				this.activeDate = this.today;
+				this.min = this.initDate(this.minDate);
+				this.max = this.initDate(this.maxDate || this.today);
+				this.startDate = "";
+				this.startYear = 0;
+				this.startMonth = 0;
+				this.startDay = 0;
+				this.endYear = 0;
+				this.endMonth = 0;
+				this.endDay = 0;
+				this.endDate = "";
+				this.isStart = true;
+				this.changeData();
+			},
+			//日期处理
+			initDate(date) {
+				let fdate = date.split('-');
+				return {
+					year: Number(fdate[0] || 1920),
+					month: Number(fdate[1] || 1),
+					day: Number(fdate[2] || 1)
+				}
+			},
+			openDisAbled: function(year, month, day) {
+				let bool = true;
+				let date = `${year}/${month}/${day}`;
+				// let today = this.today.replace(/\-/g, '/');
+				let min = `${this.min.year}/${this.min.month}/${this.min.day}`;
+				let max = `${this.max.year}/${this.max.month}/${this.max.day}`;
+				let timestamp = new Date(date).getTime();
+				if (timestamp >= new Date(min).getTime() && timestamp <= new Date(max).getTime()) {
+					bool = false;
+				}
+				return bool;
+			},
+			generateArray: function(start, end) {
+				return Array.from(new Array(end + 1).keys()).slice(start);
+			},
+			formatNum: function(num) {
+				return num < 10 ? '0' + num : num + '';
+			},
+			//一个月有多少天
+			getMonthDay(year, month) {
+				let days = new Date(year, month, 0).getDate();
+				return days;
+			},
+			getWeekday(year, month) {
+				let date = new Date(`${year}/${month}/01 00:00:00`);
+				return date.getDay();
+			},
+			checkRange(year) {
+				let overstep = false;
+				if (year < this.minYear || year > this.maxYear) {
+					uni.showToast({
+						title: "日期超出范围啦~",
+						icon: 'none'
+					})
+					overstep = true;
+				}
+				return overstep;
+			},
+			changeMonthHandler(isAdd) {
+				if (isAdd) {
+					let month = this.month + 1;
+					let year = month > 12 ? this.year + 1 : this.year;
+					if (!this.checkRange(year)) {
+						this.month = month > 12 ? 1 : month;
+						this.year = year;
+						this.changeData();
+					}
+
+				} else {
+					let month = this.month - 1;
+					let year = month < 1 ? this.year - 1 : this.year;
+					if (!this.checkRange(year)) {
+						this.month = month < 1 ? 12 : month;
+						this.year = year;
+						this.changeData();
+					}
+				}
+			},
+			changeYearHandler(isAdd) {
+				let year = isAdd ? this.year + 1 : this.year - 1;
+				if (!this.checkRange(year)) {
+					this.year = year;
+					this.changeData();
+				}
+			},
+			changeData() {
+				this.days = this.getMonthDay(this.year, this.month);
+				this.daysArr=this.generateArray(1,this.days)
+				this.weekday = this.getWeekday(this.year, this.month);
+				this.weekdayArr=this.generateArray(1,this.weekday)
+				this.showTitle = `${this.year}年${this.month}月`;
+				if (this.isChange && this.mode == 'date') {
+					this.btnFix(true);
+				}
+			},
+			dateClick: function(day) {
+				day += 1;
+				if (!this.openDisAbled(this.year, this.month, day)) {
+					this.day = day;
+					let date = `${this.year}-${this.month}-${day}`;
+					if (this.mode == 'date') {
+						this.activeDate = date;
+					} else {
+						let compare = new Date(date.replace(/\-/g, '/')).getTime() < new Date(this.startDate.replace(/\-/g, '/')).getTime()
+						if (this.isStart || compare) {
+							this.startDate = date;
+							this.startYear = this.year;
+							this.startMonth = this.month;
+							this.startDay = this.day;
+							this.endYear = 0;
+							this.endMonth = 0;
+							this.endDay = 0;
+							this.endDate = "";
+							this.activeDate = "";
+							this.isStart = false;
+						} else {
+							this.endDate = date;
+							this.endYear = this.year;
+							this.endMonth = this.month;
+							this.endDay = this.day;
+							this.isStart = true;
+						}
+					}
+				}
+			},
+			close() {
+				// 修改通过v-model绑定的父组件变量的值为false,从而隐藏日历弹窗
+				this.$emit('input', false);
+			},
+			getWeekText(date) {
+				date = new Date(`${date.replace(/\-/g, '/')} 00:00:00`);
+				let week = date.getDay();
+				return '星期' + ['日', '一', '二', '三', '四', '五', '六'][week];
+			},
+			btnFix(show) {
+				if (!show) {
+					this.close();
+				}
+				if (this.mode == 'date') {
+					let arr = this.activeDate.split('-')
+					let year = this.isChange ? this.year : Number(arr[0]);
+					let month = this.isChange ? this.month : Number(arr[1]);
+					let day = this.isChange ? this.day : Number(arr[2]);
+					//当前月有多少天
+					let days = this.getMonthDay(year, month);
+					let result = `${year}-${this.formatNum(month)}-${this.formatNum(day)}`;
+					let weekText = this.getWeekText(result);
+					let isToday = false;
+					if (`${year}-${month}-${day}` == this.today) {
+						//今天
+						isToday = true;
+					}
+					this.$emit('change', {
+						year: year,
+						month: month,
+						day: day,
+						days: days,
+						result: result,
+						week: weekText,
+						isToday: isToday,
+						// switch: show //是否是切换年月操作
+					});
+				} else {
+					if (!this.startDate || !this.endDate) return;
+					let startMonth = this.formatNum(this.startMonth);
+					let startDay = this.formatNum(this.startDay);
+					let startDate = `${this.startYear}-${startMonth}-${startDay}`;
+					let startWeek = this.getWeekText(startDate)
+
+					let endMonth = this.formatNum(this.endMonth);
+					let endDay = this.formatNum(this.endDay);
+					let endDate = `${this.endYear}-${endMonth}-${endDay}`;
+					let endWeek = this.getWeekText(endDate);
+					this.$emit('change', {
+						startYear: this.startYear,
+						startMonth: this.startMonth,
+						startDay: this.startDay,
+						startDate: startDate,
+						startWeek: startWeek,
+						endYear: this.endYear,
+						endMonth: this.endMonth,
+						endDay: this.endDay,
+						endDate: endDate,
+						endWeek: endWeek
+					});
+				}
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	@import "../../libs/css/style.components.scss";
+	
+	.u-calendar {
+		color: $u-content-color;
+		
+		&__header {
+			width: 100%;
+			box-sizing: border-box;
+			font-size: 30rpx;
+			background-color: #fff;
+			color: $u-main-color;
+			
+			&__text {
+				margin-top: 30rpx;
+				padding: 0 60rpx;
+				@include vue-flex;
+				justify-content: center;
+				align-items: center;
+			}
+		}
+		
+		&__action {
+			padding: 40rpx 0 40rpx 0;
+			
+			&__icon {
+				margin: 0 16rpx;
+			}
+			
+			&__text {
+				padding: 0 16rpx;
+				color: $u-main-color;
+				font-size: 32rpx;
+				line-height: 32rpx;
+				font-weight: bold;
+			}
+		}
+	
+		&__week-day {
+			@include vue-flex;
+			align-items: center;
+			justify-content: center;
+			padding: 6px 0;
+			overflow: hidden;
+			
+			&__text {
+				flex: 1;
+				text-align: center;
+			}
+		}
+	
+		&__content {
+			width: 100%;
+			@include vue-flex;
+			flex-wrap: wrap;
+			padding: 6px 0;
+			box-sizing: border-box;
+			background-color: #fff;
+			position: relative;
+			
+			&--end-date {
+				border-top-right-radius: 8rpx;
+				border-bottom-right-radius: 8rpx;
+			}
+			
+			&--start-date {
+				border-top-left-radius: 8rpx;
+				border-bottom-left-radius: 8rpx;
+			}
+			
+			&__item {
+				width: 14.2857%;
+				@include vue-flex;
+				align-items: center;
+				justify-content: center;
+				padding: 6px 0;
+				overflow: hidden;
+				position: relative;
+				z-index: 2;
+				
+				&__inner {
+					height: 84rpx;
+					@include vue-flex;
+					align-items: center;
+					justify-content: center;
+					flex-direction: column;
+					font-size: 32rpx;
+					position: relative;
+					border-radius: 50%;
+					
+					&__desc {
+						width: 100%;
+						font-size: 24rpx;
+						line-height: 24rpx;
+						transform: scale(0.75);
+						transform-origin: center center;
+						position: absolute;
+						left: 0;
+						text-align: center;
+						bottom: 2rpx;
+					}
+				}
+				
+				&__tips {
+					width: 100%;
+					font-size: 24rpx;
+					line-height: 24rpx;
+					position: absolute;
+					left: 0;
+					transform: scale(0.8);
+					transform-origin: center center;
+					text-align: center;
+					bottom: 8rpx;
+					z-index: 2;
+				}
+			}
+			
+			&__bg-month {
+				position: absolute;
+				font-size: 130px;
+				line-height: 130px;
+				left: 50%;
+				top: 50%;
+				transform: translate(-50%, -50%);
+				color: #e4e7ed;
+				z-index: 1;
+			}
+		}
+	
+		&__bottom {
+			width: 100%;
+			@include vue-flex;
+			align-items: center;
+			justify-content: center;
+			flex-direction: column;
+			background-color: #fff;
+			padding: 0 40rpx 30rpx;
+			box-sizing: border-box;
+			font-size: 24rpx;
+			color: $u-tips-color;
+			
+			&__choose {
+				height: 50rpx;
+			}
+			
+			&__btn {
+				width: 100%;
+			}
+		}
+	}
+</style>

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

@@ -0,0 +1,257 @@
+<template>
+	<view class="u-keyboard" @touchmove.stop.prevent="() => {}">
+		<view class="u-keyboard-grids">
+			<block>
+				<view class="u-keyboard-grids-item" v-for="(group, i) in abc ? EngKeyBoardList : areaList" :key="i">
+					<view :hover-stay-time="100" @tap="carInputClick(i, j)" hover-class="u-carinput-hover" class="u-keyboard-grids-btn"
+					 v-for="(item, j) in group" :key="j">
+						{{ item }}
+					</view>
+				</view>
+				<view @touchstart="backspaceClick" @touchend="clearTimer" :hover-stay-time="100" class="u-keyboard-back"
+				 hover-class="u-hover-class">
+					<u-icon :size="38" name="backspace" :bold="true"></u-icon>
+				</view>
+				<view :hover-stay-time="100" class="u-keyboard-change" hover-class="u-carinput-hover" @tap="changeCarInputMode">
+					<text class="zh" :class="[!abc ? 'active' : 'inactive']">中</text>
+					/
+					<text class="en" :class="[abc ? 'active' : 'inactive']">英</text>
+				</view>
+			</block>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "u-keyboard",
+		props: {
+			// 是否打乱键盘按键的顺序
+			random: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称
+				abc: false
+			};
+		},
+		computed: {
+			areaList() {
+				let data = [
+					'京',
+					'沪',
+					'粤',
+					'津',
+					'冀',
+					'豫',
+					'云',
+					'辽',
+					'黑',
+					'湘',
+					'皖',
+					'鲁',
+					'苏',
+					'浙',
+					'赣',
+					'鄂',
+					'桂',
+					'甘',
+					'晋',
+					'陕',
+					'蒙',
+					'吉',
+					'闽',
+					'贵',
+					'渝',
+					'川',
+					'青',
+					'琼',
+					'宁',
+					'挂',
+					'藏',
+					'港',
+					'澳',
+					'新',
+					'使',
+					'学'
+				];
+				let tmp = [];
+				// 打乱顺序
+				if (this.random) data = this.$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 = this.$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];
+				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/style.components.scss";
+
+	.u-keyboard-grids {
+		background: rgb(215, 215, 217);
+		padding: 24rpx 0;
+		position: relative;
+	}
+
+	.u-keyboard-grids-item {
+		@include vue-flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.u-keyboard-grids-btn {
+		text-decoration: none;
+		width: 62rpx;
+		flex: 0 0 64rpx;
+		height: 80rpx;
+		/* #ifndef APP-NVUE */
+		display: inline-flex;		
+		/* #endif */
+		font-size: 36rpx;
+		text-align: center;
+		line-height: 80rpx;
+		background-color: #fff;
+		margin: 8rpx 5rpx;
+		border-radius: 8rpx;
+		box-shadow: 0 2rpx 0rpx #888992;
+		font-weight: 500;
+		justify-content: center;
+	}
+
+	.u-carinput-hover {
+		background-color: rgb(185, 188, 195) !important;
+	}
+
+	.u-keyboard-back {
+		position: absolute;
+		width: 96rpx;
+		right: 22rpx;
+		bottom: 32rpx;
+		height: 80rpx;
+		background-color: rgb(185, 188, 195);
+		@include vue-flex;
+		align-items: center;
+		border-radius: 8rpx;
+		justify-content: center;
+		box-shadow: 0 2rpx 0rpx #888992;
+	}
+
+	.u-keyboard-change {
+		font-size: 24rpx;
+		box-shadow: 0 2rpx 0rpx #888992;
+		position: absolute;
+		width: 96rpx;
+		left: 22rpx;
+		line-height: 1;
+		bottom: 32rpx;
+		height: 80rpx;
+		background-color: #ffffff;
+		@include vue-flex;
+		align-items: center;
+		border-radius: 8rpx;
+		justify-content: center;
+	}
+
+	.u-keyboard-change .inactive.zh {
+		transform: scale(0.85) translateY(-10rpx);
+	}
+
+	.u-keyboard-change .inactive.en {
+		transform: scale(0.85) translateY(10rpx);
+	}
+
+	.u-keyboard-change .active {
+		color: rgb(237, 112, 64);
+		font-size: 30rpx;
+	}
+
+	.u-keyboard-change .zh {
+		transform: translateY(-10rpx);
+	}
+
+	.u-keyboard-change .en {
+		transform: translateY(10rpx);
+	}
+</style>

+ 299 - 0
uview-ui/components/u-card/u-card.vue

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

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

@@ -0,0 +1,70 @@
+<template>
+	<view class="u-cell-box">
+		<view class="u-cell-title" v-if="title" :style="[titleStyle]">
+			{{title}}
+		</view>
+		<view class="u-cell-item-box" :class="{'u-border-bottom u-border-top': border}">
+			<slot />
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * cellGroup 单元格父组件Group
+	 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-item
+	 * @tutorial https://www.uviewui.com/components/cell.html
+	 * @property {String} title 分组标题
+	 * @property {Boolean} border 是否显示外边框(默认true)
+	 * @property {Object} title-style 分组标题的的样式,对象形式,如{'font-size': '24rpx'} 或 {'fontSize': '24rpx'}
+	 * @example <u-cell-group title="设置喜好">
+	 */
+	export default {
+		name: "u-cell-group",
+		props: {
+			// 分组标题
+			title: {
+				type: String,
+				default: ''
+			},
+			// 是否显示分组list上下边框
+			border: {
+				type: Boolean,
+				default: true
+			},
+			// 分组标题的样式,对象形式,注意驼峰属性写法
+			// 类似 {'font-size': '24rpx'} 和 {'fontSize': '24rpx'}
+			titleStyle: {
+				type: Object,
+				default () {
+					return {};
+				}
+			}
+		},
+		data() {
+			return {
+				index: 0,
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+	
+	.u-cell-box {
+		width: 100%;
+	}
+
+	.u-cell-title {
+		padding: 30rpx 32rpx 10rpx 32rpx;
+		font-size: 30rpx;
+		text-align: left;
+		color: $u-tips-color;
+	}
+
+	.u-cell-item-box {
+		background-color: #FFFFFF;
+		flex-direction: row;
+	}
+</style>

+ 316 - 0
uview-ui/components/u-cell-item/u-cell-item.vue

@@ -0,0 +1,316 @@
+<template>
+	<view
+		@tap="click"
+		class="u-cell"
+		:class="{ 'u-border-bottom': borderBottom, 'u-border-top': borderTop, 'u-col-center': center, 'u-cell--required': required }"
+		hover-stay-time="150"
+		:hover-class="hoverClass"
+		:style="{
+			backgroundColor: bgColor
+		}"
+	>
+		<u-icon :size="iconSize" :name="icon" v-if="icon" :custom-style="iconStyle" class="u-cell__left-icon-wrap"></u-icon>
+		<view class="u-flex" v-else>
+			<slot name="icon"></slot>
+		</view>
+		<view
+			class="u-cell_title"
+			:style="[
+				{
+					width: titleWidth ? titleWidth + 'rpx' : 'auto'
+				},
+				titleStyle
+			]"
+		>
+			<block v-if="title !== ''">{{ title }}</block>
+			<slot name="title" v-else></slot>
+
+			<view class="u-cell__label" v-if="label || $slots.label" :style="[labelStyle]">
+				<block v-if="label !== ''">{{ label }}</block>
+				<slot name="label" v-else></slot>
+			</view>
+		</view>
+
+		<view class="u-cell__value" :style="[valueStyle]">
+			<block class="u-cell__value" v-if="value !== ''">{{ value }}</block>
+			<slot v-else></slot>
+		</view>
+		<view class="u-flex u-cell_right" v-if="$slots['right-icon']">
+			<slot name="right-icon"></slot>
+		</view>
+		<u-icon v-if="arrow" name="arrow-right" :style="[arrowStyle]" class="u-icon-wrap u-cell__right-icon-wrap"></u-icon>
+	</view>
+</template>
+
+<script>
+/**
+ * cellItem 单元格Item
+ * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-group使用
+ * @tutorial https://www.uviewui.com/components/cell.html
+ * @property {String} title 左侧标题
+ * @property {String} icon 左侧图标名,只支持uView内置图标,见Icon 图标
+ * @property {Object} icon-style 左边图标的样式,对象形式
+ * @property {String} value 右侧内容
+ * @property {String} label 标题下方的描述信息
+ * @property {Boolean} border-bottom 是否显示cell的下边框(默认true)
+ * @property {Boolean} border-top 是否显示cell的上边框(默认false)
+ * @property {Boolean} center 是否使内容垂直居中(默认false)
+ * @property {String} hover-class 是否开启点击反馈,none为无效果(默认true)
+ * // @property {Boolean} border-gap border-bottom为true时,Cell列表中间的条目的下边框是否与左边有一个间隔(默认true)
+ * @property {Boolean} arrow 是否显示右侧箭头(默认true)
+ * @property {Boolean} required 箭头方向,可选值(默认right)
+ * @property {Boolean} arrow-direction 是否显示左边表示必填的星号(默认false)
+ * @property {Object} title-style 标题样式,对象形式
+ * @property {Object} value-style 右侧内容样式,对象形式
+ * @property {Object} label-style 标题下方描述信息的样式,对象形式
+ * @property {String} bg-color 背景颜色(默认transparent)
+ * @property {String Number} index 用于在click事件回调中返回,标识当前是第几个Item
+ * @property {String Number} title-width 标题的宽度,单位rpx
+ * @example <u-cell-item icon="integral-fill" title="会员等级" value="新版本"></u-cell-item>
+ */
+export default {
+	name: 'u-cell-item',
+	props: {
+		// 左侧图标名称(只能uView内置图标),或者图标src
+		icon: {
+			type: String,
+			default: ''
+		},
+		// 左侧标题
+		title: {
+			type: [String, Number],
+			default: ''
+		},
+		// 右侧内容
+		value: {
+			type: [String, Number],
+			default: ''
+		},
+		// 标题下方的描述信息
+		label: {
+			type: [String, Number],
+			default: ''
+		},
+		// 是否显示下边框
+		borderBottom: {
+			type: Boolean,
+			default: true
+		},
+		// 是否显示上边框
+		borderTop: {
+			type: Boolean,
+			default: false
+		},
+		// 多个cell中,中间的cell显示下划线时,下划线是否给一个到左边的距离
+		// 1.4.0版本废除此参数,默认边框由border-top和border-bottom提供,此参数会造成干扰
+		// borderGap: {
+		// 	type: Boolean,
+		// 	default: true
+		// },
+		// 是否开启点击反馈,即点击时cell背景为灰色,none为无效果
+		hoverClass: {
+			type: String,
+			default: 'u-cell-hover'
+		},
+		// 是否显示右侧箭头
+		arrow: {
+			type: Boolean,
+			default: true
+		},
+		// 内容是否垂直居中
+		center: {
+			type: Boolean,
+			default: false
+		},
+		// 是否显示左边表示必填的星号
+		required: {
+			type: Boolean,
+			default: false
+		},
+		// 标题的宽度,单位rpx
+		titleWidth: {
+			type: [Number, String],
+			default: ''
+		},
+		// 右侧箭头方向,可选值:right|up|down,默认为right
+		arrowDirection: {
+			type: String,
+			default: 'right'
+		},
+		// 控制标题的样式
+		titleStyle: {
+			type: Object,
+			default() {
+				return {};
+			}
+		},
+		// 右侧显示内容的样式
+		valueStyle: {
+			type: Object,
+			default() {
+				return {};
+			}
+		},
+		// 描述信息的样式
+		labelStyle: {
+			type: Object,
+			default() {
+				return {};
+			}
+		},
+		// 背景颜色
+		bgColor: {
+			type: String,
+			default: 'transparent'
+		},
+		// 用于识别被点击的是第几个cell
+		index: {
+			type: [String, Number],
+			default: ''
+		},
+		// 是否使用lable插槽
+		useLabelSlot: {
+			type: Boolean,
+			default: false
+		},
+		// 左边图标的大小,单位rpx,只对传入icon字段时有效
+		iconSize: {
+			type: [Number, String],
+			default: 34
+		},
+		// 左边图标的样式,对象形式
+		iconStyle: {
+			type: Object,
+			default() {
+				return {}
+			}
+		},
+	},
+	data() {
+		return {
+
+		};
+	},
+	computed: {
+		arrowStyle() {
+			let style = {};
+			if (this.arrowDirection == 'up') style.transform = 'rotate(-90deg)';
+			else if (this.arrowDirection == 'down') style.transform = 'rotate(90deg)';
+			else style.transform = 'rotate(0deg)';
+			return style;
+		}
+	},
+	methods: {
+		click() {
+			this.$emit('click', this.index);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../libs/css/style.components.scss";
+.u-cell {
+	@include vue-flex;
+	align-items: center;
+	position: relative;
+	/* #ifndef APP-NVUE */
+	box-sizing: border-box;
+	/* #endif */
+	width: 100%;
+	padding: 26rpx 32rpx;
+	font-size: 28rpx;
+	line-height: 54rpx;
+	color: $u-content-color;
+	background-color: #fff;
+	text-align: left;
+}
+
+.u-cell_title {
+	font-size: 28rpx;
+}
+
+.u-cell__left-icon-wrap {
+	margin-right: 10rpx;
+	font-size: 32rpx;
+}
+
+.u-cell__right-icon-wrap {
+	margin-left: 10rpx;
+	color: #969799;
+	font-size: 28rpx;
+}
+
+.u-cell__left-icon-wrap,
+.u-cell__right-icon-wrap {
+	@include vue-flex;
+	align-items: center;
+	height: 48rpx;
+}
+
+.u-cell-border:after {
+	position: absolute; 
+	/* #ifndef APP-NVUE */
+	box-sizing: border-box;
+	content: ' ';
+	pointer-events: none;
+	border-bottom: 1px solid $u-border-color;
+	/* #endif */
+	right: 0;
+	left: 0;
+	top: 0;
+	transform: scaleY(0.5);
+}
+
+.u-cell-border {
+	position: relative;
+}
+
+.u-cell__label {
+	margin-top: 6rpx;
+	font-size: 26rpx;
+	line-height: 36rpx;
+	color: $u-tips-color;
+	/* #ifndef APP-NVUE */
+	word-wrap: break-word;
+	/* #endif */
+}
+
+.u-cell__value {
+	overflow: hidden;
+	text-align: right;
+	/* #ifndef APP-NVUE */
+	vertical-align: middle;
+	/* #endif */
+	color: $u-tips-color;
+	font-size: 26rpx;
+}
+
+.u-cell__title,
+.u-cell__value {
+	flex: 1;
+}
+
+.u-cell--required {
+	/* #ifndef APP-NVUE */
+	overflow: visible;
+	/* #endif */
+	@include vue-flex;
+	align-items: center;
+}
+
+.u-cell--required:before {
+	position: absolute;
+	/* #ifndef APP-NVUE */
+	content: '*';
+	/* #endif */
+	left: 8px;
+	margin-top: 4rpx;
+	font-size: 14px;
+	color: $u-type-error;
+}
+
+.u-cell_right {
+	line-height: 1;
+}
+</style>

+ 123 - 0
uview-ui/components/u-checkbox-group/u-checkbox-group.vue

@@ -0,0 +1,123 @@
+<template>
+	<view class="u-checkbox-group u-clearfix">
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	import Emitter from '../../libs/util/emitter.js';
+	/**
+	 * checkboxGroup 开关选择器父组件Group
+	 * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
+	 * @tutorial https://www.uviewui.com/components/checkbox.html
+	 * @property {String Number} max 最多能选中多少个checkbox(默认999)
+	 * @property {String Number} size 组件整体的大小,单位rpx(默认40)
+	 * @property {Boolean} disabled 是否禁用所有checkbox(默认false)
+	 * @property {String Number} icon-size 图标大小,单位rpx(默认20)
+	 * @property {Boolean} label-disabled 是否禁止点击文本操作checkbox(默认false)
+	 * @property {String} width 宽度,需带单位
+	 * @property {String} width 宽度,需带单位
+	 * @property {String} shape 外观形状,shape-方形,circle-圆形(默认circle)
+	 * @property {Boolean} wrap 是否每个checkbox都换行(默认false)
+	 * @property {String} active-color 选中时的颜色,应用到所有子Checkbox组件(默认#2979ff)
+	 * @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象
+	 * @example <u-checkbox-group></u-checkbox-group>
+	 */
+	export default {
+		name: 'u-checkbox-group',
+		mixins: [Emitter],
+		props: {
+			// 最多能选中多少个checkbox
+			max: {
+				type: [Number, String],
+				default: 999
+			},
+			// 所有选中项的 name
+			// value: {
+			// 	default: Array,
+			// 	default() {
+			// 		return []
+			// 	}
+			// },
+			// 是否禁用所有复选框
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			// 在表单内提交时的标识符
+			name: {
+				type: [Boolean, String],
+				default: ''
+			},
+			// 是否禁止点击提示语选中复选框
+			labelDisabled: {
+				type: Boolean,
+				default: false
+			},
+			// 形状,square为方形,circle为原型
+			shape: {
+				type: String,
+				default: 'square'
+			},
+			// 选中状态下的颜色
+			activeColor: {
+				type: String,
+				default: '#2979ff'
+			},
+			// 组件的整体大小
+			size: {
+				type: [String, Number],
+				default: 34
+			},
+			// 每个checkbox占u-checkbox-group的宽度
+			width: {
+				type: String,
+				default: 'auto'
+			},
+			// 是否每个checkbox都换行
+			wrap: { 
+				type: Boolean,
+				default: false
+			},
+			// 图标的大小,单位rpx
+			iconSize: {
+				type: [String, Number],
+				default: 20
+			},
+		},
+		data() {
+			return {
+			}
+		},
+		created() {
+			// 如果将children定义在data中,在微信小程序会造成循环引用而报错
+			this.children = [];
+		},
+		methods: {
+			emitEvent() {
+				let values = [];
+				this.children.map(val => {
+					if(val.value) values.push(val.name);
+				})
+				this.$emit('change', values);
+				// 发出事件,用于在表单组件中嵌入checkbox的情况,进行验证
+				// 由于头条小程序执行迟钝,故需要用几十毫秒的延时
+				setTimeout(() => {
+					// 将当前的值发送到 u-form-item 进行校验
+					this.dispatch('u-form-item', 'on-form-change', values);
+				}, 60)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+
+	.u-checkbox-group {
+		/* #ifndef MP || APP-NVUE */
+		display: inline-flex;
+		flex-wrap: wrap;
+		/* #endif */
+	}
+</style>

+ 284 - 0
uview-ui/components/u-checkbox/u-checkbox.vue

@@ -0,0 +1,284 @@
+<template>
+	<view class="u-checkbox" :style="[checkboxStyle]">
+		<view class="u-checkbox__icon-wrap" @tap="toggle" :class="[iconClass]" :style="[iconStyle]">
+			<u-icon class="u-checkbox__icon-wrap__icon" name="checkbox-mark" :size="checkboxIconSize" :color="iconColor"/>
+		</view>
+		<view class="u-checkbox__label" @tap="onClickLabel" :style="{
+			fontSize: $u.addUnit(labelSize)
+		}">
+			<slot />
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * checkbox 复选框
+	 * @description 该组件需要搭配checkboxGroup组件使用,以便用户进行操作时,获得当前复选框组的选中情况。
+	 * @tutorial https://www.uviewui.com/components/checkbox.html
+	 * @property {String Number} icon-size 图标大小,单位rpx(默认20)
+	 * @property {String Number} label-size label字体大小,单位rpx(默认28)
+	 * @property {String Number} name checkbox组件的标示符
+	 * @property {String} shape 形状,见官网说明(默认circle)
+	 * @property {Boolean} disabled 是否禁用
+	 * @property {Boolean} label-disabled 是否禁止点击文本操作checkbox
+	 * @property {String} active-color 选中时的颜色,如设置CheckboxGroup的active-color将失效
+	 * @event {Function} change 某个checkbox状态发生变化时触发,回调为一个对象
+	 * @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox>
+	 */
+	export default {
+		name: "u-checkbox",
+		props: {
+			// checkbox的名称
+			name: {
+				type: [String, Number],
+				default: ''
+			},
+			// 形状,square为方形,circle为原型
+			shape: {
+				type: String,
+				default: ''
+			},
+			// 是否为选中状态
+			value: {
+				type: Boolean,
+				default: false
+			},
+			// 是否禁用
+			disabled: {
+				type: [String, Boolean],
+				default: ''
+			},
+			// 是否禁止点击提示语选中复选框
+			labelDisabled: {
+				type: [String, Boolean],
+				default: ''
+			},
+			// 选中状态下的颜色,如设置此值,将会覆盖checkboxGroup的activeColor值
+			activeColor: {
+				type: String,
+				default: ''
+			},
+			// 图标的大小,单位rpx
+			iconSize: {
+				type: [String, Number],
+				default: ''
+			},
+			// label的字体大小,rpx单位
+			labelSize: {
+				type: [String, Number],
+				default: ''
+			},
+			// 组件的整体大小
+			size: {
+				type: [String, Number],
+				default: ''
+			},
+		},
+		data() {
+			return {
+				parentDisabled: false,
+				newParams: {},
+			};
+		},
+		created() {
+			// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用
+			this.parent = this.$u.$parent.call(this, 'u-checkbox-group');
+			// 如果存在u-checkbox-group,将本组件的this塞进父组件的children中
+			this.parent && this.parent.children.push(this);
+		},
+		computed: {
+			// 是否禁用,如果父组件u-checkbox-group禁用的话,将会忽略子组件的配置
+			isDisabled() {
+				return this.disabled !== '' ? this.disabled : this.parent ? this.parent.disabled : false;
+			},
+			// 是否禁用label点击
+			isLabelDisabled() {
+				return this.labelDisabled !== '' ? this.labelDisabled : this.parent ? this.parent.labelDisabled : false;
+			},
+			// 组件尺寸,对应size的值,默认值为34rpx
+			checkboxSize() {
+				return this.size ? this.size : (this.parent ? this.parent.size : 34);
+			},
+			// 组件的勾选图标的尺寸,默认20
+			checkboxIconSize() {
+				return this.iconSize ? this.iconSize : (this.parent ? this.parent.iconSize : 20);
+			},
+			// 组件选中激活时的颜色
+			elActiveColor() {
+				return this.activeColor ? this.activeColor : (this.parent ? this.parent.activeColor : 'primary');
+			},
+			// 组件的形状
+			elShape() {
+				return this.shape ? this.shape : (this.parent ? this.parent.shape : 'square');
+			},
+			iconStyle() {
+				let style = {};
+				// 既要判断是否手动禁用,还要判断用户v-model绑定的值,如果绑定为false,那么也无法选中
+				if (this.elActiveColor && this.value && !this.isDisabled) {
+					style.borderColor = this.elActiveColor; 
+					style.backgroundColor = this.elActiveColor;
+				}
+				style.width = this.$u.addUnit(this.checkboxSize);
+				style.height = this.$u.addUnit(this.checkboxSize);
+				return style;
+			},
+			// checkbox内部的勾选图标,如果选中状态,为白色,否则为透明色即可
+			iconColor() {
+				return this.value ? '#ffffff' : 'transparent';
+			},
+			iconClass() {
+				let classes = [];
+				classes.push('u-checkbox__icon-wrap--' + this.elShape);
+				if (this.value == true) classes.push('u-checkbox__icon-wrap--checked');
+				if (this.isDisabled) classes.push('u-checkbox__icon-wrap--disabled');
+				if (this.value && this.isDisabled) classes.push('u-checkbox__icon-wrap--disabled--checked');
+				// 支付宝小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+				return classes.join(' ');
+			},
+			checkboxStyle() {
+				let style = {};
+				if(this.parent && this.parent.width) {
+					style.width = this.parent.width;
+					// #ifdef MP
+					// 各家小程序因为它们特殊的编译结构,使用float布局
+					style.float = 'left';
+					// #endif
+					// #ifndef MP
+					// H5和APP使用flex布局
+					style.flex = `0 0 ${this.parent.width}`;
+					// #endif
+				}
+				if(this.parent && this.parent.wrap) {
+					style.width = '100%';
+					// #ifndef MP
+					// H5和APP使用flex布局,将宽度设置100%,即可自动换行
+					style.flex = '0 0 100%';
+					// #endif
+				}
+				return style;
+			}
+		},
+		methods: {
+			onClickLabel() {
+				if (!this.isLabelDisabled && !this.isDisabled) {
+					this.setValue();
+				}
+			},
+			toggle() {
+				if (!this.isDisabled) {
+					this.setValue();
+				}
+			},
+			emitEvent() {
+				this.$emit('change', {
+					value: !this.value,
+					name: this.name
+				})
+				// 执行父组件u-checkbox-group的事件方法
+				// 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间
+				setTimeout(() => {
+					if(this.parent && this.parent.emitEvent) this.parent.emitEvent();
+				}, 80);
+			},
+			// 设置input的值,这里通过input事件,设置通过v-model绑定的组件的值
+			setValue() {
+				// 判断是否超过了可选的最大数量
+				let checkedNum = 0;
+				if(this.parent && this.parent.children) {
+					// 只要父组件的某一个子元素的value为true,就加1(已有的选中数量)
+					this.parent.children.map(val => {
+						if (val.value) checkedNum++;
+					})
+				}
+				// 如果原来为选中状态,那么可以取消
+				if (this.value == true) {
+					this.emitEvent();
+					this.$emit('input', !this.value);
+				} else {
+					// 如果超出最多可选项,提示
+					if(this.parent && checkedNum >= this.parent.max) {
+						return this.$u.toast(`最多可选${this.parent.max}项`);
+					}
+					// 如果原来为未选中状态,需要选中的数量少于父组件中设置的max值,才可以选中
+					this.emitEvent();
+					this.$emit('input', !this.value);
+				}
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+
+	.u-checkbox {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		align-items: center;
+		overflow: hidden;
+		user-select: none;
+		line-height: 1.8;
+		
+		&__icon-wrap {
+			color: $u-content-color;
+			flex: none;
+			display: -webkit-flex;
+			@include vue-flex;
+			align-items: center;
+			justify-content: center;
+			box-sizing: border-box;
+			width: 42rpx;
+			height: 42rpx;
+			color: transparent;
+			text-align: center;
+			transition-property: color, border-color, background-color;
+			font-size: 20px;
+			border: 1px solid #c8c9cc;
+			transition-duration: 0.2s;
+			
+			/* #ifdef MP-TOUTIAO */
+			// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
+			&__icon {
+				line-height: 0;
+			}
+			/* #endif */
+			
+			&--circle {
+				border-radius: 100%;
+			}
+			
+			&--square {
+				border-radius: 6rpx;
+			}
+			
+			&--checked {
+				color: #fff;
+				background-color: $u-type-primary;
+				border-color: $u-type-primary;
+			}
+			
+			&--disabled {
+				background-color: #ebedf0;
+				border-color: #c8c9cc;
+			}
+			
+			&--disabled--checked {
+				color: #c8c9cc !important;
+			}
+		}
+	
+		&__label {
+			word-wrap: break-word;
+			margin-left: 10rpx;
+			margin-right: 24rpx;
+			color: $u-content-color;
+			font-size: 30rpx;
+			
+			&--disabled {
+				color: #c8c9cc;
+			}
+		}
+	}
+</style>

+ 220 - 0
uview-ui/components/u-circle-progress/u-circle-progress.vue

@@ -0,0 +1,220 @@
+<template>
+	<view
+		class="u-circle-progress"
+		:style="{
+			width: widthPx + 'px',
+			height: widthPx + 'px',
+			backgroundColor: bgColor
+		}"
+	>
+		<!-- 支付宝小程序不支持canvas-id属性,必须用id属性 -->
+		<canvas
+			class="u-canvas-bg"
+			:canvas-id="elBgId"
+			:id="elBgId"
+			:style="{
+				width: widthPx + 'px',
+				height: widthPx + 'px'
+			}"
+		></canvas>
+		<canvas
+			class="u-canvas"
+			:canvas-id="elId"
+			:id="elId"
+			:style="{
+				width: widthPx + 'px',
+				height: widthPx + 'px'
+			}"
+		></canvas>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+/**
+ * circleProgress 环形进度条
+ * @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度条。注意:此组件的percent值只能动态增加,不能动态减少。
+ * @tutorial https://www.uviewui.com/components/circleProgress.html
+ * @property {String Number} percent 圆环进度百分比值,为数值类型,0-100
+ * @property {String} inactive-color 圆环的底色,默认为灰色(该值无法动态变更)(默认#ececec)
+ * @property {String} active-color 圆环激活部分的颜色(该值无法动态变更)(默认#19be6b)
+ * @property {String Number} width 整个圆环组件的宽度,高度默认等于宽度值,单位rpx(默认200)
+ * @property {String Number} border-width 圆环的边框宽度,单位rpx(默认14)
+ * @property {String Number} duration 整个圆环执行一圈的时间,单位ms(默认呢1500)
+ * @property {String} type 如设置,active-color值将会失效
+ * @property {String} bg-color 整个组件背景颜色,默认为白色
+ * @example <u-circle-progress active-color="#2979ff" :percent="80"></u-circle-progress>
+ */
+export default {
+	name: 'u-circle-progress',
+	props: {
+		// 圆环进度百分比值
+		percent: {
+			type: Number,
+			default: 0,
+			// 限制值在0到100之间
+			validator: val => {
+				return val >= 0 && val <= 100;
+			}
+		},
+		// 底部圆环的颜色(灰色的圆环)
+		inactiveColor: {
+			type: String,
+			default: '#ececec'
+		},
+		// 圆环激活部分的颜色
+		activeColor: {
+			type: String,
+			default: '#19be6b'
+		},
+		// 圆环线条的宽度,单位rpx
+		borderWidth: {
+			type: [Number, String],
+			default: 14
+		},
+		// 整个圆形的宽度,单位rpx
+		width: {
+			type: [Number, String],
+			default: 200
+		},
+		// 整个圆环执行一圈的时间,单位ms
+		duration: {
+			type: [Number, String],
+			default: 1500
+		},
+		// 主题类型
+		type: {
+			type: String,
+			default: ''
+		},
+		// 整个圆环进度区域的背景色
+		bgColor: {
+			type: String,
+			default: '#ffffff'
+		}
+	},
+	data() {
+		return {
+			// #ifdef MP-WEIXIN
+			elBgId: 'uCircleProgressBgId', // 微信小程序中不能使用this.$u.guid()形式动态生成id值,否则会报错
+			elId: 'uCircleProgressElId',
+			// #endif
+			// #ifndef MP-WEIXIN
+			elBgId: this.$u.guid(), // 非微信端的时候,需用动态的id,否则一个页面多个圆形进度条组件数据会混乱
+			elId: this.$u.guid(),
+			// #endif
+			widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
+			borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
+			startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向
+			progressContext: null, // 活动圆的canvas上下文
+			newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
+			oldPercent: 0 // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
+		};
+	},
+	watch: {
+		percent(nVal, oVal = 0) {
+			if (nVal > 100) nVal = 100;
+			if (nVal < 0) oVal = 0;
+			// 此值其实等于this.percent,命名一个新
+			this.newPercent = nVal;
+			this.oldPercent = oVal;
+			setTimeout(() => {
+				// 无论是百分比值增加还是减少,需要操作还是原来的旧的百分比值
+				// 将此值减少或者新增到新的百分比值
+				this.drawCircleByProgress(oVal);
+			}, 50);
+		}
+	},
+	created() {
+		// 赋值,用于加载后第一个画圆使用
+		this.newPercent = this.percent;
+		this.oldPercent = 0;
+	},
+	computed: {
+		// 有type主题时,优先起作用
+		circleColor() {
+			if (['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type];
+			else return this.activeColor;
+		}
+	},
+	mounted() {
+		// 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7)
+		setTimeout(() => {
+			this.drawProgressBg();
+			this.drawCircleByProgress(this.oldPercent);
+		}, 50);
+	},
+	methods: {
+		drawProgressBg() {
+			let ctx = uni.createCanvasContext(this.elBgId, this);
+			ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度
+			ctx.setStrokeStyle(this.inactiveColor); // 线条颜色
+			ctx.beginPath(); // 开始描绘路径
+			// 设置一个原点(110,110),半径为100的圆的路径到当前路径
+			let radius = this.widthPx / 2;
+			ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI, false);
+			ctx.stroke(); // 对路径进行描绘
+			ctx.draw();
+		},
+		drawCircleByProgress(progress) {
+			// 第一次操作进度环时将上下文保存到了this.data中,直接使用即可
+			let ctx = this.progressContext;
+			if (!ctx) {
+				ctx = uni.createCanvasContext(this.elId, this);
+				this.progressContext = ctx;
+			}
+			// 表示进度的两端为圆形
+			ctx.setLineCap('round');
+			// 设置线条的宽度和颜色
+			ctx.setLineWidth(this.borderWidthPx);
+			ctx.setStrokeStyle(this.circleColor);
+			// 将总过渡时间除以100,得出每修改百分之一进度所需的时间
+			let time = Math.floor(this.duration / 100);
+			// 结束角的计算依据为:将2π分为100份,乘以当前的进度值,得出终止点的弧度值,加起始角,为整个圆从默认的
+			// 3点钟方向开始画图,转为更好理解的12点钟方向开始作图,这需要起始角和终止角同时加上this.startAngle值
+			let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle;
+			ctx.beginPath();
+			// 半径为整个canvas宽度的一半
+			let radius = this.widthPx / 2;
+			ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false);
+			ctx.stroke();
+			ctx.draw();
+			// 如果变更后新值大于旧值,意味着增大了百分比
+			if (this.newPercent > this.oldPercent) {
+				// 每次递增百分之一
+				progress++;
+				// 如果新增后的值,大于需要设置的值百分比值,停止继续增加
+				if (progress > this.newPercent) return;
+			} else {
+				// 同理于上面
+				progress--;
+				if (progress < this.newPercent) return;
+			}
+			setTimeout(() => {
+				// 定时器,每次操作间隔为time值,为了让进度条有动画效果
+				this.drawCircleByProgress(progress);
+			}, time);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../libs/css/style.components.scss";
+.u-circle-progress {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: inline-flex;		
+	/* #endif */
+	align-items: center;
+	justify-content: center;
+}
+
+.u-canvas-bg {
+	position: absolute;
+}
+
+.u-canvas {
+	position: absolute;
+}
+</style>

+ 147 - 0
uview-ui/components/u-circle-progress/u-line-progress/u-line-progress.vue

@@ -0,0 +1,147 @@
+<template>
+	<view class="u-progress" :style="{
+		borderRadius: round ? '100rpx' : 0,
+		height: height + 'rpx',
+		backgroundColor: inactiveColor
+	}">
+		<view :class="[
+			type ? `u-type-${type}-bg` : '',
+			striped ? 'u-striped' : '',
+			striped && stripedActive ? 'u-striped-active' : ''
+		]" class="u-active" :style="[progressStyle]">
+			<slot v-if="$slots.default || $slots.$default" />
+			<block v-else-if="showPercent">
+				{{percent + '%'}}
+			</block>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * lineProgress 线型进度条
+	 * @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。
+	 * @tutorial https://www.uviewui.com/components/lineProgress.html
+	 * @property {String Number} percent 进度条百分比值,为数值类型,0-100
+	 * @property {Boolean} round 进度条两端是否为半圆(默认true)
+	 * @property {String} type 如设置,active-color值将会失效
+	 * @property {String} active-color 进度条激活部分的颜色(默认#19be6b)
+	 * @property {String} inactive-color 进度条的底色(默认#ececec)
+	 * @property {Boolean} show-percent 是否在进度条内部显示当前的百分比值数值(默认true)
+	 * @property {String Number} height 进度条的高度,单位rpx(默认28)
+	 * @property {Boolean} striped 是否显示进度条激活部分的条纹(默认false)
+	 * @property {Boolean} striped-active 条纹是否具有动态效果(默认false)
+	 * @example <u-line-progress :percent="70" :show-percent="true"></u-line-progress>
+	 */
+	export default {
+		name: "u-line-progress",
+		props: {
+			// 两端是否显示半圆形
+			round: {
+				type: Boolean,
+				default: true
+			},
+			// 主题颜色
+			type: {
+				type: String,
+				default: ''
+			},
+			// 激活部分的颜色
+			activeColor: {
+				type: String,
+				default: '#19be6b'
+			},
+			inactiveColor: {
+				type: String,
+				default: '#ececec'
+			},
+			// 进度百分比,数值
+			percent: {
+				type: Number,
+				default: 0
+			},
+			// 是否在进度条内部显示百分比的值
+			showPercent: {
+				type: Boolean,
+				default: true
+			},
+			// 进度条的高度,单位rpx
+			height: {
+				type: [Number, String],
+				default: 28
+			},
+			// 是否显示条纹
+			striped: {
+				type: Boolean,
+				default: false
+			},
+			// 条纹是否显示活动状态
+			stripedActive: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			progressStyle() {
+				let style = {};
+				style.width = this.percent + '%';
+				if(this.activeColor) style.backgroundColor = this.activeColor;
+				return style;
+			}
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+	
+	.u-progress {
+		overflow: hidden;
+		height: 15px;
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		align-items: center;
+		width: 100%;
+		border-radius: 100rpx;
+	}
+
+	.u-active {
+		width: 0;
+		height: 100%;
+		align-items: center;
+		@include vue-flex;
+		justify-items: flex-end;
+		justify-content: space-around;
+		font-size: 20rpx;
+		color: #ffffff;
+		transition: all 0.4s ease;
+	}
+
+	.u-striped {
+		background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+		background-size: 39px 39px;
+	}
+
+	.u-striped-active {
+		animation: progress-stripes 2s linear infinite;
+	}
+
+	@keyframes progress-stripes {
+		0% {
+			background-position: 0 0;
+		}
+
+		100% {
+			background-position: 39px 0;
+		}
+	}
+</style>

+ 156 - 0
uview-ui/components/u-col/u-col.vue

@@ -0,0 +1,156 @@
+<template>
+	<view class="u-col" :class="[
+		'u-col-' + span
+	]" :style="{
+		padding: `0 ${Number(gutter)/2 + 'rpx'}`,
+		marginLeft: 100 / 12 * offset + '%',
+		flex: `0 0 ${100 / 12 * span}%`,
+		alignItems: uAlignItem,
+		justifyContent: uJustify,
+		textAlign: textAlign
+	}"
+	 @tap="click">
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	/**
+	 * col 布局单元格
+	 * @description 通过基础的 12 分栏,迅速简便地创建布局(搭配<u-row>使用)
+	 * @tutorial https://www.uviewui.com/components/layout.html
+	 * @property {String Number} span 栅格占据的列数,总12等分(默认0)
+	 * @property {String} text-align 文字水平对齐方式(默认left)
+	 * @property {String Number} offset 分栏左边偏移,计算方式与span相同(默认0)
+	 * @example <u-col span="3"><view class="demo-layout bg-purple"></view></u-col>
+	 */
+	export default {
+		name: "u-col",
+		props: {
+			// 占父容器宽度的多少等分,总分为12份
+			span: {
+				type: [Number, String],
+				default: 12
+			},
+			// 指定栅格左侧的间隔数(总12栏)
+			offset: {
+				type: [Number, String],
+				default: 0
+			},
+			// 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)
+			justify: {
+				type: String,
+				default: 'start'
+			},
+			// 垂直对齐方式,可选值为top、center、bottom
+			align: {
+				type: String,
+				default: 'center'
+			},
+			// 文字对齐方式
+			textAlign: {
+				type: String,
+				default: 'left'
+			},
+			// 是否阻止事件传播
+			stop: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				gutter: 20, // 给col添加间距,左右边距各占一半,从父组件u-row获取
+			}
+		},
+		created() {
+			this.parent = false;
+		},
+		mounted() {
+			// 获取父组件实例,并赋值给对应的参数
+			this.parent = this.$u.$parent.call(this, 'u-row');
+			if (this.parent) {
+				this.gutter = this.parent.gutter;
+			}
+		},
+		computed: {
+			uJustify() {
+				if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify;
+				else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify;
+				else return this.justify;
+			},
+			uAlignItem() {
+				if (this.align == 'top') return 'flex-start';
+				if (this.align == 'bottom') return 'flex-end';
+				else return this.align;
+			}
+		},
+		methods: {
+			click(e) {
+				this.$emit('click');
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import "../../libs/css/style.components.scss";
+
+	.u-col {
+		/* #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO */
+		float: left;
+		/* #endif */
+	}
+
+	.u-col-0 {
+		width: 0;
+	}
+
+	.u-col-1 {
+		width: calc(100%/12);
+	}
+
+	.u-col-2 {
+		width: calc(100%/12 * 2);
+	}
+
+	.u-col-3 {
+		width: calc(100%/12 * 3);
+	}
+
+	.u-col-4 {
+		width: calc(100%/12 * 4);
+	}
+
+	.u-col-5 {
+		width: calc(100%/12 * 5);
+	}
+
+	.u-col-6 {
+		width: calc(100%/12 * 6);
+	}
+
+	.u-col-7 {
+		width: calc(100%/12 * 7);
+	}
+
+	.u-col-8 {
+		width: calc(100%/12 * 8);
+	}
+
+	.u-col-9 {
+		width: calc(100%/12 * 9);
+	}
+
+	.u-col-10 {
+		width: calc(100%/12 * 10);
+	}
+
+	.u-col-11 {
+		width: calc(100%/12 * 11);
+	}
+
+	.u-col-12 {
+		width: calc(100%/12 * 12);
+	}
+</style>

+ 205 - 0
uview-ui/components/u-collapse-item/u-collapse-item.vue

@@ -0,0 +1,205 @@
+<template>
+	<view class="u-collapse-item" :style="[itemStyle]">
+		<view :hover-stay-time="200" class="u-collapse-head" @tap.stop="headClick" :hover-class="hoverClass" :style="[headStyle]">
+			<block v-if="!$slots['title-all']">
+				<view v-if="!$slots['title']" class="u-collapse-title u-line-1" :style="[{ textAlign: align ? align : 'left' },
+					isShow && activeStyle && !arrow ? activeStyle : '']">
+					{{ title }}
+				</view>
+				<slot v-else name="title" />
+				<view class="u-icon-wrap">
+					<u-icon v-if="arrow" :color="arrowColor" :class="{ 'u-arrow-down-icon-active': isShow }"
+					 class="u-arrow-down-icon" name="arrow-down"></u-icon>
+				</view>
+			</block>
+			<slot v-else name="title-all" />
+		</view>
+		<view class="u-collapse-body" :style="[{
+				height: isShow ? height + 'px' : '0'
+			}]">
+			<view class="u-collapse-content" :id="elId" :style="[bodyStyle]">
+				<slot></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * collapseItem 手风琴Item
+	 * @description 通过折叠面板收纳内容区域(搭配u-collapse使用)
+	 * @tutorial https://www.uviewui.com/components/collapse.html
+	 * @property {String} title 面板标题
+	 * @property {String Number} index 主要用于事件的回调,标识那个Item被点击
+	 * @property {Boolean} disabled 面板是否可以打开或收起(默认false)
+	 * @property {Boolean} open 设置某个面板的初始状态是否打开(默认false)
+	 * @property {String Number} name 唯一标识符,如不设置,默认用当前collapse-item的索引值
+	 * @property {String} align 标题的对齐方式(默认left)
+	 * @property {Object} active-style 不显示箭头时,可以添加当前选择的collapse-item活动样式,对象形式
+	 * @event {Function} change 某个item被打开或者收起时触发
+	 * @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item>
+	 */
+	export default {
+		name: "u-collapse-item",
+		props: {
+			// 标题
+			title: {
+				type: String,
+				default: ''
+			},
+			// 标题的对齐方式
+			align: {
+				type: String,
+				default: 'left'
+			},
+			// 是否可以点击收起
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			// collapse显示与否
+			open: {
+				type: Boolean,
+				default: false
+			},
+			// 唯一标识符
+			name: {
+				type: [Number, String],
+				default: ''
+			},
+			//活动样式
+			activeStyle: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			// 标识当前为第几个
+			index: {
+				type: [String, Number],
+				default: ''
+			}
+		},
+		data() {
+			return {
+				isShow: false,
+				elId: this.$u.guid(),
+				height: 0, // body内容的高度
+				headStyle: {}, // 头部样式,对象形式
+				bodyStyle: {}, // 主体部分样式
+				itemStyle: {}, // 每个item的整体样式
+				arrowColor: '', // 箭头的颜色
+				hoverClass: '', // 头部按下时的效果样式类
+				arrow: true, // 是否显示右侧箭头
+				
+			};
+		},
+		watch: {
+			open(val) {
+				this.isShow = val;
+			}
+		},
+		created() {
+			this.parent = false;
+			// 获取u-collapse的信息,放在u-collapse是为了方便,不用每个u-collapse-item写一遍
+			this.isShow = this.open;
+		},
+		methods: {
+			// 异步获取内容,或者动态修改了内容时,需要重新初始化
+			init() {
+				this.parent = this.$u.$parent.call(this, 'u-collapse');
+				if(this.parent) {
+					this.nameSync = this.name ? this.name : this.parent.childrens.length;
+					// 不存在时才添加本实例
+					!this.parent.childrens.includes(this) && this.parent.childrens.push(this);
+					this.headStyle = this.parent.headStyle;
+					this.bodyStyle = this.parent.bodyStyle;
+					this.arrowColor = this.parent.arrowColor;
+					this.hoverClass = this.parent.hoverClass;
+					this.arrow = this.parent.arrow;
+					this.itemStyle = this.parent.itemStyle;
+				}
+				this.$nextTick(() => {
+					this.queryRect();
+				});
+			},
+			// 点击collapsehead头部
+			headClick() {
+				if (this.disabled) return;
+				if (this.parent && this.parent.accordion == true) {
+					this.parent.childrens.map(val => {
+						// 自身不设置为false,因为后面有this.isShow = !this.isShow;处理了
+						if (this != val) {
+							val.isShow = false;
+						}
+					});
+				}
+
+				this.isShow = !this.isShow;
+				// 触发本组件的事件
+				this.$emit('change', {
+					index: this.index,
+					show: this.isShow
+				})
+				// 只有在打开时才发出事件
+				if (this.isShow) this.parent && this.parent.onChange();
+				this.$forceUpdate();
+			},
+			// 查询内容高度
+			queryRect() {
+				// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html
+				// 组件内部一般用this.$uGetRect,对外的为this.$u.getRect,二者功能一致,名称不同
+				this.$uGetRect('#' + this.elId).then(res => {
+					this.height = res.height;
+				})
+			}
+		},
+		mounted() {
+			this.init();
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+	
+	.u-collapse-head {
+		position: relative;
+		@include vue-flex;
+		justify-content: space-between;
+		align-items: center;
+		color: $u-main-color;
+		font-size: 30rpx;
+		line-height: 1;
+		padding: 24rpx 0;
+		text-align: left;
+	}
+
+	.u-collapse-title {
+		flex: 1;
+		overflow: hidden;
+	}
+
+	.u-arrow-down-icon {
+		transition: all 0.3s;
+		margin-right: 20rpx;
+		margin-left: 14rpx;
+	}
+
+	.u-arrow-down-icon-active {
+		transform: rotate(180deg);
+		transform-origin: center center;
+	}
+
+	.u-collapse-body {
+		overflow: hidden;
+		transition: all 0.3s;
+	}
+
+	.u-collapse-content {
+		overflow: hidden;
+		font-size: 28rpx;
+		color: $u-tips-color;
+		text-align: left;
+	}
+</style>

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików