瀏覽代碼

项目初始化

seimin 3 年之前
當前提交
d5f8178daa
共有 100 個文件被更改,包括 12244 次插入0 次删除
  1. 2 0
      .gitignore
  2. 16 0
      .hbuilderx/launch.json
  3. 32 0
      App.vue
  4. 140 0
      components/uni-section/uni-section.vue
  5. 14 0
      index.html
  6. 25 0
      main.js
  7. 58 0
      manifest.json
  8. 17 0
      pages.json
  9. 29 0
      pages/index/index.vue
  10. 二進制
      static/c1.png
  11. 二進制
      static/c2.png
  12. 二進制
      static/c3.png
  13. 二進制
      static/c4.png
  14. 二進制
      static/c5.png
  15. 二進制
      static/c6.png
  16. 二進制
      static/c7.png
  17. 二進制
      static/c8.png
  18. 二進制
      static/c9.png
  19. 20 0
      static/customicons.css
  20. 二進制
      static/customicons.ttf
  21. 二進制
      static/logo.png
  22. 二進制
      static/uni.png
  23. 1 0
      uni.scss
  24. 29 0
      uni_modules/uni-badge/changelog.md
  25. 268 0
      uni_modules/uni-badge/components/uni-badge/uni-badge.vue
  26. 88 0
      uni_modules/uni-badge/package.json
  27. 10 0
      uni_modules/uni-badge/readme.md
  28. 10 0
      uni_modules/uni-calendar/changelog.md
  29. 546 0
      uni_modules/uni-calendar/components/uni-calendar/calendar.js
  30. 12 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/en.json
  31. 8 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/index.js
  32. 12 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json
  33. 12 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json
  34. 188 0
      uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue
  35. 555 0
      uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue
  36. 354 0
      uni_modules/uni-calendar/components/uni-calendar/util.js
  37. 88 0
      uni_modules/uni-calendar/package.json
  38. 103 0
      uni_modules/uni-calendar/readme.md
  39. 26 0
      uni_modules/uni-card/changelog.md
  40. 270 0
      uni_modules/uni-card/components/uni-card/uni-card.vue
  41. 90 0
      uni_modules/uni-card/package.json
  42. 12 0
      uni_modules/uni-card/readme.md
  43. 36 0
      uni_modules/uni-collapse/changelog.md
  44. 402 0
      uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue
  45. 147 0
      uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue
  46. 89 0
      uni_modules/uni-collapse/package.json
  47. 12 0
      uni_modules/uni-collapse/readme.md
  48. 15 0
      uni_modules/uni-combox/changelog.md
  49. 275 0
      uni_modules/uni-combox/components/uni-combox/uni-combox.vue
  50. 90 0
      uni_modules/uni-combox/package.json
  51. 11 0
      uni_modules/uni-combox/readme.md
  52. 24 0
      uni_modules/uni-countdown/changelog.md
  53. 6 0
      uni_modules/uni-countdown/components/uni-countdown/i18n/en.json
  54. 8 0
      uni_modules/uni-countdown/components/uni-countdown/i18n/index.js
  55. 6 0
      uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json
  56. 6 0
      uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json
  57. 271 0
      uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue
  58. 86 0
      uni_modules/uni-countdown/package.json
  59. 10 0
      uni_modules/uni-countdown/readme.md
  60. 41 0
      uni_modules/uni-data-checkbox/changelog.md
  61. 817 0
      uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue
  62. 87 0
      uni_modules/uni-data-checkbox/package.json
  63. 18 0
      uni_modules/uni-data-checkbox/readme.md
  64. 52 0
      uni_modules/uni-data-picker/changelog.md
  65. 45 0
      uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
  66. 537 0
      uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
  67. 563 0
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
  68. 333 0
      uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
  69. 92 0
      uni_modules/uni-data-picker/package.json
  70. 22 0
      uni_modules/uni-data-picker/readme.md
  71. 10 0
      uni_modules/uni-dateformat/changelog.md
  72. 200 0
      uni_modules/uni-dateformat/components/uni-dateformat/date-format.js
  73. 88 0
      uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue
  74. 88 0
      uni_modules/uni-dateformat/package.json
  75. 11 0
      uni_modules/uni-dateformat/readme.md
  76. 85 0
      uni_modules/uni-datetime-picker/changelog.md
  77. 185 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
  78. 898 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
  79. 19 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
  80. 8 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
  81. 19 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
  82. 19 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
  83. 45 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js
  84. 927 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
  85. 981 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
  86. 410 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
  87. 90 0
      uni_modules/uni-datetime-picker/package.json
  88. 21 0
      uni_modules/uni-datetime-picker/readme.md
  89. 13 0
      uni_modules/uni-drawer/changelog.md
  90. 45 0
      uni_modules/uni-drawer/components/uni-drawer/keypress.js
  91. 183 0
      uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
  92. 87 0
      uni_modules/uni-drawer/package.json
  93. 10 0
      uni_modules/uni-drawer/readme.md
  94. 31 0
      uni_modules/uni-easyinput/changelog.md
  95. 56 0
      uni_modules/uni-easyinput/components/uni-easyinput/common.js
  96. 461 0
      uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
  97. 90 0
      uni_modules/uni-easyinput/package.json
  98. 11 0
      uni_modules/uni-easyinput/readme.md
  99. 17 0
      uni_modules/uni-fab/changelog.md
  100. 0 0
      uni_modules/uni-fab/components/uni-fab/uni-fab.vue

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
1
+unpackage/
2
+node_modules/

+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
1
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
2
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
3
+    "version": "0.0",
4
+    "configurations": [{
5
+     	"app-plus" : 
6
+     	{
7
+     		"launchtype" : "local"
8
+     	},
9
+     	"default" : 
10
+     	{
11
+     		"launchtype" : "local"
12
+     	},
13
+     	"type" : "uniCloud"
14
+     }
15
+    ]
16
+}

+ 32 - 0
App.vue

@@ -0,0 +1,32 @@
1
+<script>
2
+	export default {
3
+		onLaunch: function() {
4
+			console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!')
5
+			console.log('App Launch')
6
+		},
7
+		onShow: function() {
8
+			console.log('App Show')
9
+		},
10
+		onHide: function() {
11
+			console.log('App Hide')
12
+		}
13
+	}
14
+</script>
15
+
16
+<style lang="scss">
17
+	/*每个页面公共css */
18
+	@import '@/uni_modules/uni-scss/index.scss';
19
+	/* #ifndef APP-NVUE */
20
+	@import '@/static/customicons.css';
21
+	// 设置整个项目的背景色
22
+	page {
23
+		background-color: #f5f5f5;
24
+	}
25
+
26
+	/* #endif */
27
+	.example-info {
28
+		font-size: 14px;
29
+		color: #333;
30
+		padding: 10px;
31
+	}
32
+</style>

+ 140 - 0
components/uni-section/uni-section.vue

@@ -0,0 +1,140 @@
1
+<template>
2
+	<view class="uni-section">
3
+		<view class="uni-section-header" nvue>
4
+			<view v-if="type" class="uni-section__head">
5
+				<view :class="type" class="uni-section__head-tag"/>
6
+			</view>
7
+			<view class="uni-section__content">
8
+				<text :class="{'distraction':!subTitle}" :style="{color:color}" class="uni-section__content-title">{{ title }}</text>
9
+				<text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text>
10
+			</view>
11
+		</view>
12
+		<view :style="{padding: padding ? '10px' : ''}">
13
+			<slot/>
14
+		</view>
15
+	</view>
16
+</template>
17
+
18
+<script>
19
+
20
+	/**
21
+	 * Section 标题栏
22
+	 * @description 标题栏
23
+	 * @property {String} type = [line|circle] 标题装饰类型
24
+	 * 	@value line 竖线
25
+	 * 	@value circle 圆形
26
+	 * @property {String} title 主标题
27
+	 * @property {String} subTitle 副标题
28
+	 */
29
+
30
+	export default {
31
+		name: 'UniSection',
32
+		emits:['click'],
33
+		props: {
34
+			type: {
35
+				type: String,
36
+				default: ''
37
+			},
38
+			title: {
39
+				type: String,
40
+				default: ''
41
+			},
42
+			color:{
43
+				type: String,
44
+				default: '#333'
45
+			},
46
+			subTitle: {
47
+				type: String,
48
+				default: ''
49
+			},
50
+			padding: {
51
+				type: Boolean,
52
+				default: false
53
+			}
54
+		},
55
+		data() {
56
+			return {}
57
+		},
58
+		watch: {
59
+			title(newVal) {
60
+				if (uni.report && newVal !== '') {
61
+					uni.report('title', newVal)
62
+				}
63
+			}
64
+		},
65
+		methods: {
66
+			onClick() {
67
+				this.$emit('click')
68
+			}
69
+		}
70
+	}
71
+</script>
72
+<style lang="scss" >
73
+	$uni-primary: #2979ff !default;
74
+	
75
+	.uni-section {
76
+		background-color: #fff;
77
+		// overflow: hidden;
78
+		margin-top: 10px;
79
+	}
80
+	.uni-section-header {
81
+		position: relative;
82
+		/* #ifndef APP-NVUE */
83
+		display: flex;
84
+		/* #endif */
85
+		flex-direction: row;
86
+		align-items: center;
87
+		padding: 12px 10px;
88
+		// height: 50px;
89
+		font-weight: normal;
90
+	}
91
+	.uni-section__head {
92
+		flex-direction: row;
93
+		justify-content: center;
94
+		align-items: center;
95
+		margin-right: 10px;
96
+	}
97
+
98
+	.line {
99
+		height: 12px;
100
+		background-color: $uni-primary;
101
+		border-radius: 10px;
102
+		width: 4px;
103
+	}
104
+
105
+	.circle {
106
+		width: 8px;
107
+		height: 8px;
108
+		border-top-right-radius: 50px;
109
+		border-top-left-radius: 50px;
110
+		border-bottom-left-radius: 50px;
111
+		border-bottom-right-radius: 50px;
112
+		background-color: $uni-primary;
113
+	}
114
+
115
+	.uni-section__content {
116
+		/* #ifndef APP-NVUE */
117
+		display: flex;
118
+		/* #endif */
119
+		flex-direction: column;
120
+		flex: 1;
121
+		color: #333;
122
+	}
123
+
124
+	.uni-section__content-title {
125
+		font-size: 14px;
126
+		color: $uni-primary;
127
+	}
128
+
129
+	.distraction {
130
+		flex-direction: row;
131
+		align-items: center;
132
+	}
133
+
134
+	.uni-section__content-sub {
135
+		font-size: 12px;
136
+		color: #999;
137
+		line-height: 16px;
138
+		margin-top: 2px;
139
+	}
140
+</style>

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+    <title></title>
7
+    <!--preload-links-->
8
+    <!--app-context-->
9
+  </head>
10
+  <body>
11
+    <div id="app"><!--app-html--></div>
12
+    <script type="module" src="/main.js"></script>
13
+  </body>
14
+</html>

+ 25 - 0
main.js

@@ -0,0 +1,25 @@
1
+
2
+// #ifndef VUE3
3
+import Vue from 'vue'
4
+import App from './App'
5
+
6
+Vue.config.productionTip = false
7
+
8
+App.mpType = 'app'
9
+
10
+const app = new Vue({
11
+    ...App
12
+})
13
+app.$mount()
14
+// #endif
15
+
16
+// #ifdef VUE3
17
+import { createSSRApp } from 'vue'
18
+import App from './App.vue'
19
+export function createApp() {
20
+  const app = createSSRApp(App)
21
+  return {
22
+    app
23
+  }
24
+}
25
+// #endif

+ 58 - 0
manifest.json

@@ -0,0 +1,58 @@
1
+{
2
+	"name" : "zy-pda",
3
+	"appid" : "__UNI__FEB1B1E",
4
+	"description": "",
5
+	"versionName": "1.0.0",
6
+	"versionCode": "100",
7
+	"transformPx": false,
8
+	"app-plus": { /* 5+App特有相关 */
9
+		"usingComponents": true,
10
+		"nvueCompiler": "uni-app",
11
+		"splashscreen": {
12
+			"alwaysShowBeforeRender": true,
13
+			"waiting": true,
14
+			"autoclose": true,
15
+			"delay": 0
16
+		},
17
+		"modules": { /* 模块配置 */
18
+
19
+		},
20
+		"distribute": { /* 应用发布信息 */
21
+			"android": { /* android打包配置 */
22
+				"permissions": [
23
+					"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
24
+					"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
25
+					"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
26
+					"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
27
+					"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
28
+					"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
29
+					"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
30
+					"<uses-permission android:name=\"android.permission.CAMERA\"/>",
31
+					"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
32
+					"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
33
+					"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
34
+					"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
35
+					"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
36
+					"<uses-feature android:name=\"android.hardware.camera\"/>",
37
+					"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
38
+				]
39
+			},
40
+			"ios": { /* ios打包配置 */
41
+
42
+			},
43
+			"sdkConfigs": { /* SDK配置 */
44
+
45
+			}
46
+		}
47
+	},
48
+	"quickapp": { /* 快应用特有相关 */
49
+
50
+	},
51
+	"mp-weixin": { /* 小程序特有相关 */
52
+		"appid": "",
53
+		"setting": {
54
+			"urlCheck": false
55
+		},
56
+		"usingComponents": true
57
+	}
58
+}

+ 17 - 0
pages.json

@@ -0,0 +1,17 @@
1
+{
2
+	"pages": [{
3
+		"path": "pages/index/index",
4
+		"style": {
5
+			"navigationBarTitleText": "uni-app"
6
+		}
7
+	}],
8
+	"globalStyle": {
9
+		"navigationBarTextStyle": "black",
10
+		"navigationBarTitleText": "uni-app",
11
+		"navigationBarBackgroundColor": "#F8F8F8",
12
+		"backgroundColor": "#F8F8F8",
13
+		"app-plus": {
14
+			"background": "#efeff4"
15
+		}
16
+	}
17
+}

+ 29 - 0
pages/index/index.vue

@@ -0,0 +1,29 @@
1
+<template>
2
+	<view class="container">
3
+		
4
+		<view class="intro">本项目已包含uni ui组件,无需import和注册,可直接使用。在代码区键入字母u,即可通过代码助手列出所有可用组件。光标置于组件名称处按F1,即可查看组件文档。</view>
5
+		<text class="intro">详见:</text>
6
+		<uni-link :href="href" :text="href"></uni-link>
7
+	</view>
8
+</template>
9
+
10
+<script>
11
+	export default {
12
+		data() {
13
+			return {
14
+				href: 'https://uniapp.dcloud.io/component/README?id=uniui'
15
+			}
16
+		},
17
+		methods: {
18
+
19
+		}
20
+	}
21
+</script>
22
+
23
+<style>
24
+	.container {
25
+		padding: 20px;
26
+		font-size: 14px;
27
+		line-height: 24px;
28
+	}
29
+</style>

二進制
static/c1.png


二進制
static/c2.png


二進制
static/c3.png


二進制
static/c4.png


二進制
static/c5.png


二進制
static/c6.png


二進制
static/c7.png


二進制
static/c8.png


二進制
static/c9.png


+ 20 - 0
static/customicons.css

@@ -0,0 +1,20 @@
1
+@font-face {
2
+  font-family: "customicons"; /* Project id 2878519 */
3
+  src:url('/static/customicons.ttf') format('truetype');
4
+}
5
+
6
+.customicons {
7
+  font-family: "customicons" !important;
8
+}
9
+
10
+.youxi:before {
11
+  content: "\e60e";
12
+}
13
+
14
+.wenjian:before {
15
+  content: "\e60f";
16
+}
17
+
18
+.zhuanfa:before {
19
+  content: "\e610";
20
+}

二進制
static/customicons.ttf


二進制
static/logo.png


二進制
static/uni.png


+ 1 - 0
uni.scss

@@ -0,0 +1 @@
1
+@import '@/uni_modules/uni-scss/variables.scss';

+ 29 - 0
uni_modules/uni-badge/changelog.md

@@ -0,0 +1,29 @@
1
+## 1.2.0(2021-11-19)
2
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
3
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
4
+## 1.1.7(2021-11-08)
5
+- 优化 升级ui
6
+- 修改 size 属性默认值调整为 small
7
+- 修改 type 属性,默认值调整为 error,info 替换 default
8
+## 1.1.6(2021-09-22)
9
+- 修复 在字节小程序上样式不生效的 bug
10
+## 1.1.5(2021-07-30)
11
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
12
+## 1.1.4(2021-07-29)
13
+- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性
14
+## 1.1.3(2021-06-24)
15
+- 优化 示例项目
16
+## 1.1.1(2021-05-12)
17
+- 新增 组件示例地址
18
+## 1.1.0(2021-05-12)
19
+- 新增 uni-badge 的 absolute 属性,支持定位
20
+- 新增 uni-badge 的 offset 属性,支持定位偏移
21
+- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
22
+- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
23
+- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式
24
+## 1.0.7(2021-05-07)
25
+- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug
26
+- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug
27
+- 新增 uni-badge 属性 custom-style, 支持自定义样式
28
+## 1.0.6(2021-02-04)
29
+- 调整为uni_modules目录规范

+ 268 - 0
uni_modules/uni-badge/components/uni-badge/uni-badge.vue

@@ -0,0 +1,268 @@
1
+<template>
2
+	<view class="uni-badge--x">
3
+		<slot />
4
+		<text v-if="text" :class="classNames" :style="[badgeWidth, positionStyle, customStyle, dotStyle]"
5
+			class="uni-badge" @click="onClick()">{{displayValue}}</text>
6
+	</view>
7
+</template>
8
+
9
+<script>
10
+	/**
11
+	 * Badge 数字角标
12
+	 * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景
13
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=21
14
+	 * @property {String} text 角标内容
15
+	 * @property {String} size = [normal|small] 角标内容
16
+	 * @property {String} type = [info|primary|success|warning|error] 颜色类型
17
+	 * 	@value info 灰色
18
+	 * 	@value primary 蓝色
19
+	 * 	@value success 绿色
20
+	 * 	@value warning 黄色
21
+	 * 	@value error 红色
22
+	 * @property {String} inverted = [true|false] 是否无需背景颜色
23
+	 * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+
24
+	 * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上		
25
+	 * 	@value rightTop 右上
26
+	 * 	@value rightBottom 右下
27
+	 * 	@value leftTop 左上
28
+	 * 	@value leftBottom 左下
29
+	 * @property {Array[number]} offset	距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px
30
+	 * @property {String} isDot = [true|false] 是否显示为一个小点
31
+	 * @event {Function} click 点击 Badge 触发事件
32
+	 * @example <uni-badge text="1"></uni-badge>
33
+	 */
34
+
35
+	export default {
36
+		name: 'UniBadge',
37
+		emits: ['click'],
38
+		props: {
39
+			type: {
40
+				type: String,
41
+				default: 'error'
42
+			},
43
+			inverted: {
44
+				type: Boolean,
45
+				default: false
46
+			},
47
+			isDot: {
48
+				type: Boolean,
49
+				default: false
50
+			},
51
+			maxNum: {
52
+				type: Number,
53
+				default: 99
54
+			},
55
+			absolute: {
56
+				type: String,
57
+				default: ''
58
+			},
59
+			offset: {
60
+				type: Array,
61
+				default () {
62
+					return [0, 0]
63
+				}
64
+			},
65
+			text: {
66
+				type: [String, Number],
67
+				default: ''
68
+			},
69
+			size: {
70
+				type: String,
71
+				default: 'small'
72
+			},
73
+			customStyle: {
74
+				type: Object,
75
+				default () {
76
+					return {}
77
+				}
78
+			}
79
+		},
80
+		data() {
81
+			return {};
82
+		},
83
+		computed: {
84
+			width() {
85
+				return String(this.text).length * 8 + 12
86
+			},
87
+			classNames() {
88
+				const {
89
+					inverted,
90
+					type,
91
+					size,
92
+					absolute
93
+				} = this
94
+				return [
95
+					inverted ? 'uni-badge--' + type + '-inverted' : '',
96
+					'uni-badge--' + type,
97
+					'uni-badge--' + size,
98
+					absolute ? 'uni-badge--absolute' : ''
99
+				].join(' ')
100
+			},
101
+			positionStyle() {
102
+				if (!this.absolute) return {}
103
+				let w = this.width / 2,
104
+					h = 10
105
+				if (this.isDot) {
106
+					w = 5
107
+					h = 5
108
+				}
109
+				const x = `${- w  + this.offset[0]}px`
110
+				const y = `${- h + this.offset[1]}px`
111
+
112
+				const whiteList = {
113
+					rightTop: {
114
+						right: x,
115
+						top: y
116
+					},
117
+					rightBottom: {
118
+						right: x,
119
+						bottom: y
120
+					},
121
+					leftBottom: {
122
+						left: x,
123
+						bottom: y
124
+					},
125
+					leftTop: {
126
+						left: x,
127
+						top: y
128
+					}
129
+				}
130
+				const match = whiteList[this.absolute]
131
+				return match ? match : whiteList['rightTop']
132
+			},
133
+			badgeWidth() {
134
+				return {
135
+					width: `${this.width}px`
136
+				}
137
+			},
138
+			dotStyle() {
139
+				if (!this.isDot) return {}
140
+				return {
141
+					width: '10px',
142
+					height: '10px',
143
+					borderRadius: '10px'
144
+				}
145
+			},
146
+			displayValue() {
147
+				const {
148
+					isDot,
149
+					text,
150
+					maxNum
151
+				} = this
152
+				return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
153
+			}
154
+		},
155
+		methods: {
156
+			onClick() {
157
+				this.$emit('click');
158
+			}
159
+		}
160
+	};
161
+</script>
162
+
163
+<style lang="scss" >
164
+	$uni-primary: #2979ff !default;
165
+	$uni-success: #4cd964 !default;
166
+	$uni-warning: #f0ad4e !default;
167
+	$uni-error: #dd524d !default;
168
+	$uni-info: #909399 !default;
169
+
170
+
171
+	$bage-size: 12px;
172
+	$bage-small: scale(0.8);
173
+
174
+	.uni-badge--x {
175
+		/* #ifdef APP-NVUE */
176
+		// align-self: flex-start;
177
+		/* #endif */
178
+		/* #ifndef APP-NVUE */
179
+		display: inline-block;
180
+		/* #endif */
181
+		position: relative;
182
+	}
183
+
184
+	.uni-badge--absolute {
185
+		position: absolute;
186
+	}
187
+
188
+	.uni-badge--small {
189
+		transform: $bage-small;
190
+		transform-origin: center center;
191
+	}
192
+
193
+	.uni-badge {
194
+		/* #ifndef APP-NVUE */
195
+		display: flex;
196
+		overflow: hidden;
197
+		box-sizing: border-box;
198
+		/* #endif */
199
+		justify-content: center;
200
+		flex-direction: row;
201
+		height: 20px;
202
+		line-height: 18px;
203
+		color: #fff;
204
+		border-radius: 100px;
205
+		background-color: $uni-info;
206
+		background-color: transparent;
207
+		border: 1px solid #fff;
208
+		text-align: center;
209
+		font-family: 'Helvetica Neue', Helvetica, sans-serif;
210
+		font-size: $bage-size;
211
+		/* #ifdef H5 */
212
+		z-index: 999;
213
+		cursor: pointer;
214
+		/* #endif */
215
+
216
+		&--info {
217
+			color: #fff;
218
+			background-color: $uni-info;
219
+		}
220
+
221
+		&--primary {
222
+			background-color: $uni-primary;
223
+		}
224
+
225
+		&--success {
226
+			background-color: $uni-success;
227
+		}
228
+
229
+		&--warning {
230
+			background-color: $uni-warning;
231
+		}
232
+
233
+		&--error {
234
+			background-color: $uni-error;
235
+		}
236
+
237
+		&--inverted {
238
+			padding: 0 5px 0 0;
239
+			color: $uni-info;
240
+		}
241
+
242
+		&--info-inverted {
243
+			color: $uni-info;
244
+			background-color: transparent;
245
+		}
246
+
247
+		&--primary-inverted {
248
+			color: $uni-primary;
249
+			background-color: transparent;
250
+		}
251
+
252
+		&--success-inverted {
253
+			color: $uni-success;
254
+			background-color: transparent;
255
+		}
256
+
257
+		&--warning-inverted {
258
+			color: $uni-warning;
259
+			background-color: transparent;
260
+		}
261
+
262
+		&--error-inverted {
263
+			color: $uni-error;
264
+			background-color: transparent;
265
+		}
266
+
267
+	}
268
+</style>

+ 88 - 0
uni_modules/uni-badge/package.json

@@ -0,0 +1,88 @@
1
+{
2
+  "id": "uni-badge",
3
+  "displayName": "uni-badge 数字角标",
4
+  "version": "1.2.0",
5
+  "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
6
+  "keywords": [
7
+    "",
8
+    "badge",
9
+    "uni-ui",
10
+    "uniui",
11
+    "数字角标",
12
+    "徽章"
13
+],
14
+  "repository": "https://github.com/dcloudio/uni-ui",
15
+  "engines": {
16
+    "HBuilderX": ""
17
+  },
18
+  "directories": {
19
+    "example": "../../temps/example_temps"
20
+  },
21
+  "dcloudext": {
22
+    "category": [
23
+      "前端组件",
24
+      "通用组件"
25
+    ],
26
+    "sale": {
27
+      "regular": {
28
+        "price": "0.00"
29
+      },
30
+      "sourcecode": {
31
+        "price": "0.00"
32
+      }
33
+    },
34
+    "contact": {
35
+      "qq": ""
36
+    },
37
+    "declaration": {
38
+      "ads": "无",
39
+      "data": "无",
40
+      "permissions": "无"
41
+    },
42
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
43
+  },
44
+  "uni_modules": {
45
+    "dependencies": ["uni-scss"],
46
+    "encrypt": [],
47
+    "platforms": {
48
+      "cloud": {
49
+        "tcb": "y",
50
+        "aliyun": "y"
51
+      },
52
+      "client": {
53
+        "App": {
54
+          "app-vue": "y",
55
+          "app-nvue": "y"
56
+        },
57
+        "H5-mobile": {
58
+          "Safari": "y",
59
+          "Android Browser": "y",
60
+          "微信浏览器(Android)": "y",
61
+          "QQ浏览器(Android)": "y"
62
+        },
63
+        "H5-pc": {
64
+          "Chrome": "y",
65
+          "IE": "y",
66
+          "Edge": "y",
67
+          "Firefox": "y",
68
+          "Safari": "y"
69
+        },
70
+        "小程序": {
71
+          "微信": "y",
72
+          "阿里": "y",
73
+          "百度": "y",
74
+          "字节跳动": "y",
75
+          "QQ": "y"
76
+        },
77
+        "快应用": {
78
+          "华为": "y",
79
+          "联盟": "y"
80
+        },
81
+        "Vue": {
82
+            "vue2": "y",
83
+            "vue3": "y"
84
+        }
85
+      }
86
+    }
87
+  }
88
+}

+ 10 - 0
uni_modules/uni-badge/readme.md

@@ -0,0 +1,10 @@
1
+## Badge 数字角标
2
+> **组件名:uni-badge**
3
+> 代码块: `uBadge`
4
+
5
+数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景,
6
+
7
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
8
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
9
+
10
+

+ 10 - 0
uni_modules/uni-calendar/changelog.md

@@ -0,0 +1,10 @@
1
+## 1.4.2(2021-08-24)
2
+- 新增 支持国际化
3
+## 1.4.1(2021-08-05)
4
+- 修复 弹出层被 tabbar 遮盖 bug
5
+## 1.4.0(2021-07-30)
6
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
7
+## 1.3.16(2021-05-12)
8
+- 新增 组件示例地址
9
+## 1.3.15(2021-02-04)
10
+- 调整为uni_modules目录规范 

+ 546 - 0
uni_modules/uni-calendar/components/uni-calendar/calendar.js

@@ -0,0 +1,546 @@
1
+/**
2
+* @1900-2100区间内的公历、农历互转
3
+* @charset UTF-8
4
+* @github  https://github.com/jjonline/calendar.js
5
+* @Author  Jea杨(JJonline@JJonline.Cn)
6
+* @Time    2014-7-21
7
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
8
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
9
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
10
+* @Version 1.0.3
11
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
12
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
13
+*/
14
+/* eslint-disable */
15
+var calendar = {
16
+
17
+  /**
18
+      * 农历1900-2100的润大小信息表
19
+      * @Array Of Property
20
+      * @return Hex
21
+      */
22
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
23
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
24
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
25
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
26
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
27
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
28
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
29
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
30
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
31
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
32
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
33
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
34
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
35
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
36
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
37
+    /** Add By JJonline@JJonline.Cn**/
38
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
39
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
40
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
41
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
42
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
43
+    0x0d520], // 2100
44
+
45
+  /**
46
+      * 公历每个月份的天数普通表
47
+      * @Array Of Property
48
+      * @return Number
49
+      */
50
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
51
+
52
+  /**
53
+      * 天干地支之天干速查表
54
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
55
+      * @return Cn string
56
+      */
57
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
58
+
59
+  /**
60
+      * 天干地支之地支速查表
61
+      * @Array Of Property
62
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
63
+      * @return Cn string
64
+      */
65
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
66
+
67
+  /**
68
+      * 天干地支之地支速查表<=>生肖
69
+      * @Array Of Property
70
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
71
+      * @return Cn string
72
+      */
73
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
74
+
75
+  /**
76
+      * 24节气速查表
77
+      * @Array Of Property
78
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
79
+      * @return Cn string
80
+      */
81
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
82
+
83
+  /**
84
+      * 1900-2100各年的24节气日期速查表
85
+      * @Array Of Property
86
+      * @return 0x string For splice
87
+      */
88
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
89
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
90
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
91
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
92
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
93
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
94
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
95
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
96
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
97
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
98
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
99
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
100
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
101
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
102
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
103
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
104
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
105
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
106
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
107
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
108
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
109
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
110
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
111
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
112
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
113
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
114
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
115
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
116
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
117
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
118
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
119
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
120
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
121
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
122
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
123
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
124
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
125
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
126
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
127
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
128
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
129
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
130
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
131
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
132
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
133
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
134
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
135
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
136
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
137
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
138
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
139
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
140
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
141
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
142
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
143
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
144
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
145
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
146
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
147
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
148
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
149
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
150
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
151
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
152
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
153
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
154
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
155
+
156
+  /**
157
+      * 数字转中文速查表
158
+      * @Array Of Property
159
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
160
+      * @return Cn string
161
+      */
162
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
163
+
164
+  /**
165
+      * 日期转农历称呼速查表
166
+      * @Array Of Property
167
+      * @trans ['初','十','廿','卅']
168
+      * @return Cn string
169
+      */
170
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
171
+
172
+  /**
173
+      * 月份转农历称呼速查表
174
+      * @Array Of Property
175
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
176
+      * @return Cn string
177
+      */
178
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
179
+
180
+  /**
181
+      * 返回农历y年一整年的总天数
182
+      * @param lunar Year
183
+      * @return Number
184
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
185
+      */
186
+  lYearDays: function (y) {
187
+    var i; var sum = 348
188
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
189
+    return (sum + this.leapDays(y))
190
+  },
191
+
192
+  /**
193
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
194
+      * @param lunar Year
195
+      * @return Number (0-12)
196
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
197
+      */
198
+  leapMonth: function (y) { // 闰字编码 \u95f0
199
+    return (this.lunarInfo[y - 1900] & 0xf)
200
+  },
201
+
202
+  /**
203
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
204
+      * @param lunar Year
205
+      * @return Number (0、29、30)
206
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
207
+      */
208
+  leapDays: function (y) {
209
+    if (this.leapMonth(y)) {
210
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
211
+    }
212
+    return (0)
213
+  },
214
+
215
+  /**
216
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
217
+      * @param lunar Year
218
+      * @return Number (-1、29、30)
219
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
220
+      */
221
+  monthDays: function (y, m) {
222
+    if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
223
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
224
+  },
225
+
226
+  /**
227
+      * 返回公历(!)y年m月的天数
228
+      * @param solar Year
229
+      * @return Number (-1、28、29、30、31)
230
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
231
+      */
232
+  solarDays: function (y, m) {
233
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
234
+    var ms = m - 1
235
+    if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
236
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
237
+    } else {
238
+      return (this.solarMonth[ms])
239
+    }
240
+  },
241
+
242
+  /**
243
+     * 农历年份转换为干支纪年
244
+     * @param  lYear 农历年的年份数
245
+     * @return Cn string
246
+     */
247
+  toGanZhiYear: function (lYear) {
248
+    var ganKey = (lYear - 3) % 10
249
+    var zhiKey = (lYear - 3) % 12
250
+    if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
251
+    if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
252
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
253
+  },
254
+
255
+  /**
256
+     * 公历月、日判断所属星座
257
+     * @param  cMonth [description]
258
+     * @param  cDay [description]
259
+     * @return Cn string
260
+     */
261
+  toAstro: function (cMonth, cDay) {
262
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
263
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
264
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
265
+  },
266
+
267
+  /**
268
+      * 传入offset偏移量返回干支
269
+      * @param offset 相对甲子的偏移量
270
+      * @return Cn string
271
+      */
272
+  toGanZhi: function (offset) {
273
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
274
+  },
275
+
276
+  /**
277
+      * 传入公历(!)y年获得该年第n个节气的公历日期
278
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
279
+      * @return day Number
280
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
281
+      */
282
+  getTerm: function (y, n) {
283
+    if (y < 1900 || y > 2100) { return -1 }
284
+    if (n < 1 || n > 24) { return -1 }
285
+    var _table = this.sTermInfo[y - 1900]
286
+    var _info = [
287
+      parseInt('0x' + _table.substr(0, 5)).toString(),
288
+      parseInt('0x' + _table.substr(5, 5)).toString(),
289
+      parseInt('0x' + _table.substr(10, 5)).toString(),
290
+      parseInt('0x' + _table.substr(15, 5)).toString(),
291
+      parseInt('0x' + _table.substr(20, 5)).toString(),
292
+      parseInt('0x' + _table.substr(25, 5)).toString()
293
+    ]
294
+    var _calday = [
295
+      _info[0].substr(0, 1),
296
+      _info[0].substr(1, 2),
297
+      _info[0].substr(3, 1),
298
+      _info[0].substr(4, 2),
299
+
300
+      _info[1].substr(0, 1),
301
+      _info[1].substr(1, 2),
302
+      _info[1].substr(3, 1),
303
+      _info[1].substr(4, 2),
304
+
305
+      _info[2].substr(0, 1),
306
+      _info[2].substr(1, 2),
307
+      _info[2].substr(3, 1),
308
+      _info[2].substr(4, 2),
309
+
310
+      _info[3].substr(0, 1),
311
+      _info[3].substr(1, 2),
312
+      _info[3].substr(3, 1),
313
+      _info[3].substr(4, 2),
314
+
315
+      _info[4].substr(0, 1),
316
+      _info[4].substr(1, 2),
317
+      _info[4].substr(3, 1),
318
+      _info[4].substr(4, 2),
319
+
320
+      _info[5].substr(0, 1),
321
+      _info[5].substr(1, 2),
322
+      _info[5].substr(3, 1),
323
+      _info[5].substr(4, 2)
324
+    ]
325
+    return parseInt(_calday[n - 1])
326
+  },
327
+
328
+  /**
329
+      * 传入农历数字月份返回汉语通俗表示法
330
+      * @param lunar month
331
+      * @return Cn string
332
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
333
+      */
334
+  toChinaMonth: function (m) { // 月 => \u6708
335
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
336
+    var s = this.nStr3[m - 1]
337
+    s += '\u6708'// 加上月字
338
+    return s
339
+  },
340
+
341
+  /**
342
+      * 传入农历日期数字返回汉字表示法
343
+      * @param lunar day
344
+      * @return Cn string
345
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
346
+      */
347
+  toChinaDay: function (d) { // 日 => \u65e5
348
+    var s
349
+    switch (d) {
350
+      case 10:
351
+        s = '\u521d\u5341'; break
352
+      case 20:
353
+        s = '\u4e8c\u5341'; break
354
+        break
355
+      case 30:
356
+        s = '\u4e09\u5341'; break
357
+        break
358
+      default :
359
+        s = this.nStr2[Math.floor(d / 10)]
360
+        s += this.nStr1[d % 10]
361
+    }
362
+    return (s)
363
+  },
364
+
365
+  /**
366
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
367
+      * @param y year
368
+      * @return Cn string
369
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
370
+      */
371
+  getAnimal: function (y) {
372
+    return this.Animals[(y - 4) % 12]
373
+  },
374
+
375
+  /**
376
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
377
+      * @param y  solar year
378
+      * @param m  solar month
379
+      * @param d  solar day
380
+      * @return JSON object
381
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
382
+      */
383
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
384
+    // 年份限定、上限
385
+    if (y < 1900 || y > 2100) {
386
+      return -1// undefined转换为数字变为NaN
387
+    }
388
+    // 公历传参最下限
389
+    if (y == 1900 && m == 1 && d < 31) {
390
+      return -1
391
+    }
392
+    // 未传参  获得当天
393
+    if (!y) {
394
+      var objDate = new Date()
395
+    } else {
396
+      var objDate = new Date(y, parseInt(m) - 1, d)
397
+    }
398
+    var i; var leap = 0; var temp = 0
399
+    // 修正ymd参数
400
+    var y = objDate.getFullYear()
401
+    var m = objDate.getMonth() + 1
402
+    var d = objDate.getDate()
403
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
404
+    for (i = 1900; i < 2101 && offset > 0; i++) {
405
+      temp = this.lYearDays(i)
406
+      offset -= temp
407
+    }
408
+    if (offset < 0) {
409
+      offset += temp; i--
410
+    }
411
+
412
+    // 是否今天
413
+    var isTodayObj = new Date()
414
+    var isToday = false
415
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
416
+      isToday = true
417
+    }
418
+    // 星期几
419
+    var nWeek = objDate.getDay()
420
+    var cWeek = this.nStr1[nWeek]
421
+    // 数字表示周几顺应天朝周一开始的惯例
422
+    if (nWeek == 0) {
423
+      nWeek = 7
424
+    }
425
+    // 农历年
426
+    var year = i
427
+    var leap = this.leapMonth(i) // 闰哪个月
428
+    var isLeap = false
429
+
430
+    // 效验闰月
431
+    for (i = 1; i < 13 && offset > 0; i++) {
432
+      // 闰月
433
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
434
+        --i
435
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
436
+      } else {
437
+        temp = this.monthDays(year, i)// 计算农历普通月天数
438
+      }
439
+      // 解除闰月
440
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
441
+      offset -= temp
442
+    }
443
+    // 闰月导致数组下标重叠取反
444
+    if (offset == 0 && leap > 0 && i == leap + 1) {
445
+      if (isLeap) {
446
+        isLeap = false
447
+      } else {
448
+        isLeap = true; --i
449
+      }
450
+    }
451
+    if (offset < 0) {
452
+      offset += temp; --i
453
+    }
454
+    // 农历月
455
+    var month = i
456
+    // 农历日
457
+    var day = offset + 1
458
+    // 天干地支处理
459
+    var sm = m - 1
460
+    var gzY = this.toGanZhiYear(year)
461
+
462
+    // 当月的两个节气
463
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
464
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
465
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
466
+
467
+    // 依据12节气修正干支月
468
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
469
+    if (d >= firstNode) {
470
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
471
+    }
472
+
473
+    // 传入的日期的节气与否
474
+    var isTerm = false
475
+    var Term = null
476
+    if (firstNode == d) {
477
+      isTerm = true
478
+      Term = this.solarTerm[m * 2 - 2]
479
+    }
480
+    if (secondNode == d) {
481
+      isTerm = true
482
+      Term = this.solarTerm[m * 2 - 1]
483
+    }
484
+    // 日柱 当月一日与 1900/1/1 相差天数
485
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
486
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
487
+    // 该日期所属的星座
488
+    var astro = this.toAstro(m, d)
489
+
490
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
491
+  },
492
+
493
+  /**
494
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
495
+      * @param y  lunar year
496
+      * @param m  lunar month
497
+      * @param d  lunar day
498
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
499
+      * @return JSON object
500
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
501
+      */
502
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
503
+    var isLeapMonth = !!isLeapMonth
504
+    var leapOffset = 0
505
+    var leapMonth = this.leapMonth(y)
506
+    var leapDay = this.leapDays(y)
507
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
508
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
509
+    var day = this.monthDays(y, m)
510
+    var _day = day
511
+    // bugFix 2016-9-25
512
+    // if month is leap, _day use leapDays method
513
+    if (isLeapMonth) {
514
+      _day = this.leapDays(y, m)
515
+    }
516
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
517
+
518
+    // 计算农历的时间差
519
+    var offset = 0
520
+    for (var i = 1900; i < y; i++) {
521
+      offset += this.lYearDays(i)
522
+    }
523
+    var leap = 0; var isAdd = false
524
+    for (var i = 1; i < m; i++) {
525
+      leap = this.leapMonth(y)
526
+      if (!isAdd) { // 处理闰月
527
+        if (leap <= i && leap > 0) {
528
+          offset += this.leapDays(y); isAdd = true
529
+        }
530
+      }
531
+      offset += this.monthDays(y, i)
532
+    }
533
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
534
+    if (isLeapMonth) { offset += day }
535
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
536
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
537
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
538
+    var cY = calObj.getUTCFullYear()
539
+    var cM = calObj.getUTCMonth() + 1
540
+    var cD = calObj.getUTCDate()
541
+
542
+    return this.solar2lunar(cY, cM, cD)
543
+  }
544
+}
545
+
546
+export default calendar

+ 12 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/en.json

@@ -0,0 +1,12 @@
1
+{
2
+	"uni-calender.ok": "ok",
3
+	"uni-calender.cancel": "cancel",
4
+	"uni-calender.today": "today",
5
+	"uni-calender.MON": "MON",
6
+	"uni-calender.TUE": "TUE",
7
+	"uni-calender.WED": "WED",
8
+	"uni-calender.THU": "THU",
9
+	"uni-calender.FRI": "FRI",
10
+	"uni-calender.SAT": "SAT",
11
+	"uni-calender.SUN": "SUN"
12
+}

+ 8 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/index.js

@@ -0,0 +1,8 @@
1
+import en from './en.json'
2
+import zhHans from './zh-Hans.json'
3
+import zhHant from './zh-Hant.json'
4
+export default {
5
+	en,
6
+	'zh-Hans': zhHans,
7
+	'zh-Hant': zhHant
8
+}

+ 12 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json

@@ -0,0 +1,12 @@
1
+{
2
+	"uni-calender.ok": "确定",
3
+	"uni-calender.cancel": "取消",
4
+	"uni-calender.today": "今日",
5
+	"uni-calender.SUN": "日",
6
+	"uni-calender.MON": "一",
7
+	"uni-calender.TUE": "二",
8
+	"uni-calender.WED": "三",
9
+	"uni-calender.THU": "四",
10
+	"uni-calender.FRI": "五",
11
+	"uni-calender.SAT": "六"
12
+}

+ 12 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json

@@ -0,0 +1,12 @@
1
+{
2
+	"uni-calender.ok": "確定",
3
+	"uni-calender.cancel": "取消",
4
+	"uni-calender.today": "今日",
5
+	"uni-calender.SUN": "日",
6
+	"uni-calender.MON": "一",
7
+	"uni-calender.TUE": "二",
8
+	"uni-calender.WED": "三",
9
+	"uni-calender.THU": "四",
10
+	"uni-calender.FRI": "五",
11
+	"uni-calender.SAT": "六"
12
+}

+ 188 - 0
uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue

@@ -0,0 +1,188 @@
1
+<template>
2
+	<view class="uni-calendar-item__weeks-box" :class="{
3
+		'uni-calendar-item--disable':weeks.disable,
4
+		'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
5
+		'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
6
+		'uni-calendar-item--before-checked':weeks.beforeMultiple,
7
+		'uni-calendar-item--multiple': weeks.multiple,
8
+		'uni-calendar-item--after-checked':weeks.afterMultiple,
9
+		}"
10
+	 @click="choiceDate(weeks)">
11
+		<view class="uni-calendar-item__weeks-box-item">
12
+			<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
13
+			<text class="uni-calendar-item__weeks-box-text" :class="{
14
+				'uni-calendar-item--isDay-text': weeks.isDay,
15
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
16
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
17
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
18
+				'uni-calendar-item--multiple': weeks.multiple,
19
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
20
+				'uni-calendar-item--disable':weeks.disable,
21
+				}">{{weeks.date}}</text>
22
+			<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
23
+				'uni-calendar-item--isDay-text':weeks.isDay,
24
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
25
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
26
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
27
+				'uni-calendar-item--multiple': weeks.multiple,
28
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
29
+				}">{{todayText}}</text>
30
+			<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
31
+				'uni-calendar-item--isDay-text':weeks.isDay,
32
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
33
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
34
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
35
+				'uni-calendar-item--multiple': weeks.multiple,
36
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
37
+				'uni-calendar-item--disable':weeks.disable,
38
+				}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
39
+			<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
40
+				'uni-calendar-item--extra':weeks.extraInfo.info,
41
+				'uni-calendar-item--isDay-text':weeks.isDay,
42
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
43
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
44
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
45
+				'uni-calendar-item--multiple': weeks.multiple,
46
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
47
+				'uni-calendar-item--disable':weeks.disable,
48
+				}">{{weeks.extraInfo.info}}</text>
49
+		</view>
50
+	</view>
51
+</template>
52
+
53
+<script>
54
+	import {
55
+	initVueI18n
56
+	} from '@dcloudio/uni-i18n'
57
+	import messages from './i18n/index.js'
58
+	const {	t	} = initVueI18n(messages)
59
+	export default {
60
+		emits:['change'],
61
+		props: {
62
+			weeks: {
63
+				type: Object,
64
+				default () {
65
+					return {}
66
+				}
67
+			},
68
+			calendar: {
69
+				type: Object,
70
+				default: () => {
71
+					return {}
72
+				}
73
+			},
74
+			selected: {
75
+				type: Array,
76
+				default: () => {
77
+					return []
78
+				}
79
+			},
80
+			lunar: {
81
+				type: Boolean,
82
+				default: false
83
+			}
84
+		},
85
+		computed: {
86
+			todayText() {
87
+				return t("uni-calender.today")
88
+			},
89
+		},
90
+		methods: {
91
+			choiceDate(weeks) {
92
+				this.$emit('change', weeks)
93
+			}
94
+		}
95
+	}
96
+</script>
97
+
98
+<style lang="scss" >
99
+	$uni-font-size-base:14px;
100
+	$uni-text-color:#333;
101
+	$uni-font-size-sm:12px;
102
+	$uni-color-error: #e43d33;
103
+	$uni-opacity-disabled: 0.3;
104
+	$uni-text-color-disable:#c0c0c0;
105
+	$uni-color-primary: #2979ff;
106
+	.uni-calendar-item__weeks-box {
107
+		flex: 1;
108
+		/* #ifndef APP-NVUE */
109
+		display: flex;
110
+		/* #endif */
111
+		flex-direction: column;
112
+		justify-content: center;
113
+		align-items: center;
114
+	}
115
+
116
+	.uni-calendar-item__weeks-box-text {
117
+		font-size: $uni-font-size-base;
118
+		color: $uni-text-color;
119
+	}
120
+
121
+	.uni-calendar-item__weeks-lunar-text {
122
+		font-size: $uni-font-size-sm;
123
+		color: $uni-text-color;
124
+	}
125
+
126
+	.uni-calendar-item__weeks-box-item {
127
+		position: relative;
128
+		/* #ifndef APP-NVUE */
129
+		display: flex;
130
+		/* #endif */
131
+		flex-direction: column;
132
+		justify-content: center;
133
+		align-items: center;
134
+		width: 100rpx;
135
+		height: 100rpx;
136
+	}
137
+
138
+	.uni-calendar-item__weeks-box-circle {
139
+		position: absolute;
140
+		top: 5px;
141
+		right: 5px;
142
+		width: 8px;
143
+		height: 8px;
144
+		border-radius: 8px;
145
+		background-color: $uni-color-error;
146
+
147
+	}
148
+
149
+	.uni-calendar-item--disable {
150
+		background-color: rgba(249, 249, 249, $uni-opacity-disabled);
151
+		color: $uni-text-color-disable;
152
+	}
153
+
154
+	.uni-calendar-item--isDay-text {
155
+		color: $uni-color-primary;
156
+	}
157
+
158
+	.uni-calendar-item--isDay {
159
+		background-color: $uni-color-primary;
160
+		opacity: 0.8;
161
+		color: #fff;
162
+	}
163
+
164
+	.uni-calendar-item--extra {
165
+		color: $uni-color-error;
166
+		opacity: 0.8;
167
+	}
168
+
169
+	.uni-calendar-item--checked {
170
+		background-color: $uni-color-primary;
171
+		color: #fff;
172
+		opacity: 0.8;
173
+	}
174
+
175
+	.uni-calendar-item--multiple {
176
+		background-color: $uni-color-primary;
177
+		color: #fff;
178
+		opacity: 0.8;
179
+	}
180
+	.uni-calendar-item--before-checked {
181
+		background-color: #ff5a5f;
182
+		color: #fff;
183
+	}
184
+	.uni-calendar-item--after-checked {
185
+		background-color: #ff5a5f;
186
+		color: #fff;
187
+	}
188
+</style>

+ 555 - 0
uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue

@@ -0,0 +1,555 @@
1
+<template>
2
+	<view class="uni-calendar">
3
+		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
4
+		<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
5
+			<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
6
+				<view class="uni-calendar__header-btn-box" @click="close">
7
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
8
+				</view>
9
+				<view class="uni-calendar__header-btn-box" @click="confirm">
10
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
11
+				</view>
12
+			</view>
13
+			<view class="uni-calendar__header">
14
+				<view class="uni-calendar__header-btn-box" @click.stop="pre">
15
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
16
+				</view>
17
+				<picker mode="date" :value="date" fields="month" @change="bindDateChange">
18
+					<text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text>
19
+				</picker>
20
+				<view class="uni-calendar__header-btn-box" @click.stop="next">
21
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
22
+				</view>
23
+				<text class="uni-calendar__backtoday" @click="backtoday">{{todayText}}</text>
24
+
25
+			</view>
26
+			<view class="uni-calendar__box">
27
+				<view v-if="showMonth" class="uni-calendar__box-bg">
28
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
29
+				</view>
30
+				<view class="uni-calendar__weeks">
31
+					<view class="uni-calendar__weeks-day">
32
+						<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
33
+					</view>
34
+					<view class="uni-calendar__weeks-day">
35
+						<text class="uni-calendar__weeks-day-text">{{monText}}</text>
36
+					</view>
37
+					<view class="uni-calendar__weeks-day">
38
+						<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
39
+					</view>
40
+					<view class="uni-calendar__weeks-day">
41
+						<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
42
+					</view>
43
+					<view class="uni-calendar__weeks-day">
44
+						<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
45
+					</view>
46
+					<view class="uni-calendar__weeks-day">
47
+						<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
48
+					</view>
49
+					<view class="uni-calendar__weeks-day">
50
+						<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
51
+					</view>
52
+				</view>
53
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
54
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
55
+						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
56
+					</view>
57
+				</view>
58
+			</view>
59
+		</view>
60
+	</view>
61
+</template>
62
+
63
+<script>
64
+	import Calendar from './util.js';
65
+	import calendarItem from './uni-calendar-item.vue'
66
+	import {
67
+	initVueI18n
68
+	} from '@dcloudio/uni-i18n'
69
+	import messages from './i18n/index.js'
70
+	const {	t	} = initVueI18n(messages)
71
+	/**
72
+	 * Calendar 日历
73
+	 * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
74
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
75
+	 * @property {String} date 自定义当前时间,默认为今天
76
+	 * @property {Boolean} lunar 显示农历
77
+	 * @property {String} startDate 日期选择范围-开始日期
78
+	 * @property {String} endDate 日期选择范围-结束日期
79
+	 * @property {Boolean} range 范围选择
80
+	 * @property {Boolean} insert = [true|false] 插入模式,默认为false
81
+	 * 	@value true 弹窗模式
82
+	 * 	@value false 插入模式
83
+	 * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
84
+	 * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
85
+	 * @property {Boolean} showMonth 是否选择月份为背景
86
+	 * @event {Function} change 日期改变,`insert :ture` 时生效
87
+	 * @event {Function} confirm 确认选择`insert :false` 时生效
88
+	 * @event {Function} monthSwitch 切换月份时触发
89
+	 * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
90
+	 */
91
+	export default {
92
+		components: {
93
+			calendarItem
94
+		},
95
+		emits:['close','confirm','change','monthSwitch'],
96
+		props: {
97
+			date: {
98
+				type: String,
99
+				default: ''
100
+			},
101
+			selected: {
102
+				type: Array,
103
+				default () {
104
+					return []
105
+				}
106
+			},
107
+			lunar: {
108
+				type: Boolean,
109
+				default: false
110
+			},
111
+			startDate: {
112
+				type: String,
113
+				default: ''
114
+			},
115
+			endDate: {
116
+				type: String,
117
+				default: ''
118
+			},
119
+			range: {
120
+				type: Boolean,
121
+				default: false
122
+			},
123
+			insert: {
124
+				type: Boolean,
125
+				default: true
126
+			},
127
+			showMonth: {
128
+				type: Boolean,
129
+				default: true
130
+			},
131
+			clearDate: {
132
+				type: Boolean,
133
+				default: true
134
+			}
135
+		},
136
+		data() {
137
+			return {
138
+				show: false,
139
+				weeks: [],
140
+				calendar: {},
141
+				nowDate: '',
142
+				aniMaskShow: false
143
+			}
144
+		},
145
+		computed:{
146
+			/**
147
+			 * for i18n
148
+			 */
149
+
150
+			okText() {
151
+				return t("uni-calender.ok")
152
+			},
153
+			cancelText() {
154
+				return t("uni-calender.cancel")
155
+			},
156
+			todayText() {
157
+				return t("uni-calender.today")
158
+			},
159
+			monText() {
160
+				return t("uni-calender.MON")
161
+			},
162
+			TUEText() {
163
+				return t("uni-calender.TUE")
164
+			},
165
+			WEDText() {
166
+				return t("uni-calender.WED")
167
+			},
168
+			THUText() {
169
+				return t("uni-calender.THU")
170
+			},
171
+			FRIText() {
172
+				return t("uni-calender.FRI")
173
+			},
174
+			SATText() {
175
+				return t("uni-calender.SAT")
176
+			},
177
+			SUNText() {
178
+				return t("uni-calender.SUN")
179
+			},
180
+		},
181
+		watch: {
182
+			date(newVal) {
183
+				// this.cale.setDate(newVal)
184
+				this.init(newVal)
185
+			},
186
+			startDate(val){
187
+				this.cale.resetSatrtDate(val)
188
+			},
189
+			endDate(val){
190
+				this.cale.resetEndDate(val)
191
+			},
192
+			selected(newVal) {
193
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
194
+				this.weeks = this.cale.weeks
195
+			}
196
+		},
197
+		created() {
198
+			// 获取日历方法实例
199
+			this.cale = new Calendar({
200
+				// date: new Date(),
201
+				selected: this.selected,
202
+				startDate: this.startDate,
203
+				endDate: this.endDate,
204
+				range: this.range,
205
+			})
206
+			// 选中某一天
207
+			// this.cale.setDate(this.date)
208
+			this.init(this.date)
209
+			// this.setDay
210
+		},
211
+		methods: {
212
+			// 取消穿透
213
+			clean() {},
214
+			bindDateChange(e) {
215
+				const value = e.detail.value + '-1'
216
+				console.log(this.cale.getDate(value));
217
+				this.init(value)
218
+			},
219
+			/**
220
+			 * 初始化日期显示
221
+			 * @param {Object} date
222
+			 */
223
+			init(date) {
224
+				this.cale.setDate(date)
225
+				this.weeks = this.cale.weeks
226
+				this.nowDate = this.calendar = this.cale.getInfo(date)
227
+			},
228
+			/**
229
+			 * 打开日历弹窗
230
+			 */
231
+			open() {
232
+				// 弹窗模式并且清理数据
233
+				if (this.clearDate && !this.insert) {
234
+					this.cale.cleanMultipleStatus()
235
+					// this.cale.setDate(this.date)
236
+					this.init(this.date)
237
+				}
238
+				this.show = true
239
+				this.$nextTick(() => {
240
+					setTimeout(() => {
241
+						this.aniMaskShow = true
242
+					}, 50)
243
+				})
244
+			},
245
+			/**
246
+			 * 关闭日历弹窗
247
+			 */
248
+			close() {
249
+				this.aniMaskShow = false
250
+				this.$nextTick(() => {
251
+					setTimeout(() => {
252
+						this.show = false
253
+						this.$emit('close')
254
+					}, 300)
255
+				})
256
+			},
257
+			/**
258
+			 * 确认按钮
259
+			 */
260
+			confirm() {
261
+				this.setEmit('confirm')
262
+				this.close()
263
+			},
264
+			/**
265
+			 * 变化触发
266
+			 */
267
+			change() {
268
+				if (!this.insert) return
269
+				this.setEmit('change')
270
+			},
271
+			/**
272
+			 * 选择月份触发
273
+			 */
274
+			monthSwitch() {
275
+				let {
276
+					year,
277
+					month
278
+				} = this.nowDate
279
+				this.$emit('monthSwitch', {
280
+					year,
281
+					month: Number(month)
282
+				})
283
+			},
284
+			/**
285
+			 * 派发事件
286
+			 * @param {Object} name
287
+			 */
288
+			setEmit(name) {
289
+				let {
290
+					year,
291
+					month,
292
+					date,
293
+					fullDate,
294
+					lunar,
295
+					extraInfo
296
+				} = this.calendar
297
+				this.$emit(name, {
298
+					range: this.cale.multipleStatus,
299
+					year,
300
+					month,
301
+					date,
302
+					fulldate: fullDate,
303
+					lunar,
304
+					extraInfo: extraInfo || {}
305
+				})
306
+			},
307
+			/**
308
+			 * 选择天触发
309
+			 * @param {Object} weeks
310
+			 */
311
+			choiceDate(weeks) {
312
+				if (weeks.disable) return
313
+				this.calendar = weeks
314
+				// 设置多选
315
+				this.cale.setMultiple(this.calendar.fullDate)
316
+				this.weeks = this.cale.weeks
317
+				this.change()
318
+			},
319
+			/**
320
+			 * 回到今天
321
+			 */
322
+			backtoday() {
323
+				console.log(this.cale.getDate(new Date()).fullDate);
324
+				let date = this.cale.getDate(new Date()).fullDate
325
+				// this.cale.setDate(date)
326
+				this.init(date)
327
+				this.change()
328
+			},
329
+			/**
330
+			 * 上个月
331
+			 */
332
+			pre() {
333
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
334
+				this.setDate(preDate)
335
+				this.monthSwitch()
336
+
337
+			},
338
+			/**
339
+			 * 下个月
340
+			 */
341
+			next() {
342
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
343
+				this.setDate(nextDate)
344
+				this.monthSwitch()
345
+			},
346
+			/**
347
+			 * 设置日期
348
+			 * @param {Object} date
349
+			 */
350
+			setDate(date) {
351
+				this.cale.setDate(date)
352
+				this.weeks = this.cale.weeks
353
+				this.nowDate = this.cale.getInfo(date)
354
+			}
355
+		}
356
+	}
357
+</script>
358
+
359
+<style lang="scss" >
360
+	$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
361
+	$uni-border-color: #EDEDED;
362
+	$uni-text-color: #333;
363
+	$uni-bg-color-hover:#f1f1f1;
364
+	$uni-font-size-base:14px;
365
+	$uni-text-color-placeholder: #808080;
366
+	$uni-color-subtitle: #555555;
367
+	$uni-text-color-grey:#999;
368
+	.uni-calendar {
369
+		/* #ifndef APP-NVUE */
370
+		display: flex;
371
+		/* #endif */
372
+		flex-direction: column;
373
+	}
374
+
375
+	.uni-calendar__mask {
376
+		position: fixed;
377
+		bottom: 0;
378
+		top: 0;
379
+		left: 0;
380
+		right: 0;
381
+		background-color: $uni-bg-color-mask;
382
+		transition-property: opacity;
383
+		transition-duration: 0.3s;
384
+		opacity: 0;
385
+		/* #ifndef APP-NVUE */
386
+		z-index: 99;
387
+		/* #endif */
388
+	}
389
+
390
+	.uni-calendar--mask-show {
391
+		opacity: 1
392
+	}
393
+
394
+	.uni-calendar--fixed {
395
+		position: fixed;
396
+		bottom: calc(var(--window-bottom));
397
+		left: 0;
398
+		right: 0;
399
+		transition-property: transform;
400
+		transition-duration: 0.3s;
401
+		transform: translateY(460px);
402
+		/* #ifndef APP-NVUE */
403
+		z-index: 99;
404
+		/* #endif */
405
+	}
406
+
407
+	.uni-calendar--ani-show {
408
+		transform: translateY(0);
409
+	}
410
+
411
+	.uni-calendar__content {
412
+		background-color: #fff;
413
+	}
414
+
415
+	.uni-calendar__header {
416
+		position: relative;
417
+		/* #ifndef APP-NVUE */
418
+		display: flex;
419
+		/* #endif */
420
+		flex-direction: row;
421
+		justify-content: center;
422
+		align-items: center;
423
+		height: 50px;
424
+		border-bottom-color: $uni-border-color;
425
+		border-bottom-style: solid;
426
+		border-bottom-width: 1px;
427
+	}
428
+
429
+	.uni-calendar--fixed-top {
430
+		/* #ifndef APP-NVUE */
431
+		display: flex;
432
+		/* #endif */
433
+		flex-direction: row;
434
+		justify-content: space-between;
435
+		border-top-color: $uni-border-color;
436
+		border-top-style: solid;
437
+		border-top-width: 1px;
438
+	}
439
+
440
+	.uni-calendar--fixed-width {
441
+		width: 50px;
442
+		// padding: 0 15px;
443
+	}
444
+
445
+	.uni-calendar__backtoday {
446
+		position: absolute;
447
+		right: 0;
448
+		top: 25rpx;
449
+		padding: 0 5px;
450
+		padding-left: 10px;
451
+		height: 25px;
452
+		line-height: 25px;
453
+		font-size: 12px;
454
+		border-top-left-radius: 25px;
455
+		border-bottom-left-radius: 25px;
456
+		color: $uni-text-color;
457
+		background-color: $uni-bg-color-hover;
458
+	}
459
+
460
+	.uni-calendar__header-text {
461
+		text-align: center;
462
+		width: 100px;
463
+		font-size: $uni-font-size-base;
464
+		color: $uni-text-color;
465
+	}
466
+
467
+	.uni-calendar__header-btn-box {
468
+		/* #ifndef APP-NVUE */
469
+		display: flex;
470
+		/* #endif */
471
+		flex-direction: row;
472
+		align-items: center;
473
+		justify-content: center;
474
+		width: 50px;
475
+		height: 50px;
476
+	}
477
+
478
+	.uni-calendar__header-btn {
479
+		width: 10px;
480
+		height: 10px;
481
+		border-left-color: $uni-text-color-placeholder;
482
+		border-left-style: solid;
483
+		border-left-width: 2px;
484
+		border-top-color: $uni-color-subtitle;
485
+		border-top-style: solid;
486
+		border-top-width: 2px;
487
+	}
488
+
489
+	.uni-calendar--left {
490
+		transform: rotate(-45deg);
491
+	}
492
+
493
+	.uni-calendar--right {
494
+		transform: rotate(135deg);
495
+	}
496
+
497
+
498
+	.uni-calendar__weeks {
499
+		position: relative;
500
+		/* #ifndef APP-NVUE */
501
+		display: flex;
502
+		/* #endif */
503
+		flex-direction: row;
504
+	}
505
+
506
+	.uni-calendar__weeks-item {
507
+		flex: 1;
508
+	}
509
+
510
+	.uni-calendar__weeks-day {
511
+		flex: 1;
512
+		/* #ifndef APP-NVUE */
513
+		display: flex;
514
+		/* #endif */
515
+		flex-direction: column;
516
+		justify-content: center;
517
+		align-items: center;
518
+		height: 45px;
519
+		border-bottom-color: #F5F5F5;
520
+		border-bottom-style: solid;
521
+		border-bottom-width: 1px;
522
+	}
523
+
524
+	.uni-calendar__weeks-day-text {
525
+		font-size: 14px;
526
+	}
527
+
528
+	.uni-calendar__box {
529
+		position: relative;
530
+	}
531
+
532
+	.uni-calendar__box-bg {
533
+		/* #ifndef APP-NVUE */
534
+		display: flex;
535
+		/* #endif */
536
+		justify-content: center;
537
+		align-items: center;
538
+		position: absolute;
539
+		top: 0;
540
+		left: 0;
541
+		right: 0;
542
+		bottom: 0;
543
+	}
544
+
545
+	.uni-calendar__box-bg-text {
546
+		font-size: 200px;
547
+		font-weight: bold;
548
+		color: $uni-text-color-grey;
549
+		opacity: 0.1;
550
+		text-align: center;
551
+		/* #ifndef APP-NVUE */
552
+		line-height: 1;
553
+		/* #endif */
554
+	}
555
+</style>

+ 354 - 0
uni_modules/uni-calendar/components/uni-calendar/util.js

@@ -0,0 +1,354 @@
1
+import CALENDAR from './calendar.js'
2
+
3
+class Calendar {
4
+	constructor({
5
+		date,
6
+		selected,
7
+		startDate,
8
+		endDate,
9
+		range
10
+	} = {}) {
11
+		// 当前日期
12
+		this.date = this.getDate(new Date()) // 当前初入日期
13
+		// 打点信息
14
+		this.selected = selected || [];
15
+		// 范围开始
16
+		this.startDate = startDate
17
+		// 范围结束
18
+		this.endDate = endDate
19
+		this.range = range
20
+		// 多选状态
21
+		this.cleanMultipleStatus()
22
+		// 每周日期
23
+		this.weeks = {}
24
+		// this._getWeek(this.date.fullDate)
25
+	}
26
+	/**
27
+	 * 设置日期
28
+	 * @param {Object} date
29
+	 */
30
+	setDate(date) {
31
+		this.selectDate = this.getDate(date)
32
+		this._getWeek(this.selectDate.fullDate)
33
+	}
34
+
35
+	/**
36
+	 * 清理多选状态
37
+	 */
38
+	cleanMultipleStatus() {
39
+		this.multipleStatus = {
40
+			before: '',
41
+			after: '',
42
+			data: []
43
+		}
44
+	}
45
+
46
+	/**
47
+	 * 重置开始日期
48
+	 */
49
+	resetSatrtDate(startDate) {
50
+		// 范围开始
51
+		this.startDate = startDate
52
+
53
+	}
54
+
55
+	/**
56
+	 * 重置结束日期
57
+	 */
58
+	resetEndDate(endDate) {
59
+		// 范围结束
60
+		this.endDate = endDate
61
+	}
62
+
63
+	/**
64
+	 * 获取任意时间
65
+	 */
66
+	getDate(date, AddDayCount = 0, str = 'day') {
67
+		if (!date) {
68
+			date = new Date()
69
+		}
70
+		if (typeof date !== 'object') {
71
+			date = date.replace(/-/g, '/')
72
+		}
73
+		const dd = new Date(date)
74
+		switch (str) {
75
+			case 'day':
76
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
77
+				break
78
+			case 'month':
79
+				if (dd.getDate() === 31) {
80
+					dd.setDate(dd.getDate() + AddDayCount)
81
+				} else {
82
+					dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
83
+				}
84
+				break
85
+			case 'year':
86
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
87
+				break
88
+		}
89
+		const y = dd.getFullYear()
90
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
91
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
92
+		return {
93
+			fullDate: y + '-' + m + '-' + d,
94
+			year: y,
95
+			month: m,
96
+			date: d,
97
+			day: dd.getDay()
98
+		}
99
+	}
100
+
101
+
102
+	/**
103
+	 * 获取上月剩余天数
104
+	 */
105
+	_getLastMonthDays(firstDay, full) {
106
+		let dateArr = []
107
+		for (let i = firstDay; i > 0; i--) {
108
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
109
+			dateArr.push({
110
+				date: beforeDate,
111
+				month: full.month - 1,
112
+				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
113
+				disable: true
114
+			})
115
+		}
116
+		return dateArr
117
+	}
118
+	/**
119
+	 * 获取本月天数
120
+	 */
121
+	_currentMonthDys(dateData, full) {
122
+		let dateArr = []
123
+		let fullDate = this.date.fullDate
124
+		for (let i = 1; i <= dateData; i++) {
125
+			let isinfo = false
126
+			let nowDate = full.year + '-' + (full.month < 10 ?
127
+				full.month : full.month) + '-' + (i < 10 ?
128
+				'0' + i : i)
129
+			// 是否今天
130
+			let isDay = fullDate === nowDate
131
+			// 获取打点信息
132
+			let info = this.selected && this.selected.find((item) => {
133
+				if (this.dateEqual(nowDate, item.date)) {
134
+					return item
135
+				}
136
+			})
137
+
138
+			// 日期禁用
139
+			let disableBefore = true
140
+			let disableAfter = true
141
+			if (this.startDate) {
142
+				// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
143
+				// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
144
+				disableBefore = this.dateCompare(this.startDate, nowDate)
145
+			}
146
+
147
+			if (this.endDate) {
148
+				// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
149
+				// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
150
+				disableAfter = this.dateCompare(nowDate, this.endDate)
151
+			}
152
+			let multiples = this.multipleStatus.data
153
+			let checked = false
154
+			let multiplesStatus = -1
155
+			if (this.range) {
156
+				if (multiples) {
157
+					multiplesStatus = multiples.findIndex((item) => {
158
+						return this.dateEqual(item, nowDate)
159
+					})
160
+				}
161
+				if (multiplesStatus !== -1) {
162
+					checked = true
163
+				}
164
+			}
165
+			let data = {
166
+				fullDate: nowDate,
167
+				year: full.year,
168
+				date: i,
169
+				multiple: this.range ? checked : false,
170
+				beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
171
+				afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
172
+				month: full.month,
173
+				lunar: this.getlunar(full.year, full.month, i),
174
+				disable: !(disableBefore && disableAfter),
175
+				isDay
176
+			}
177
+			if (info) {
178
+				data.extraInfo = info
179
+			}
180
+
181
+			dateArr.push(data)
182
+		}
183
+		return dateArr
184
+	}
185
+	/**
186
+	 * 获取下月天数
187
+	 */
188
+	_getNextMonthDays(surplus, full) {
189
+		let dateArr = []
190
+		for (let i = 1; i < surplus + 1; i++) {
191
+			dateArr.push({
192
+				date: i,
193
+				month: Number(full.month) + 1,
194
+				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
195
+				disable: true
196
+			})
197
+		}
198
+		return dateArr
199
+	}
200
+
201
+	/**
202
+	 * 获取当前日期详情
203
+	 * @param {Object} date
204
+	 */
205
+	getInfo(date) {
206
+		if (!date) {
207
+			date = new Date()
208
+		}
209
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
210
+		return dateInfo
211
+	}
212
+
213
+	/**
214
+	 * 比较时间大小
215
+	 */
216
+	dateCompare(startDate, endDate) {
217
+		// 计算截止时间
218
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
219
+		// 计算详细项的截止时间
220
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
221
+		if (startDate <= endDate) {
222
+			return true
223
+		} else {
224
+			return false
225
+		}
226
+	}
227
+
228
+	/**
229
+	 * 比较时间是否相等
230
+	 */
231
+	dateEqual(before, after) {
232
+		// 计算截止时间
233
+		before = new Date(before.replace('-', '/').replace('-', '/'))
234
+		// 计算详细项的截止时间
235
+		after = new Date(after.replace('-', '/').replace('-', '/'))
236
+		if (before.getTime() - after.getTime() === 0) {
237
+			return true
238
+		} else {
239
+			return false
240
+		}
241
+	}
242
+
243
+
244
+	/**
245
+	 * 获取日期范围内所有日期
246
+	 * @param {Object} begin
247
+	 * @param {Object} end
248
+	 */
249
+	geDateAll(begin, end) {
250
+		var arr = []
251
+		var ab = begin.split('-')
252
+		var ae = end.split('-')
253
+		var db = new Date()
254
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
255
+		var de = new Date()
256
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
257
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
258
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
259
+		for (var k = unixDb; k <= unixDe;) {
260
+			k = k + 24 * 60 * 60 * 1000
261
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
262
+		}
263
+		return arr
264
+	}
265
+	/**
266
+	 * 计算阴历日期显示
267
+	 */
268
+	getlunar(year, month, date) {
269
+		return CALENDAR.solar2lunar(year, month, date)
270
+	}
271
+	/**
272
+	 * 设置打点
273
+	 */
274
+	setSelectInfo(data, value) {
275
+		this.selected = value
276
+		this._getWeek(data)
277
+	}
278
+
279
+	/**
280
+	 *  获取多选状态
281
+	 */
282
+	setMultiple(fullDate) {
283
+		let {
284
+			before,
285
+			after
286
+		} = this.multipleStatus
287
+
288
+		if (!this.range) return
289
+		if (before && after) {
290
+			this.multipleStatus.before = ''
291
+			this.multipleStatus.after = ''
292
+			this.multipleStatus.data = []
293
+		} else {
294
+			if (!before) {
295
+				this.multipleStatus.before = fullDate
296
+			} else {
297
+				this.multipleStatus.after = fullDate
298
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
299
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
300
+				} else {
301
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
302
+				}
303
+			}
304
+		}
305
+		this._getWeek(fullDate)
306
+	}
307
+
308
+	/**
309
+	 * 获取每周数据
310
+	 * @param {Object} dateData
311
+	 */
312
+	_getWeek(dateData) {
313
+		const {
314
+			fullDate,
315
+			year,
316
+			month,
317
+			date,
318
+			day
319
+		} = this.getDate(dateData)
320
+		let firstDay = new Date(year, month - 1, 1).getDay()
321
+		let currentDay = new Date(year, month, 0).getDate()
322
+		let dates = {
323
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
324
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
325
+			nextMonthDays: [], // 下个月开始几天
326
+			weeks: []
327
+		}
328
+		let canlender = []
329
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
330
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
331
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
332
+		let weeks = {}
333
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
334
+		for (let i = 0; i < canlender.length; i++) {
335
+			if (i % 7 === 0) {
336
+				weeks[parseInt(i / 7)] = new Array(7)
337
+			}
338
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
339
+		}
340
+		this.canlender = canlender
341
+		this.weeks = weeks
342
+	}
343
+
344
+	//静态方法
345
+	// static init(date) {
346
+	// 	if (!this.instance) {
347
+	// 		this.instance = new Calendar(date);
348
+	// 	}
349
+	// 	return this.instance;
350
+	// }
351
+}
352
+
353
+
354
+export default Calendar

+ 88 - 0
uni_modules/uni-calendar/package.json

@@ -0,0 +1,88 @@
1
+{
2
+  "id": "uni-calendar",
3
+  "displayName": "uni-calendar 日历",
4
+  "version": "1.4.2",
5
+  "description": "日历组件",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "日历",
10
+    "",
11
+    "打卡",
12
+    "日历选择"
13
+],
14
+  "repository": "https://github.com/dcloudio/uni-ui",
15
+  "engines": {
16
+    "HBuilderX": ""
17
+  },
18
+  "directories": {
19
+    "example": "../../temps/example_temps"
20
+  },
21
+  "dcloudext": {
22
+    "category": [
23
+      "前端组件",
24
+      "通用组件"
25
+    ],
26
+    "sale": {
27
+      "regular": {
28
+        "price": "0.00"
29
+      },
30
+      "sourcecode": {
31
+        "price": "0.00"
32
+      }
33
+    },
34
+    "contact": {
35
+      "qq": ""
36
+    },
37
+    "declaration": {
38
+      "ads": "无",
39
+      "data": "无",
40
+      "permissions": "无"
41
+    },
42
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
43
+  },
44
+  "uni_modules": {
45
+    "dependencies": ["uni-scss"],
46
+    "encrypt": [],
47
+    "platforms": {
48
+      "cloud": {
49
+        "tcb": "y",
50
+        "aliyun": "y"
51
+      },
52
+      "client": {
53
+        "App": {
54
+          "app-vue": "y",
55
+          "app-nvue": "y"
56
+        },
57
+        "H5-mobile": {
58
+          "Safari": "y",
59
+          "Android Browser": "y",
60
+          "微信浏览器(Android)": "y",
61
+          "QQ浏览器(Android)": "y"
62
+        },
63
+        "H5-pc": {
64
+          "Chrome": "y",
65
+          "IE": "y",
66
+          "Edge": "y",
67
+          "Firefox": "y",
68
+          "Safari": "y"
69
+        },
70
+        "小程序": {
71
+          "微信": "y",
72
+          "阿里": "y",
73
+          "百度": "y",
74
+          "字节跳动": "y",
75
+          "QQ": "y"
76
+        },
77
+        "快应用": {
78
+          "华为": "u",
79
+          "联盟": "u"
80
+        },
81
+        "Vue": {
82
+            "vue2": "y",
83
+            "vue3": "u"
84
+        }
85
+      }
86
+    }
87
+  }
88
+}

+ 103 - 0
uni_modules/uni-calendar/readme.md

@@ -0,0 +1,103 @@
1
+
2
+
3
+## Calendar 日历
4
+> **组件名:uni-calendar**
5
+> 代码块: `uCalendar`
6
+
7
+
8
+日历组件
9
+
10
+> **注意事项**
11
+> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
12
+> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js)  
13
+> - 仅支持自定义组件模式
14
+> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date()
15
+> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意
16
+> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动
17
+
18
+
19
+### 安装方式
20
+
21
+本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
22
+
23
+如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
24
+
25
+### 基本用法
26
+
27
+在 ``template`` 中使用组件
28
+
29
+```html
30
+<view>
31
+	<uni-calendar 
32
+	:insert="true"
33
+	:lunar="true" 
34
+	:start-date="'2019-3-2'"
35
+	:end-date="'2019-5-20'"
36
+	@change="change"
37
+	 />
38
+</view>
39
+```
40
+
41
+### 通过方法打开日历
42
+
43
+需要设置 `insert` 为 `false`
44
+
45
+```html
46
+<view>
47
+	<uni-calendar 
48
+	ref="calendar"
49
+	:insert="false"
50
+	@confirm="confirm"
51
+	 />
52
+	 <button @click="open">打开日历</button>
53
+</view>
54
+```
55
+
56
+```javascript
57
+
58
+export default {
59
+	data() {
60
+		return {};
61
+	},
62
+	methods: {
63
+		open(){
64
+			this.$refs.calendar.open();
65
+		},
66
+		confirm(e) {
67
+			console.log(e);
68
+		}
69
+	}
70
+};
71
+
72
+```
73
+
74
+
75
+## API
76
+
77
+### Calendar Props
78
+
79
+|  属性名	|    类型	| 默认值| 说明																													|
80
+| 		| 																													|
81
+| date		| String	|-		| 自定义当前时间,默认为今天																							|
82
+| lunar		| Boolean	| false	| 显示农历																												|
83
+| startDate	| String	|-		| 日期选择范围-开始日期																									|
84
+| endDate	| String	|-		| 日期选择范围-结束日期																									|
85
+| range		| Boolean	| false	| 范围选择																												|
86
+| insert	| Boolean	| false	| 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式														|
87
+|clearDate	|Boolean	|true	|弹窗模式是否清空上次选择内容	|
88
+| selected	| Array		|-		| 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]	|
89
+|showMonth	| Boolean	| true	| 是否显示月份为背景																									|
90
+
91
+### Calendar Events
92
+
93
+|  事件名		| 说明								|返回值|
94
+| 								|		| 									|
95
+| open	| 弹出日历组件,`insert :false` 时生效|- 	|
96
+
97
+
98
+
99
+
100
+
101
+## 组件示例
102
+
103
+点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar)

+ 26 - 0
uni_modules/uni-card/changelog.md

@@ -0,0 +1,26 @@
1
+## 1.3.1(2021-12-20)
2
+- 修复 在vue页面下略缩图显示不正常的bug
3
+## 1.3.0(2021-11-19)
4
+- 重构插槽的用法 ,header 替换为 title 
5
+- 新增 actions 插槽
6
+- 新增 cover 封面图属性和插槽
7
+- 新增 padding 内容默认内边距离
8
+- 新增 margin 卡片默认外边距离
9
+- 新增 spacing 卡片默认内边距
10
+- 新增 shadow 卡片阴影属性
11
+- 取消 mode 属性,可使用组合插槽代替
12
+- 取消 note 属性 ,使用actions插槽代替
13
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
14
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card)
15
+## 1.2.1(2021-07-30)
16
+- 优化 vue3下事件警告的问题
17
+## 1.2.0(2021-07-13)
18
+- 组件兼容 vue3,如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
19
+## 1.1.8(2021-07-01)
20
+- 优化 图文卡片无图片加载时,提供占位图标
21
+- 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持)
22
+- 修复 thumbnail 不存在仍然占位的 bug
23
+## 1.1.7(2021-05-12)
24
+- 新增 组件示例地址
25
+## 1.1.6(2021-02-04)
26
+- 调整为uni_modules目录规范

+ 270 - 0
uni_modules/uni-card/components/uni-card/uni-card.vue

@@ -0,0 +1,270 @@
1
+<template>
2
+	<view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}"
3
+		:style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}">
4
+		<!-- 封面 -->
5
+		<slot name="cover">
6
+			<view v-if="cover" class="uni-card__cover">
7
+				<image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image>
8
+			</view>
9
+		</slot>
10
+		<slot name="title">
11
+			<view v-if="title || extra" class="uni-card__header">
12
+				<!-- 卡片标题 -->
13
+				<view class="uni-card__header-box" @click="onClick('title')">
14
+					<view v-if="thumbnail" class="uni-card__header-avatar">
15
+						<image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" />
16
+					</view>
17
+					<view class="uni-card__header-content">
18
+						<text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text>
19
+						<text v-if="title&&subTitle"
20
+							class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text>
21
+					</view>
22
+				</view>
23
+				<view class="uni-card__header-extra" @click="onClick('extra')">
24
+					<text class="uni-card__header-extra-text">{{ extra }}</text>
25
+				</view>
26
+			</view>
27
+		</slot>
28
+		<!-- 卡片内容 -->
29
+		<view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')">
30
+			<slot></slot>
31
+		</view>
32
+		<view class="uni-card__actions" @click="onClick('actions')">
33
+			<slot name="actions"></slot>
34
+		</view>
35
+	</view>
36
+</template>
37
+
38
+<script>
39
+	/**
40
+	 * Card 卡片
41
+	 * @description 卡片视图组件
42
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=22
43
+	 * @property {String} title 标题文字
44
+	 * @property {String} subTitle 副标题
45
+	 * @property {Number} padding 内容内边距
46
+	 * @property {Number} margin 卡片外边距
47
+	 * @property {Number} spacing 卡片内边距
48
+	 * @property {String} extra 标题额外信息
49
+	 * @property {String} cover 封面图(本地路径需要引入)
50
+	 * @property {String} thumbnail 标题左侧缩略图
51
+	 * @property {Boolean} is-full = [true | false] 卡片内容是否通栏,为 true 时将去除padding值
52
+	 * @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影
53
+	 * @property {String} shadow 卡片阴影
54
+	 * @property {Boolean} border 卡片边框
55
+	 * @event {Function} click 点击 Card 触发事件
56
+	 */
57
+	export default {
58
+		name: 'UniCard',
59
+		emits: ['click'],
60
+		props: {
61
+			title: {
62
+				type: String,
63
+				default: ''
64
+			},
65
+			subTitle: {
66
+				type: String,
67
+				default: ''
68
+			},
69
+			padding: {
70
+				type: String,
71
+				default: '10px'
72
+			},
73
+			margin: {
74
+				type: String,
75
+				default: '15px'
76
+			},
77
+			spacing: {
78
+				type: String,
79
+				default: '0 10px'
80
+			},
81
+			extra: {
82
+				type: String,
83
+				default: ''
84
+			},
85
+			cover: {
86
+				type: String,
87
+				default: ''
88
+			},
89
+			thumbnail: {
90
+				type: String,
91
+				default: ''
92
+			},
93
+			isFull: {
94
+				// 内容区域是否通栏
95
+				type: Boolean,
96
+				default: false
97
+			},
98
+			isShadow: {
99
+				// 是否开启阴影
100
+				type: Boolean,
101
+				default: true
102
+			},
103
+			shadow: {
104
+				type: String,
105
+				default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)'
106
+			},
107
+			border: {
108
+				type: Boolean,
109
+				default: true
110
+			}
111
+		},
112
+		methods: {
113
+			onClick(type) {
114
+				this.$emit('click', type)
115
+			}
116
+		}
117
+	}
118
+</script>
119
+
120
+<style lang="scss">
121
+	$uni-border-3: #EBEEF5 !default;
122
+	$uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
123
+	$uni-main-color: #3a3a3a !default;
124
+	$uni-base-color: #6a6a6a !default;
125
+	$uni-secondary-color: #909399 !default;
126
+	$uni-spacing-sm: 8px !default;
127
+	$uni-border-color:$uni-border-3;
128
+	$uni-shadow: $uni-shadow-base;
129
+	$uni-card-title: 15px;
130
+	$uni-cart-title-color:$uni-main-color;
131
+	$uni-card-subtitle: 12px;
132
+	$uni-cart-subtitle-color:$uni-secondary-color;
133
+	$uni-card-spacing: 10px;
134
+	$uni-card-content-color: $uni-base-color;
135
+
136
+	.uni-card {
137
+		margin: $uni-card-spacing;
138
+		padding: 0 $uni-spacing-sm;
139
+		border-radius: 4px;
140
+		overflow: hidden;
141
+		font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
142
+		background-color: #fff;
143
+		flex: 1;
144
+
145
+		.uni-card__cover {
146
+			position: relative;
147
+			margin-top: $uni-card-spacing;
148
+			flex-direction: row;
149
+			overflow: hidden;
150
+			border-radius: 4px;
151
+			.uni-card__cover-image {
152
+				flex: 1;
153
+				// width: 100%;
154
+				/* #ifndef APP-PLUS */
155
+				vertical-align: middle;
156
+				/* #endif */
157
+			}
158
+		}
159
+
160
+		.uni-card__header {
161
+			display: flex;
162
+			border-bottom: 1px $uni-border-color solid;
163
+			flex-direction: row;
164
+			align-items: center;
165
+			padding: $uni-card-spacing;
166
+			overflow: hidden;
167
+
168
+			.uni-card__header-box {
169
+				/* #ifndef APP-NVUE */
170
+				display: flex;
171
+				/* #endif */
172
+				flex: 1;
173
+				flex-direction: row;
174
+				align-items: center;
175
+				overflow: hidden;
176
+			}
177
+
178
+			.uni-card__header-avatar {
179
+				width: 40px;
180
+				height: 40px;
181
+				overflow: hidden;
182
+				border-radius: 5px;
183
+				margin-right: $uni-card-spacing;
184
+				.uni-card__header-avatar-image {
185
+					flex: 1;
186
+					width: 40px;
187
+					height: 40px;
188
+				}
189
+			}
190
+
191
+			.uni-card__header-content {
192
+				/* #ifndef APP-NVUE */
193
+				display: flex;
194
+				/* #endif */
195
+				flex-direction: column;
196
+				justify-content: center;
197
+				flex: 1;
198
+				// height: 40px;
199
+				overflow: hidden;
200
+
201
+				.uni-card__header-content-title {
202
+					font-size: $uni-card-title;
203
+					color: $uni-cart-title-color;
204
+					// line-height: 22px;
205
+				}
206
+
207
+				.uni-card__header-content-subtitle {
208
+					font-size: $uni-card-subtitle;
209
+					margin-top: 5px;
210
+					color: $uni-cart-subtitle-color;
211
+				}
212
+			}
213
+
214
+			.uni-card__header-extra {
215
+				line-height: 12px;
216
+
217
+				.uni-card__header-extra-text {
218
+					font-size: 12px;
219
+					color: $uni-cart-subtitle-color;
220
+				}
221
+			}
222
+		}
223
+
224
+		.uni-card__content {
225
+			padding: $uni-card-spacing;
226
+			font-size: 14px;
227
+			color: $uni-card-content-color;
228
+			line-height: 22px;
229
+		}
230
+
231
+		.uni-card__actions {
232
+			font-size: 12px;
233
+		}
234
+	}
235
+
236
+	.uni-card--border {
237
+		border: 1px solid $uni-border-color;
238
+	}
239
+
240
+	.uni-card--shadow {
241
+		position: relative;
242
+		/* #ifndef APP-NVUE */
243
+		box-shadow: $uni-shadow;
244
+		/* #endif */
245
+	}
246
+
247
+	.uni-card--full {
248
+		margin: 0;
249
+		border-left-width: 0;
250
+		border-left-width: 0;
251
+		border-radius: 0;
252
+	}
253
+
254
+	/* #ifndef APP-NVUE */
255
+	.uni-card--full:after {
256
+		border-radius: 0;
257
+	}
258
+
259
+	/* #endif */
260
+	.uni-ellipsis {
261
+		/* #ifndef APP-NVUE */
262
+		overflow: hidden;
263
+		white-space: nowrap;
264
+		text-overflow: ellipsis;
265
+		/* #endif */
266
+		/* #ifdef APP-NVUE */
267
+		lines: 1;
268
+		/* #endif */
269
+	}
270
+</style>

+ 90 - 0
uni_modules/uni-card/package.json

@@ -0,0 +1,90 @@
1
+{
2
+  "id": "uni-card",
3
+  "displayName": "uni-card 卡片",
4
+  "version": "1.3.1",
5
+  "description": "Card 组件,提供常见的卡片样式。",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "card",
10
+    "",
11
+    "卡片"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": ""
16
+  },
17
+  "directories": {
18
+    "example": "../../temps/example_temps"
19
+  },
20
+  "dcloudext": {
21
+    "category": [
22
+      "前端组件",
23
+      "通用组件"
24
+    ],
25
+    "sale": {
26
+      "regular": {
27
+        "price": "0.00"
28
+      },
29
+      "sourcecode": {
30
+        "price": "0.00"
31
+      }
32
+    },
33
+    "contact": {
34
+      "qq": ""
35
+    },
36
+    "declaration": {
37
+      "ads": "无",
38
+      "data": "无",
39
+      "permissions": "无"
40
+    },
41
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
42
+  },
43
+  "uni_modules": {
44
+    "dependencies": [
45
+			"uni-icons",
46
+			"uni-scss"
47
+		],
48
+    "encrypt": [],
49
+    "platforms": {
50
+      "cloud": {
51
+        "tcb": "y",
52
+        "aliyun": "y"
53
+      },
54
+      "client": {
55
+        "App": {
56
+          "app-vue": "y",
57
+          "app-nvue": "y"
58
+        },
59
+        "H5-mobile": {
60
+          "Safari": "y",
61
+          "Android Browser": "y",
62
+          "微信浏览器(Android)": "y",
63
+          "QQ浏览器(Android)": "y"
64
+        },
65
+        "H5-pc": {
66
+          "Chrome": "y",
67
+          "IE": "y",
68
+          "Edge": "y",
69
+          "Firefox": "y",
70
+          "Safari": "y"
71
+        },
72
+        "小程序": {
73
+          "微信": "y",
74
+          "阿里": "y",
75
+          "百度": "y",
76
+          "字节跳动": "y",
77
+          "QQ": "y"
78
+        },
79
+        "快应用": {
80
+          "华为": "u",
81
+          "联盟": "u"
82
+        },
83
+        "Vue": {
84
+            "vue2": "y",
85
+            "vue3": "y"
86
+        }
87
+      }
88
+    }
89
+  }
90
+}

+ 12 - 0
uni_modules/uni-card/readme.md

@@ -0,0 +1,12 @@
1
+
2
+
3
+## Card 卡片
4
+> **组件名:uni-card**
5
+> 代码块: `uCard`
6
+
7
+卡片视图组件。
8
+
9
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card)
10
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
11
+
12
+

+ 36 - 0
uni_modules/uni-collapse/changelog.md

@@ -0,0 +1,36 @@
1
+## 1.4.3(2022-01-25)
2
+- 修复 初始化的时候 ,open 属性失效的bug
3
+## 1.4.2(2022-01-21)
4
+- 修复 微信小程序resize后组件收起的bug
5
+## 1.4.1(2021-11-22)
6
+- 修复 vue3中个别scss变量无法找到的问题
7
+## 1.4.0(2021-11-19)
8
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
9
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse)
10
+## 1.3.3(2021-08-17)
11
+- 优化 show-arrow 属性默认为true
12
+## 1.3.2(2021-08-17)
13
+- 新增 show-arrow 属性,控制是否显示右侧箭头
14
+## 1.3.1(2021-07-30)
15
+- 优化 vue3下小程序事件警告的问题
16
+## 1.3.0(2021-07-30)
17
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
18
+## 1.2.2(2021-07-21)
19
+- 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug
20
+## 1.2.1(2021-07-21)
21
+- 优化 组件示例
22
+## 1.2.0(2021-07-21)
23
+- 新增 组件折叠动画
24
+- 新增 value\v-model 属性 ,动态修改面板折叠状态
25
+- 新增 title 插槽 ,可定义面板标题
26
+- 新增 border 属性 ,显示隐藏面板内容分隔线
27
+- 新增 title-border 属性 ,显示隐藏面板标题分隔线
28
+- 修复 resize 方法失效的Bug
29
+- 修复 change 事件返回参数不正确的Bug
30
+- 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法
31
+## 1.1.7(2021-05-12)
32
+- 新增 组件示例地址
33
+## 1.1.6(2021-02-05)
34
+- 优化 组件引用关系,通过uni_modules引用组件
35
+## 1.1.5(2021-02-05)
36
+- 调整为uni_modules目录规范

+ 402 - 0
uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue

@@ -0,0 +1,402 @@
1
+<template>
2
+	<view class="uni-collapse-item">
3
+		<!-- onClick(!isOpen) -->
4
+		<view @click="onClick(!isOpen)" class="uni-collapse-item__title"
5
+			:class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}">
6
+			<view class="uni-collapse-item__title-wrap">
7
+				<slot name="title">
8
+					<view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}">
9
+						<image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" />
10
+						<text class="uni-collapse-item__title-text">{{ title }}</text>
11
+					</view>
12
+				</slot>
13
+			</view>
14
+			<view v-if="showArrow"
15
+				:class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }"
16
+				class="uni-collapse-item__title-arrow">
17
+				<uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" />
18
+			</view>
19
+		</view>
20
+		<view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}"
21
+			:style="{height: (isOpen?height:0) +'px'}">
22
+			<view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content"
23
+				:class="{open:isheight,'uni-collapse-item--border':border&&isOpen}">
24
+				<slot></slot>
25
+			</view>
26
+		</view>
27
+
28
+	</view>
29
+</template>
30
+
31
+<script>
32
+	// #ifdef APP-NVUE
33
+	const dom = weex.requireModule('dom')
34
+	// #endif
35
+	/**
36
+	 * CollapseItem 折叠面板子组件
37
+	 * @description 折叠面板子组件
38
+	 * @property {String} title 标题文字
39
+	 * @property {String} thumb 标题左侧缩略图
40
+	 * @property {String} name 唯一标志符
41
+	 * @property {Boolean} open = [true|false] 是否展开组件
42
+	 * @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线
43
+	 * @property {Boolean} border = [true|false] 是否显示分隔线
44
+	 * @property {Boolean} disabled = [true|false] 是否展开面板
45
+	 * @property {Boolean} showAnimation = [true|false] 开启动画
46
+	 * @property {Boolean} showArrow = [true|false] 是否显示右侧箭头
47
+	 */
48
+	export default {
49
+		name: 'uniCollapseItem',
50
+		props: {
51
+			// 列表标题
52
+			title: {
53
+				type: String,
54
+				default: ''
55
+			},
56
+			name: {
57
+				type: [Number, String],
58
+				default: ''
59
+			},
60
+			// 是否禁用
61
+			disabled: {
62
+				type: Boolean,
63
+				default: false
64
+			},
65
+			// #ifdef APP-PLUS
66
+			// 是否显示动画,app 端默认不开启动画,卡顿严重
67
+			showAnimation: {
68
+				type: Boolean,
69
+				default: false
70
+			},
71
+			// #endif
72
+			// #ifndef APP-PLUS
73
+			// 是否显示动画
74
+			showAnimation: {
75
+				type: Boolean,
76
+				default: true
77
+			},
78
+			// #endif
79
+			// 是否展开
80
+			open: {
81
+				type: Boolean,
82
+				default: false
83
+			},
84
+			// 缩略图
85
+			thumb: {
86
+				type: String,
87
+				default: ''
88
+			},
89
+			// 标题分隔线显示类型
90
+			titleBorder: {
91
+				type: String,
92
+				default: 'auto'
93
+			},
94
+			border: {
95
+				type: Boolean,
96
+				default: true
97
+			},
98
+			showArrow: {
99
+				type: Boolean,
100
+				default: true
101
+			}
102
+		},
103
+		data() {
104
+			// TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug
105
+			const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
106
+			return {
107
+				isOpen: false,
108
+				isheight: null,
109
+				height: 0,
110
+				elId,
111
+				nameSync: 0
112
+			}
113
+		},
114
+		watch: {
115
+			open(val) {
116
+				this.isOpen = val
117
+				this.onClick(val, 'init')
118
+			}
119
+		},
120
+		updated(e) {
121
+			this.$nextTick(() => {
122
+				this.init(true)
123
+			})
124
+		},
125
+		created() {
126
+			this.collapse = this.getCollapse()
127
+			this.oldHeight = 0
128
+			this.onClick(this.open, 'init')
129
+		},
130
+		// #ifndef VUE3
131
+		// TODO vue2
132
+		destroyed() {
133
+			if (this.__isUnmounted) return
134
+			this.uninstall()
135
+		},
136
+		// #endif
137
+		// #ifdef VUE3
138
+		// TODO vue3
139
+		unmounted() {
140
+			this.__isUnmounted = true
141
+			this.uninstall()
142
+		},
143
+		// #endif
144
+		mounted() {
145
+			if (!this.collapse) return
146
+			if (this.name !== '') {
147
+				this.nameSync = this.name
148
+			} else {
149
+				this.nameSync = this.collapse.childrens.length + ''
150
+			}
151
+			if (this.collapse.names.indexOf(this.nameSync) === -1) {
152
+				this.collapse.names.push(this.nameSync)
153
+			} else {
154
+				console.warn(`name 值 ${this.nameSync} 重复`);
155
+			}
156
+			if (this.collapse.childrens.indexOf(this) === -1) {
157
+				this.collapse.childrens.push(this)
158
+			}
159
+			this.init()
160
+		},
161
+		methods: {
162
+			init(type) {
163
+				// #ifndef APP-NVUE
164
+				this.getCollapseHeight(type)
165
+				// #endif
166
+				// #ifdef APP-NVUE
167
+				this.getNvueHwight(type)
168
+				// #endif
169
+			},
170
+			uninstall() {
171
+				if (this.collapse) {
172
+					this.collapse.childrens.forEach((item, index) => {
173
+						if (item === this) {
174
+							this.collapse.childrens.splice(index, 1)
175
+						}
176
+					})
177
+					this.collapse.names.forEach((item, index) => {
178
+						if (item === this.nameSync) {
179
+							this.collapse.names.splice(index, 1)
180
+						}
181
+					})
182
+				}
183
+			},
184
+			onClick(isOpen, type) {
185
+				if (this.disabled) return
186
+				this.isOpen = isOpen
187
+				if (this.isOpen && this.collapse) {
188
+					this.collapse.setAccordion(this)
189
+				}
190
+				if (type !== 'init') {
191
+					this.collapse.onChange(isOpen, this)
192
+				}
193
+			},
194
+			getCollapseHeight(type, index = 0) {
195
+				const views = uni.createSelectorQuery().in(this)
196
+				views
197
+					.select(`#${this.elId}`)
198
+					.fields({
199
+						size: true
200
+					}, data => {
201
+						// TODO 百度中可能获取不到节点信息 ,需要循环获取
202
+						if (index >= 10) return
203
+						if (!data) {
204
+							index++
205
+							this.getCollapseHeight(false, index)
206
+							return
207
+						}
208
+						// #ifdef APP-NVUE
209
+						this.height = data.height + 1
210
+						// #endif
211
+						// #ifndef APP-NVUE
212
+						this.height = data.height
213
+						// #endif
214
+						this.isheight = true
215
+						if (type) return
216
+						this.onClick(this.isOpen, 'init')
217
+					})
218
+					.exec()
219
+			},
220
+			getNvueHwight(type) {
221
+				const result = dom.getComponentRect(this.$refs['collapse--hook'], option => {
222
+					if (option && option.result && option.size) {
223
+						// #ifdef APP-NVUE
224
+						this.height = option.size.height + 1
225
+						// #endif
226
+						// #ifndef APP-NVUE
227
+						this.height = option.size.height
228
+						// #endif
229
+						this.isheight = true
230
+						if (type) return
231
+						this.onClick(this.open, 'init')
232
+					}
233
+				})
234
+			},
235
+			/**
236
+			 * 获取父元素实例
237
+			 */
238
+			getCollapse(name = 'uniCollapse') {
239
+				let parent = this.$parent;
240
+				let parentName = parent.$options.name;
241
+				while (parentName !== name) {
242
+					parent = parent.$parent;
243
+					if (!parent) return false;
244
+					parentName = parent.$options.name;
245
+				}
246
+				return parent;
247
+			}
248
+		}
249
+	}
250
+</script>
251
+
252
+<style lang="scss">
253
+	.uni-collapse-item {
254
+		/* #ifndef APP-NVUE */
255
+		box-sizing: border-box;
256
+
257
+		/* #endif */
258
+		&__title {
259
+			/* #ifndef APP-NVUE */
260
+			display: flex;
261
+			width: 100%;
262
+			box-sizing: border-box;
263
+			/* #endif */
264
+			flex-direction: row;
265
+			align-items: center;
266
+			transition: border-bottom-color .3s;
267
+
268
+			// transition-property: border-bottom-color;
269
+			// transition-duration: 5s;
270
+			&-wrap {
271
+				width: 100%;
272
+				flex: 1;
273
+
274
+			}
275
+
276
+			&-box {
277
+				padding: 0 15px;
278
+				/* #ifndef APP-NVUE */
279
+				display: flex;
280
+				width: 100%;
281
+				box-sizing: border-box;
282
+				/* #endif */
283
+				flex-direction: row;
284
+				justify-content: space-between;
285
+				align-items: center;
286
+				height: 48px;
287
+				line-height: 48px;
288
+				background-color: #fff;
289
+				color: #303133;
290
+				font-size: 13px;
291
+				font-weight: 500;
292
+				/* #ifdef H5 */
293
+				cursor: pointer;
294
+				outline: none;
295
+
296
+				/* #endif */
297
+				&.is-disabled {
298
+					.uni-collapse-item__title-text {
299
+						color: #999;
300
+					}
301
+				}
302
+
303
+			}
304
+
305
+			&.uni-collapse-item-border {
306
+				border-bottom: 1px solid #ebeef5;
307
+			}
308
+
309
+			&.is-open {
310
+				border-bottom-color: transparent;
311
+			}
312
+
313
+			&-img {
314
+				height: 22px;
315
+				width: 22px;
316
+				margin-right: 10px;
317
+			}
318
+
319
+			&-text {
320
+				flex: 1;
321
+				font-size: 14px;
322
+				/* #ifndef APP-NVUE */
323
+				white-space: nowrap;
324
+				color: inherit;
325
+				/* #endif */
326
+				/* #ifdef APP-NVUE */
327
+				lines: 1;
328
+				/* #endif */
329
+				overflow: hidden;
330
+				text-overflow: ellipsis;
331
+			}
332
+
333
+			&-arrow {
334
+				/* #ifndef APP-NVUE */
335
+				display: flex;
336
+				box-sizing: border-box;
337
+				/* #endif */
338
+				align-items: center;
339
+				justify-content: center;
340
+				width: 20px;
341
+				height: 20px;
342
+				margin-right: 10px;
343
+				transform: rotate(0deg);
344
+
345
+				&-active {
346
+					transform: rotate(-180deg);
347
+				}
348
+			}
349
+
350
+
351
+		}
352
+
353
+		&__wrap {
354
+			/* #ifndef APP-NVUE */
355
+			will-change: height;
356
+			box-sizing: border-box;
357
+			/* #endif */
358
+			background-color: #fff;
359
+			overflow: hidden;
360
+			position: relative;
361
+			height: 0;
362
+
363
+			&.is--transition {
364
+				// transition: all 0.3s;
365
+				transition-property: height, border-bottom-width;
366
+				transition-duration: 0.3s;
367
+				/* #ifndef APP-NVUE */
368
+				will-change: height;
369
+				/* #endif */
370
+			}
371
+
372
+
373
+
374
+			&-content {
375
+				position: absolute;
376
+				font-size: 13px;
377
+				color: #303133;
378
+				// transition: height 0.3s;
379
+				border-bottom-color: transparent;
380
+				border-bottom-style: solid;
381
+				border-bottom-width: 0;
382
+
383
+				&.uni-collapse-item--border {
384
+					border-bottom-width: 1px;
385
+					border-bottom-color: red;
386
+					border-bottom-color: #ebeef5;
387
+				}
388
+
389
+				&.open {
390
+					position: relative;
391
+				}
392
+			}
393
+		}
394
+
395
+		&--animation {
396
+			transition-property: transform;
397
+			transition-duration: 0.3s;
398
+			transition-timing-function: ease;
399
+		}
400
+
401
+	}
402
+</style>

+ 147 - 0
uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue

@@ -0,0 +1,147 @@
1
+<template>
2
+	<view class="uni-collapse">
3
+		<slot />
4
+	</view>
5
+</template>
6
+<script>
7
+	/**
8
+	 * Collapse 折叠面板
9
+	 * @description 展示可以折叠 / 展开的内容区域
10
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=23
11
+	 * @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array)
12
+	 * @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
13
+	 * @event {Function} change 切换面板时触发,如果是手风琴模式,返回类型为string,否则为array
14
+	 */
15
+	export default {
16
+		name: 'uniCollapse',
17
+		emits:['change','activeItem','input','update:modelValue'],
18
+		props: {
19
+			value: {
20
+				type: [String, Array],
21
+				default: ''
22
+			},
23
+			modelValue: {
24
+				type: [String, Array],
25
+				default: ''
26
+			},
27
+			accordion: {
28
+				// 是否开启手风琴效果
29
+				type: [Boolean, String],
30
+				default: false
31
+			},
32
+		},
33
+		data() {
34
+			return {}
35
+		},
36
+		computed: {
37
+			// TODO 兼容 vue2 和 vue3
38
+			dataValue() {
39
+				let value = (typeof this.value === 'string' && this.value === '') ||
40
+					(Array.isArray(this.value) && this.value.length === 0)
41
+				let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') ||
42
+					(Array.isArray(this.modelValue) && this.modelValue.length === 0)
43
+				if (value) {
44
+					return this.modelValue
45
+				}
46
+				if (modelValue) {
47
+					return this.value
48
+				}
49
+
50
+				return this.value
51
+			}
52
+		},
53
+		watch: {
54
+			dataValue(val) {
55
+				this.setOpen(val)
56
+			}
57
+		},
58
+		created() {
59
+			this.childrens = []
60
+			this.names = []
61
+		},
62
+		mounted() {
63
+			this.$nextTick(()=>{
64
+				this.setOpen(this.dataValue)
65
+			})
66
+		},
67
+		methods: {
68
+			setOpen(val) {
69
+				let str = typeof val === 'string'
70
+				let arr = Array.isArray(val)
71
+				this.childrens.forEach((vm, index) => {
72
+					if (str) {
73
+						if (val === vm.nameSync) {
74
+							if (!this.accordion) {
75
+								console.warn('accordion 属性为 false ,v-model 类型应该为 array')
76
+								return
77
+							}
78
+							vm.isOpen = true
79
+						}
80
+					}
81
+					if (arr) {
82
+						val.forEach(v => {
83
+							if (v === vm.nameSync) {
84
+								if (this.accordion) {
85
+									console.warn('accordion 属性为 true ,v-model 类型应该为 string')
86
+									return
87
+								}
88
+								vm.isOpen = true
89
+							}
90
+						})
91
+					}
92
+				})
93
+				this.emit(val)
94
+			},
95
+			setAccordion(self) {
96
+				if (!this.accordion) return
97
+				this.childrens.forEach((vm, index) => {
98
+					if (self !== vm) {
99
+						vm.isOpen = false
100
+					}
101
+				})
102
+			},
103
+			resize() {
104
+				this.childrens.forEach((vm, index) => {
105
+					// #ifndef APP-NVUE
106
+					vm.getCollapseHeight()
107
+					// #endif
108
+					// #ifdef APP-NVUE
109
+					vm.getNvueHwight()
110
+					// #endif
111
+				})
112
+			},
113
+			onChange(isOpen, self) {
114
+				let activeItem = []
115
+
116
+				if (this.accordion) {
117
+					activeItem = isOpen ? self.nameSync : ''
118
+				} else {
119
+					this.childrens.forEach((vm, index) => {
120
+						if (vm.isOpen) {
121
+							activeItem.push(vm.nameSync)
122
+						}
123
+					})
124
+				}
125
+				this.$emit('change', activeItem)
126
+				this.emit(activeItem)
127
+			},
128
+			emit(val){
129
+				this.$emit('input', val)
130
+				this.$emit('update:modelValue', val)
131
+			}
132
+		}
133
+	}
134
+</script>
135
+<style lang="scss" >
136
+	.uni-collapse {
137
+		/* #ifndef APP-NVUE */
138
+		width: 100%;
139
+		display: flex;
140
+		/* #endif */
141
+		/* #ifdef APP-NVUE */
142
+		flex: 1;
143
+		/* #endif */
144
+		flex-direction: column;
145
+		background-color: #fff;
146
+	}
147
+</style>

+ 89 - 0
uni_modules/uni-collapse/package.json

@@ -0,0 +1,89 @@
1
+{
2
+  "id": "uni-collapse",
3
+  "displayName": "uni-collapse 折叠面板",
4
+  "version": "1.4.3",
5
+  "description": "Collapse 组件,可以折叠 / 展开的内容区域。",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "折叠",
9
+    "折叠面板",
10
+    "手风琴"
11
+],
12
+  "repository": "https://github.com/dcloudio/uni-ui",
13
+  "engines": {
14
+    "HBuilderX": ""
15
+  },
16
+  "directories": {
17
+    "example": "../../temps/example_temps"
18
+  },
19
+  "dcloudext": {
20
+    "category": [
21
+      "前端组件",
22
+      "通用组件"
23
+    ],
24
+    "sale": {
25
+      "regular": {
26
+        "price": "0.00"
27
+      },
28
+      "sourcecode": {
29
+        "price": "0.00"
30
+      }
31
+    },
32
+    "contact": {
33
+      "qq": ""
34
+    },
35
+    "declaration": {
36
+      "ads": "无",
37
+      "data": "无",
38
+      "permissions": "无"
39
+    },
40
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
41
+  },
42
+  "uni_modules": {
43
+    "dependencies": [
44
+			"uni-scss",
45
+      "uni-icons"
46
+    ],
47
+    "encrypt": [],
48
+    "platforms": {
49
+      "cloud": {
50
+        "tcb": "y",
51
+        "aliyun": "y"
52
+      },
53
+      "client": {
54
+        "App": {
55
+          "app-vue": "y",
56
+          "app-nvue": "y"
57
+        },
58
+        "H5-mobile": {
59
+          "Safari": "y",
60
+          "Android Browser": "y",
61
+          "微信浏览器(Android)": "y",
62
+          "QQ浏览器(Android)": "y"
63
+        },
64
+        "H5-pc": {
65
+          "Chrome": "y",
66
+          "IE": "y",
67
+          "Edge": "y",
68
+          "Firefox": "y",
69
+          "Safari": "y"
70
+        },
71
+        "小程序": {
72
+          "微信": "y",
73
+          "阿里": "y",
74
+          "百度": "y",
75
+          "字节跳动": "y",
76
+          "QQ": "y"
77
+        },
78
+        "快应用": {
79
+          "华为": "u",
80
+          "联盟": "u"
81
+        },
82
+        "Vue": {
83
+            "vue2": "y",
84
+            "vue3": "y"
85
+        }
86
+      }
87
+    }
88
+  }
89
+}

+ 12 - 0
uni_modules/uni-collapse/readme.md

@@ -0,0 +1,12 @@
1
+
2
+
3
+## Collapse 折叠面板
4
+> **组件名:uni-collapse**
5
+> 代码块: `uCollapse`
6
+> 关联组件:`uni-collapse-item`、`uni-icons`。
7
+
8
+
9
+折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。
10
+
11
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse)
12
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 15 - 0
uni_modules/uni-combox/changelog.md

@@ -0,0 +1,15 @@
1
+## 1.0.1(2021-11-23)
2
+- 优化 label、label-width 属性
3
+## 1.0.0(2021-11-19)
4
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
5
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox)
6
+## 0.1.0(2021-07-30)
7
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
8
+## 0.0.6(2021-05-12)
9
+- 新增 组件示例地址
10
+## 0.0.5(2021-04-21)
11
+- 优化 添加依赖 uni-icons, 导入后自动下载依赖
12
+## 0.0.4(2021-02-05)
13
+- 优化 组件引用关系,通过uni_modules引用组件
14
+## 0.0.3(2021-02-04)
15
+- 调整为uni_modules目录规范

+ 275 - 0
uni_modules/uni-combox/components/uni-combox/uni-combox.vue

@@ -0,0 +1,275 @@
1
+<template>
2
+	<view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'">
3
+		<view v-if="label" class="uni-combox__label" :style="labelStyle">
4
+			<text>{{label}}</text>
5
+		</view>
6
+		<view class="uni-combox__input-box">
7
+			<input class="uni-combox__input" type="text" :placeholder="placeholder" 
8
+			placeholder-class="uni-combox__input-plac" v-model="inputVal" @input="onInput" @focus="onFocus" 
9
+@blur="onBlur" />
10
+			<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector">
11
+			</uni-icons>
12
+		</view>
13
+		<view class="uni-combox__selector" v-if="showSelector">
14
+			<view class="uni-popper__arrow"></view>
15
+			<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
16
+				<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
17
+					<text>{{emptyTips}}</text>
18
+				</view>
19
+				<view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index" 
20
+				@click="onSelectorClick(index)">
21
+					<text>{{item}}</text>
22
+				</view>
23
+			</scroll-view>
24
+		</view>
25
+	</view>
26
+</template>
27
+
28
+<script>
29
+	/**
30
+	 * Combox 组合输入框
31
+	 * @description 组合输入框一般用于既可以输入也可以选择的场景
32
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=1261
33
+	 * @property {String} label 左侧文字
34
+	 * @property {String} labelWidth 左侧内容宽度
35
+	 * @property {String} placeholder 输入框占位符
36
+	 * @property {Array} candidates 候选项列表
37
+	 * @property {String} emptyTips 筛选结果为空时显示的文字
38
+	 * @property {String} value 组合框的值
39
+	 */
40
+	export default {
41
+		name: 'uniCombox',
42
+		emits: ['input', 'update:modelValue'],
43
+		props: {
44
+			border: {
45
+				type: Boolean,
46
+				default: true
47
+			},
48
+			label: {
49
+				type: String,
50
+				default: ''
51
+			},
52
+			labelWidth: {
53
+				type: String,
54
+				default: 'auto'
55
+			},
56
+			placeholder: {
57
+				type: String,
58
+				default: ''
59
+			},
60
+			candidates: {
61
+				type: Array,
62
+				default () {
63
+					return []
64
+				}
65
+			},
66
+			emptyTips: {
67
+				type: String,
68
+				default: '无匹配项'
69
+			},
70
+			// #ifndef VUE3
71
+			value: {
72
+				type: [String, Number],
73
+				default: ''
74
+			},
75
+			// #endif
76
+			// #ifdef VUE3
77
+			modelValue: {
78
+				type: [String, Number],
79
+				default: ''
80
+			},
81
+			// #endif
82
+		},
83
+		data() {
84
+			return {
85
+				showSelector: false,
86
+				inputVal: ''
87
+			}
88
+		},
89
+		computed: {
90
+			labelStyle() {
91
+				if (this.labelWidth === 'auto') {
92
+					return ""
93
+				}
94
+				return `width: ${this.labelWidth}`
95
+			},
96
+			filterCandidates() {
97
+				return this.candidates.filter((item) => {
98
+					return item.toString().indexOf(this.inputVal) > -1
99
+				})
100
+			},
101
+			filterCandidatesLength() {
102
+				return this.filterCandidates.length
103
+			}
104
+		},
105
+		watch: {
106
+			// #ifndef VUE3
107
+			value: {
108
+				handler(newVal) {
109
+					this.inputVal = newVal
110
+				},
111
+				immediate: true
112
+			},
113
+			// #endif
114
+			// #ifdef VUE3
115
+			modelValue: {
116
+				handler(newVal) {
117
+					this.inputVal = newVal
118
+				},
119
+				immediate: true
120
+			},
121
+			// #endif
122
+		},
123
+		methods: {
124
+			toggleSelector() {
125
+				this.showSelector = !this.showSelector
126
+			},
127
+			onFocus() {
128
+				this.showSelector = true
129
+			},
130
+			onBlur() {
131
+				setTimeout(() => {
132
+					this.showSelector = false
133
+				}, 153)
134
+			},
135
+			onSelectorClick(index) {
136
+				this.inputVal = this.filterCandidates[index]
137
+				this.showSelector = false
138
+				this.$emit('input', this.inputVal)
139
+				this.$emit('update:modelValue', this.inputVal)
140
+			},
141
+			onInput() {
142
+				setTimeout(() => {
143
+					this.$emit('input', this.inputVal)
144
+					this.$emit('update:modelValue', this.inputVal)
145
+				})
146
+			}
147
+		}
148
+	}
149
+</script>
150
+
151
+<style lang="scss" >
152
+	.uni-combox {
153
+		font-size: 14px;
154
+		border: 1px solid #DCDFE6;
155
+		border-radius: 4px;
156
+		padding: 6px 10px;
157
+		position: relative;
158
+		/* #ifndef APP-NVUE */
159
+		display: flex;
160
+		/* #endif */
161
+		// height: 40px;
162
+		flex-direction: row;
163
+		align-items: center;
164
+		// border-bottom: solid 1px #DDDDDD;
165
+	}
166
+
167
+	.uni-combox__label {
168
+		font-size: 16px;
169
+		line-height: 22px;
170
+		padding-right: 10px;
171
+		color: #999999;
172
+	}
173
+
174
+	.uni-combox__input-box {
175
+		position: relative;
176
+		/* #ifndef APP-NVUE */
177
+		display: flex;
178
+		/* #endif */
179
+		flex: 1;
180
+		flex-direction: row;
181
+		align-items: center;
182
+	}
183
+
184
+	.uni-combox__input {
185
+		flex: 1;
186
+		font-size: 14px;
187
+		height: 22px;
188
+		line-height: 22px;
189
+	}
190
+
191
+	.uni-combox__input-plac {
192
+		font-size: 14px;
193
+		color: #999;
194
+	}
195
+
196
+	.uni-combox__selector {
197
+		/* #ifndef APP-NVUE */
198
+		box-sizing: border-box;
199
+		/* #endif */
200
+		position: absolute;
201
+		top: calc(100% + 12px);
202
+		left: 0;
203
+		width: 100%;
204
+		background-color: #FFFFFF;
205
+		border: 1px solid #EBEEF5;
206
+		border-radius: 6px;
207
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
208
+		z-index: 2;
209
+		padding: 4px 0;
210
+	}
211
+
212
+	.uni-combox__selector-scroll {
213
+		/* #ifndef APP-NVUE */
214
+		max-height: 200px;
215
+		box-sizing: border-box;
216
+		/* #endif */
217
+	}
218
+
219
+	.uni-combox__selector-empty,
220
+	.uni-combox__selector-item {
221
+		/* #ifndef APP-NVUE */
222
+		display: flex;
223
+		cursor: pointer;
224
+		/* #endif */
225
+		line-height: 36px;
226
+		font-size: 14px;
227
+		text-align: center;
228
+		// border-bottom: solid 1px #DDDDDD;
229
+		padding: 0px 10px;
230
+	}
231
+
232
+	.uni-combox__selector-item:hover {
233
+		background-color: #f9f9f9;
234
+	}
235
+
236
+	.uni-combox__selector-empty:last-child,
237
+	.uni-combox__selector-item:last-child {
238
+		/* #ifndef APP-NVUE */
239
+		border-bottom: none;
240
+		/* #endif */
241
+	}
242
+
243
+	// picker 弹出层通用的指示小三角
244
+	.uni-popper__arrow,
245
+	.uni-popper__arrow::after {
246
+		position: absolute;
247
+		display: block;
248
+		width: 0;
249
+		height: 0;
250
+		border-color: transparent;
251
+		border-style: solid;
252
+		border-width: 6px;
253
+	}
254
+
255
+	.uni-popper__arrow {
256
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
257
+		top: -6px;
258
+		left: 10%;
259
+		margin-right: 3px;
260
+		border-top-width: 0;
261
+		border-bottom-color: #EBEEF5;
262
+	}
263
+
264
+	.uni-popper__arrow::after {
265
+		content: " ";
266
+		top: 1px;
267
+		margin-left: -6px;
268
+		border-top-width: 0;
269
+		border-bottom-color: #fff;
270
+	}
271
+
272
+	.uni-combox__no-border {
273
+		border: none;
274
+	}
275
+</style>

+ 90 - 0
uni_modules/uni-combox/package.json

@@ -0,0 +1,90 @@
1
+{
2
+  "id": "uni-combox",
3
+  "displayName": "uni-combox 组合框",
4
+  "version": "1.0.1",
5
+  "description": "可以选择也可以输入的表单项 ",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "combox",
10
+    "组合框",
11
+    "select"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": ""
16
+  },
17
+  "directories": {
18
+    "example": "../../temps/example_temps"
19
+  },
20
+  "dcloudext": {
21
+    "category": [
22
+      "前端组件",
23
+      "通用组件"
24
+    ],
25
+    "sale": {
26
+      "regular": {
27
+        "price": "0.00"
28
+      },
29
+      "sourcecode": {
30
+        "price": "0.00"
31
+      }
32
+    },
33
+    "contact": {
34
+      "qq": ""
35
+    },
36
+    "declaration": {
37
+      "ads": "无",
38
+      "data": "无",
39
+      "permissions": "无"
40
+    },
41
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
42
+  },
43
+  "uni_modules": {
44
+    "dependencies": [
45
+			"uni-scss",
46
+			"uni-icons"
47
+		],
48
+    "encrypt": [],
49
+    "platforms": {
50
+      "cloud": {
51
+        "tcb": "y",
52
+        "aliyun": "y"
53
+      },
54
+      "client": {
55
+        "App": {
56
+          "app-vue": "y",
57
+          "app-nvue": "n"
58
+        },
59
+        "H5-mobile": {
60
+          "Safari": "y",
61
+          "Android Browser": "y",
62
+          "微信浏览器(Android)": "y",
63
+          "QQ浏览器(Android)": "y"
64
+        },
65
+        "H5-pc": {
66
+          "Chrome": "y",
67
+          "IE": "y",
68
+          "Edge": "y",
69
+          "Firefox": "y",
70
+          "Safari": "y"
71
+        },
72
+        "小程序": {
73
+          "微信": "y",
74
+          "阿里": "y",
75
+          "百度": "y",
76
+          "字节跳动": "y",
77
+          "QQ": "y"
78
+        },
79
+        "快应用": {
80
+          "华为": "u",
81
+          "联盟": "u"
82
+        },
83
+        "Vue": {
84
+            "vue2": "y",
85
+            "vue3": "y"
86
+        }
87
+      }
88
+    }
89
+  }
90
+}

+ 11 - 0
uni_modules/uni-combox/readme.md

@@ -0,0 +1,11 @@
1
+
2
+
3
+## Combox 组合框
4
+> **组件名:uni-combox**
5
+> 代码块: `uCombox`
6
+
7
+
8
+组合框组件。
9
+
10
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox)
11
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 24 - 0
uni_modules/uni-countdown/changelog.md

@@ -0,0 +1,24 @@
1
+## 1.2.2(2022-01-19)
2
+- 修复 在微信小程序中样式不生效的bug
3
+## 1.2.1(2022-01-18)
4
+- 新增 update 方法 ,在动态更新时间后,刷新组件
5
+## 1.2.0(2021-11-19)
6
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
7
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown)
8
+## 1.1.3(2021-10-18)
9
+- 重构
10
+- 新增 font-size 支持自定义字体大小
11
+## 1.1.2(2021-08-24)
12
+- 新增 支持国际化
13
+## 1.1.1(2021-07-30)
14
+- 优化 vue3下小程序事件警告的问题
15
+## 1.1.0(2021-07-30)
16
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
17
+## 1.0.5(2021-06-18)
18
+- 修复 uni-countdown 重复赋值跳两秒的 bug
19
+## 1.0.4(2021-05-12)
20
+- 新增 组件示例地址
21
+## 1.0.3(2021-05-08)
22
+- 修复 uni-countdown 不能控制倒计时的 bug
23
+## 1.0.2(2021-02-04)
24
+- 调整为uni_modules目录规范

+ 6 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/en.json

@@ -0,0 +1,6 @@
1
+{
2
+	"uni-countdown.day": "day",
3
+	"uni-countdown.h": "h",
4
+	"uni-countdown.m": "m",
5
+	"uni-countdown.s": "s"
6
+}

+ 8 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/index.js

@@ -0,0 +1,8 @@
1
+import en from './en.json'
2
+import zhHans from './zh-Hans.json'
3
+import zhHant from './zh-Hant.json'
4
+export default {
5
+	en,
6
+	'zh-Hans': zhHans,
7
+	'zh-Hant': zhHant
8
+}

+ 6 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json

@@ -0,0 +1,6 @@
1
+{
2
+	"uni-countdown.day": "天",
3
+	"uni-countdown.h": "时",
4
+	"uni-countdown.m": "分",
5
+	"uni-countdown.s": "秒"
6
+}

+ 6 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json

@@ -0,0 +1,6 @@
1
+{
2
+	"uni-countdown.day": "天",
3
+	"uni-countdown.h": "時",
4
+	"uni-countdown.m": "分",
5
+	"uni-countdown.s": "秒"
6
+}

+ 271 - 0
uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue

@@ -0,0 +1,271 @@
1
+<template>
2
+	<view class="uni-countdown">
3
+		<text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text>
4
+		<text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text>
5
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text>
6
+		<text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text>
7
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text>
8
+		<text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text>
9
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text>
10
+		<text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text>
11
+	</view>
12
+</template>
13
+<script>
14
+	import {
15
+		initVueI18n
16
+	} from '@dcloudio/uni-i18n'
17
+	import messages from './i18n/index.js'
18
+	const {
19
+		t
20
+	} = initVueI18n(messages)
21
+	/**
22
+	 * Countdown 倒计时
23
+	 * @description 倒计时组件
24
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=25
25
+	 * @property {String} backgroundColor 背景色
26
+	 * @property {String} color 文字颜色
27
+	 * @property {Number} day 天数
28
+	 * @property {Number} hour 小时
29
+	 * @property {Number} minute 分钟
30
+	 * @property {Number} second 秒
31
+	 * @property {Number} timestamp 时间戳
32
+	 * @property {Boolean} showDay = [true|false] 是否显示天数
33
+	 * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符
34
+	 * @property {String} splitorColor 分割符号颜色
35
+	 * @event {Function} timeup 倒计时时间到触发事件
36
+	 * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
37
+	 */
38
+	export default {
39
+		name: 'UniCountdown',
40
+		emits: ['timeup'],
41
+		props: {
42
+			showDay: {
43
+				type: Boolean,
44
+				default: true
45
+			},
46
+			showColon: {
47
+				type: Boolean,
48
+				default: true
49
+			},
50
+			start: {
51
+				type: Boolean,
52
+				default: true
53
+			},
54
+			backgroundColor: {
55
+				type: String,
56
+				default: ''
57
+			},
58
+			color: {
59
+				type: String,
60
+				default: '#333'
61
+			},
62
+			fontSize: {
63
+				type: Number,
64
+				default: 14
65
+			},
66
+			splitorColor: {
67
+				type: String,
68
+				default: '#333'
69
+			},
70
+			day: {
71
+				type: Number,
72
+				default: 0
73
+			},
74
+			hour: {
75
+				type: Number,
76
+				default: 0
77
+			},
78
+			minute: {
79
+				type: Number,
80
+				default: 0
81
+			},
82
+			second: {
83
+				type: Number,
84
+				default: 0
85
+			},
86
+			timestamp: {
87
+				type: Number,
88
+				default: 0
89
+			}
90
+		},
91
+		data() {
92
+			return {
93
+				timer: null,
94
+				syncFlag: false,
95
+				d: '00',
96
+				h: '00',
97
+				i: '00',
98
+				s: '00',
99
+				leftTime: 0,
100
+				seconds: 0
101
+			}
102
+		},
103
+		computed: {
104
+			dayText() {
105
+				return t("uni-countdown.day")
106
+			},
107
+			hourText(val) {
108
+				return t("uni-countdown.h")
109
+			},
110
+			minuteText(val) {
111
+				return t("uni-countdown.m")
112
+			},
113
+			secondText(val) {
114
+				return t("uni-countdown.s")
115
+			},
116
+			timeStyle() {
117
+				const {
118
+					color,
119
+					backgroundColor,
120
+					fontSize
121
+				} = this
122
+				return {
123
+					color,
124
+					backgroundColor,
125
+					fontSize: `${fontSize}px`,
126
+					width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放
127
+ 					lineHeight: `${fontSize * 20 / 14}px`,
128
+					borderRadius: `${fontSize * 3 / 14}px`,
129
+				}
130
+			},
131
+			splitorStyle() {
132
+				const { splitorColor, fontSize, backgroundColor } = this
133
+				return {
134
+					color: splitorColor,
135
+					fontSize: `${fontSize * 12 / 14}px`,
136
+					margin: backgroundColor ? `${fontSize * 4 / 14}px` : ''
137
+				}
138
+			}
139
+		},
140
+		watch: {
141
+			day(val) {
142
+				this.changeFlag()
143
+			},
144
+			hour(val) {
145
+				this.changeFlag()
146
+			},
147
+			minute(val) {
148
+				this.changeFlag()
149
+			},
150
+			second(val) {
151
+				this.changeFlag()
152
+			},
153
+			start: {
154
+				immediate: true,
155
+				handler(newVal, oldVal) {
156
+					if (newVal) {
157
+						this.startData();
158
+					} else {
159
+						if (!oldVal) return
160
+						clearInterval(this.timer)
161
+					}
162
+				}
163
+
164
+			}
165
+		},
166
+		created: function(e) {
167
+			this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
168
+			this.countDown()
169
+		},
170
+		// #ifndef VUE3
171
+		destroyed() {
172
+			clearInterval(this.timer)
173
+		},
174
+		// #endif
175
+		// #ifdef VUE3
176
+		unmounted() {
177
+			clearInterval(this.timer)
178
+		},
179
+		// #endif
180
+		methods: {
181
+			toSeconds(timestamp, day, hours, minutes, seconds) {
182
+				if (timestamp) {
183
+					return timestamp - parseInt(new Date().getTime() / 1000, 10)
184
+				}
185
+				return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
186
+			},
187
+			timeUp() {
188
+				clearInterval(this.timer)
189
+				this.$emit('timeup')
190
+			},
191
+			countDown() {
192
+				let seconds = this.seconds
193
+				let [day, hour, minute, second] = [0, 0, 0, 0]
194
+				if (seconds > 0) {
195
+					day = Math.floor(seconds / (60 * 60 * 24))
196
+					hour = Math.floor(seconds / (60 * 60)) - (day * 24)
197
+					minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
198
+					second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
199
+				} else {
200
+					this.timeUp()
201
+				}
202
+				if (day < 10) {
203
+					day = '0' + day
204
+				}
205
+				if (hour < 10) {
206
+					hour = '0' + hour
207
+				}
208
+				if (minute < 10) {
209
+					minute = '0' + minute
210
+				}
211
+				if (second < 10) {
212
+					second = '0' + second
213
+				}
214
+				this.d = day
215
+				this.h = hour
216
+				this.i = minute
217
+				this.s = second
218
+			},
219
+			startData() {
220
+				this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
221
+				if (this.seconds <= 0) {
222
+					this.seconds = this.toSeconds(0, 0, 0, 0, 0)
223
+					this.countDown()
224
+					return
225
+				}
226
+				clearInterval(this.timer)
227
+				this.countDown()
228
+				this.timer = setInterval(() => {
229
+					this.seconds--
230
+					if (this.seconds < 0) {
231
+						this.timeUp()
232
+						return
233
+					}
234
+					this.countDown()
235
+				}, 1000)
236
+			},
237
+			update(){
238
+				this.startData();
239
+			},
240
+			changeFlag() {
241
+				if (!this.syncFlag) {
242
+					this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
243
+					this.startData();
244
+					this.syncFlag = true;
245
+				}
246
+			}
247
+		}
248
+	}
249
+</script>
250
+<style lang="scss" scoped>
251
+	$font-size: 14px;
252
+
253
+	.uni-countdown {
254
+		display: flex;
255
+		flex-direction: row;
256
+		justify-content: flex-start;
257
+		align-items: center;
258
+
259
+		&__splitor {
260
+			margin: 0 2px;
261
+			font-size: $font-size;
262
+			color: #333;
263
+		}
264
+
265
+		&__number {
266
+			border-radius: 3px;
267
+			text-align: center;
268
+			font-size: $font-size;
269
+		}
270
+	}
271
+</style>

+ 86 - 0
uni_modules/uni-countdown/package.json

@@ -0,0 +1,86 @@
1
+{
2
+  "id": "uni-countdown",
3
+  "displayName": "uni-countdown 倒计时",
4
+  "version": "1.2.2",
5
+  "description": "CountDown 倒计时组件",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "countdown",
10
+    "倒计时"
11
+],
12
+  "repository": "https://github.com/dcloudio/uni-ui",
13
+  "engines": {
14
+    "HBuilderX": ""
15
+  },
16
+  "directories": {
17
+    "example": "../../temps/example_temps"
18
+  },
19
+  "dcloudext": {
20
+    "category": [
21
+      "前端组件",
22
+      "通用组件"
23
+    ],
24
+    "sale": {
25
+      "regular": {
26
+        "price": "0.00"
27
+      },
28
+      "sourcecode": {
29
+        "price": "0.00"
30
+      }
31
+    },
32
+    "contact": {
33
+      "qq": ""
34
+    },
35
+    "declaration": {
36
+      "ads": "无",
37
+      "data": "无",
38
+      "permissions": "无"
39
+    },
40
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
41
+  },
42
+  "uni_modules": {
43
+    "dependencies": ["uni-scss"],
44
+    "encrypt": [],
45
+    "platforms": {
46
+      "cloud": {
47
+        "tcb": "y",
48
+        "aliyun": "y"
49
+      },
50
+      "client": {
51
+        "App": {
52
+          "app-vue": "y",
53
+          "app-nvue": "y"
54
+        },
55
+        "H5-mobile": {
56
+          "Safari": "y",
57
+          "Android Browser": "y",
58
+          "微信浏览器(Android)": "y",
59
+          "QQ浏览器(Android)": "y"
60
+        },
61
+        "H5-pc": {
62
+          "Chrome": "y",
63
+          "IE": "y",
64
+          "Edge": "y",
65
+          "Firefox": "y",
66
+          "Safari": "y"
67
+        },
68
+        "小程序": {
69
+          "微信": "y",
70
+          "阿里": "y",
71
+          "百度": "y",
72
+          "字节跳动": "y",
73
+          "QQ": "y"
74
+        },
75
+        "快应用": {
76
+          "华为": "u",
77
+          "联盟": "u"
78
+        },
79
+        "Vue": {
80
+            "vue2": "y",
81
+            "vue3": "y"
82
+        }
83
+      }
84
+    }
85
+  }
86
+}

+ 10 - 0
uni_modules/uni-countdown/readme.md

@@ -0,0 +1,10 @@
1
+
2
+
3
+## CountDown 倒计时
4
+> **组件名:uni-countdown**
5
+> 代码块: `uCountDown`
6
+
7
+倒计时组件。
8
+
9
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown)
10
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 41 - 0
uni_modules/uni-data-checkbox/changelog.md

@@ -0,0 +1,41 @@
1
+## 1.0.1(2022-02-07)
2
+- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug
3
+## 1.0.0(2021-11-19)
4
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
5
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
6
+## 0.2.5(2021-08-23)
7
+- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题
8
+## 0.2.4(2021-08-17)
9
+- 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题
10
+## 0.2.3(2021-08-11)
11
+- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
12
+## 0.2.2(2021-07-30)
13
+- 优化 在uni-forms组件,与label不对齐的问题
14
+## 0.2.1(2021-07-27)
15
+- 修复 单选默认值为0不能选中的Bug
16
+## 0.2.0(2021-07-13)
17
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
18
+## 0.1.11(2021-07-06)
19
+- 优化 删除无用日志
20
+## 0.1.10(2021-07-05)
21
+- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题
22
+## 0.1.9(2021-07-05)
23
+- 修复 nvue 黑框样式问题
24
+## 0.1.8(2021-06-28)
25
+- 修复 selectedTextColor 属性不生效的Bug
26
+## 0.1.7(2021-06-02)
27
+- 新增 map 属性,可以方便映射text/value属性
28
+## 0.1.6(2021-05-26)
29
+- 修复 不关联服务空间的情况下组件报错的Bug
30
+## 0.1.5(2021-05-12)
31
+- 新增 组件示例地址
32
+## 0.1.4(2021-04-09)
33
+- 修复 nvue 下无法选中的问题
34
+## 0.1.3(2021-03-22)
35
+- 新增 disabled属性
36
+## 0.1.2(2021-02-24)
37
+- 优化 默认颜色显示
38
+## 0.1.1(2021-02-24)
39
+- 新增 支持nvue
40
+## 0.1.0(2021-02-18)
41
+- “暂无数据”显示居中

+ 817 - 0
uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue

@@ -0,0 +1,817 @@
1
+<template>
2
+	<view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}">
3
+		<template v-if="!isLocal">
4
+			<view class="uni-data-loading">
5
+				<uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18" :content-text="contentText"></uni-load-more>
6
+				<text v-else>{{mixinDatacomErrorMessage}}</text>
7
+			</view>
8
+		</template>
9
+		<template v-else>
10
+			<checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" @change="chagne">
11
+				<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
12
+				 :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
13
+					<checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" :checked="item.selected" />
14
+					<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="checkbox__inner"  :style="item.styleIcon">
15
+						<view class="checkbox__inner-icon"></view>
16
+					</view>
17
+					<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
18
+						<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
19
+						<view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view>
20
+					</view>
21
+				</label>
22
+			</checkbox-group>
23
+			<radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="chagne">
24
+				<!-- -->
25
+				<label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
26
+				 :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
27
+					<radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" :checked="item.selected" />
28
+					<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner"
29
+					 :style="item.styleBackgroud">
30
+						<view class="radio__inner-icon" :style="item.styleIcon"></view>
31
+					</view>
32
+					<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
33
+						<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
34
+						<view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view>
35
+					</view>
36
+				</label>
37
+			</radio-group>
38
+		</template>
39
+	</view>
40
+</template>
41
+
42
+<script>
43
+	/**
44
+	 * DataChecklist 数据选择器
45
+	 * @description 通过数据渲染 checkbox 和 radio
46
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
47
+	 * @property {String} mode = [default| list | button | tag] 显示模式
48
+	 * @value default  	默认横排模式
49
+	 * @value list		列表模式
50
+	 * @value button	按钮模式
51
+	 * @value tag 		标签模式
52
+	 * @property {Boolean} multiple = [true|false] 是否多选
53
+	 * @property {Array|String|Number} value 默认值
54
+	 * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
55
+	 * @property {Number|String} min 最小选择个数 ,multiple为true时生效
56
+	 * @property {Number|String} max 最大选择个数 ,multiple为true时生效
57
+	 * @property {Boolean} wrap 是否换行显示
58
+	 * @property {String} icon = [left|right]  list 列表模式下icon显示位置
59
+	 * @property {Boolean} selectedColor 选中颜色
60
+	 * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
61
+	 * @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示
62
+	 * @property {Object} map 字段映射, 默认 map={text:'text',value:'value'}
63
+	 * @value left 左侧显示
64
+	 * @value right 右侧显示
65
+	 * @event {Function} change  选中发生变化触发
66
+	 */
67
+
68
+	export default {
69
+		name: 'uniDataChecklist',
70
+		mixins: [uniCloud.mixinDatacom || {}],
71
+		emits:['input','update:modelValue','change'],
72
+		props: {
73
+			mode: {
74
+				type: String,
75
+				default: 'default'
76
+			},
77
+
78
+			multiple: {
79
+				type: Boolean,
80
+				default: false
81
+			},
82
+			value: {
83
+				type: [Array, String, Number],
84
+				default () {
85
+					return ''
86
+				}
87
+			},
88
+			// TODO vue3
89
+			modelValue: {
90
+				type: [Array, String, Number],
91
+				default() {
92
+					return '';
93
+				}
94
+			},
95
+			localdata: {
96
+				type: Array,
97
+				default () {
98
+					return []
99
+				}
100
+			},
101
+			min: {
102
+				type: [Number, String],
103
+				default: ''
104
+			},
105
+			max: {
106
+				type: [Number, String],
107
+				default: ''
108
+			},
109
+			wrap: {
110
+				type: Boolean,
111
+				default: false
112
+			},
113
+			icon: {
114
+				type: String,
115
+				default: 'left'
116
+			},
117
+			selectedColor: {
118
+				type: String,
119
+				default: ''
120
+			},
121
+			selectedTextColor: {
122
+				type: String,
123
+				default: ''
124
+			},
125
+			emptyText:{
126
+				type: String,
127
+				default: '暂无数据'
128
+			},
129
+			disabled:{
130
+				type: Boolean,
131
+				default: false
132
+			},
133
+			map:{
134
+				type: Object,
135
+				default(){
136
+					return {
137
+						text:'text',
138
+						value:'value'
139
+					}
140
+				}
141
+			}
142
+		},
143
+		watch: {
144
+			localdata: {
145
+				handler(newVal) {
146
+					this.range = newVal
147
+					this.dataList = this.getDataList(this.getSelectedValue(newVal))
148
+				},
149
+				deep: true
150
+			},
151
+			mixinDatacomResData(newVal) {
152
+				this.range = newVal
153
+				this.dataList = this.getDataList(this.getSelectedValue(newVal))
154
+			},
155
+			value(newVal) {
156
+				this.dataList = this.getDataList(newVal)
157
+				// fix by mehaotian is_reset 在 uni-forms 中定义
158
+				if(!this.is_reset){
159
+					this.is_reset = false
160
+					this.formItem && this.formItem.setValue(newVal)
161
+				}
162
+			},
163
+			modelValue(newVal) {
164
+				this.dataList = this.getDataList(newVal);
165
+				if(!this.is_reset){
166
+					this.is_reset = false
167
+					this.formItem && this.formItem.setValue(newVal)
168
+				}
169
+			}
170
+		},
171
+		data() {
172
+			return {
173
+				dataList: [],
174
+				range: [],
175
+				contentText: {
176
+					contentdown: '查看更多',
177
+					contentrefresh: '加载中',
178
+					contentnomore: '没有更多'
179
+				},
180
+				isLocal:true,
181
+				styles: {
182
+					selectedColor: '#2979ff',
183
+					selectedTextColor: '#666',
184
+				},
185
+				isTop:0
186
+			};
187
+		},
188
+		computed:{
189
+			dataValue(){
190
+				if(this.value === '')return this.modelValue
191
+				if(this.modelValue === '') return this.value
192
+				return this.value
193
+			}
194
+		},
195
+		created() {
196
+			this.form = this.getForm('uniForms')
197
+			this.formItem = this.getForm('uniFormsItem')
198
+			// this.formItem && this.formItem.setValue(this.value)
199
+
200
+			if (this.formItem) {
201
+				this.isTop = 6
202
+				if (this.formItem.name) {
203
+					// 如果存在name添加默认值,否则formData 中不存在这个字段不校验
204
+					if(!this.is_reset){
205
+						this.is_reset = false
206
+						this.formItem.setValue(this.dataValue)
207
+					}
208
+					this.rename = this.formItem.name
209
+					this.form.inputChildrens.push(this)
210
+				}
211
+			}
212
+
213
+			if (this.localdata && this.localdata.length !== 0) {
214
+				this.isLocal = true
215
+				this.range = this.localdata
216
+				this.dataList = this.getDataList(this.getSelectedValue(this.range))
217
+			} else {
218
+				if (this.collection) {
219
+					this.isLocal = false
220
+					this.loadData()
221
+				}
222
+			}
223
+		},
224
+		methods: {
225
+			loadData() {
226
+				this.mixinDatacomGet().then(res=>{
227
+					this.mixinDatacomResData = res.result.data
228
+					if(this.mixinDatacomResData.length === 0){
229
+						this.isLocal = false
230
+						this.mixinDatacomErrorMessage = this.emptyText
231
+					}else{
232
+						this.isLocal = true
233
+					}
234
+				}).catch(err=>{
235
+					this.mixinDatacomErrorMessage = err.message
236
+				})
237
+			},
238
+			/**
239
+			 * 获取父元素实例
240
+			 */
241
+			getForm(name = 'uniForms') {
242
+				let parent = this.$parent;
243
+				let parentName = parent.$options.name;
244
+				while (parentName !== name) {
245
+					parent = parent.$parent;
246
+					if (!parent) return false
247
+					parentName = parent.$options.name;
248
+				}
249
+				return parent;
250
+			},
251
+			chagne(e) {
252
+				const values = e.detail.value
253
+
254
+				let detail = {
255
+					value: [],
256
+					data: []
257
+				}
258
+
259
+				if (this.multiple) {
260
+					this.range.forEach(item => {
261
+
262
+						if (values.includes(item[this.map.value] + '')) {
263
+							detail.value.push(item[this.map.value])
264
+							detail.data.push(item)
265
+						}
266
+					})
267
+				} else {
268
+					const range = this.range.find(item => (item[this.map.value] + '') === values)
269
+					if (range) {
270
+						detail = {
271
+							value: range[this.map.value],
272
+							data: range
273
+						}
274
+					}
275
+				}
276
+				this.formItem && this.formItem.setValue(detail.value)
277
+				// TODO 兼容 vue2
278
+				this.$emit('input', detail.value);
279
+				// // TOTO 兼容 vue3
280
+				this.$emit('update:modelValue', detail.value);
281
+				this.$emit('change', {
282
+					detail
283
+				})
284
+				if (this.multiple) {
285
+					// 如果 v-model 没有绑定 ,则走内部逻辑
286
+					// if (this.value.length === 0) {
287
+					this.dataList = this.getDataList(detail.value, true)
288
+					// }
289
+				} else {
290
+					this.dataList = this.getDataList(detail.value)
291
+				}
292
+			},
293
+
294
+			/**
295
+			 * 获取渲染的新数组
296
+			 * @param {Object} value 选中内容
297
+			 */
298
+			getDataList(value) {
299
+				// 解除引用关系,破坏原引用关系,避免污染源数据
300
+				let dataList = JSON.parse(JSON.stringify(this.range))
301
+				let list = []
302
+				if (this.multiple) {
303
+					if (!Array.isArray(value)) {
304
+						value = []
305
+					}
306
+				}
307
+				dataList.forEach((item, index) => {
308
+					item.disabled = item.disable || item.disabled || false
309
+					if (this.multiple) {
310
+						if (value.length > 0) {
311
+							let have = value.find(val => val === item[this.map.value])
312
+							item.selected = have !== undefined
313
+						} else {
314
+							item.selected = false
315
+						}
316
+					} else {
317
+						item.selected = value === item[this.map.value]
318
+					}
319
+
320
+					list.push(item)
321
+				})
322
+				return this.setRange(list)
323
+			},
324
+			/**
325
+			 * 处理最大最小值
326
+			 * @param {Object} list
327
+			 */
328
+			setRange(list) {
329
+				let selectList = list.filter(item => item.selected)
330
+				let min = Number(this.min) || 0
331
+				let max = Number(this.max) || ''
332
+				list.forEach((item, index) => {
333
+					if (this.multiple) {
334
+						if (selectList.length <= min) {
335
+							let have = selectList.find(val => val[this.map.value] === item[this.map.value])
336
+							if (have !== undefined) {
337
+								item.disabled = true
338
+							}
339
+						}
340
+
341
+						if (selectList.length >= max && max !== '') {
342
+							let have = selectList.find(val => val[this.map.value] === item[this.map.value])
343
+							if (have === undefined) {
344
+								item.disabled = true
345
+							}
346
+						}
347
+					}
348
+					this.setStyles(item, index)
349
+					list[index] = item
350
+				})
351
+				return list
352
+			},
353
+			/**
354
+			 * 设置 class
355
+			 * @param {Object} item
356
+			 * @param {Object} index
357
+			 */
358
+			setStyles(item, index) {
359
+				//  设置自定义样式
360
+				item.styleBackgroud = this.setStyleBackgroud(item)
361
+				item.styleIcon = this.setStyleIcon(item)
362
+				item.styleIconText = this.setStyleIconText(item)
363
+				item.styleRightIcon = this.setStyleRightIcon(item)
364
+			},
365
+
366
+			/**
367
+			 * 获取选中值
368
+			 * @param {Object} range
369
+			 */
370
+			getSelectedValue(range) {
371
+				if (!this.multiple) return this.dataValue
372
+				let selectedArr = []
373
+				range.forEach((item) => {
374
+					if (item.selected) {
375
+						selectedArr.push(item[this.map.value])
376
+					}
377
+				})
378
+				return this.dataValue && this.dataValue.length > 0 ? this.dataValue : selectedArr
379
+			},
380
+
381
+			/**
382
+			 * 设置背景样式
383
+			 */
384
+			setStyleBackgroud(item) {
385
+				let styles = {}
386
+				let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
387
+				if (this.mode !== 'list') {
388
+					styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
389
+				}
390
+				if (this.mode === 'tag') {
391
+					styles['background-color'] = item.selected? selectedColor:'#f5f5f5'
392
+				}
393
+				let classles = ''
394
+				for (let i in styles) {
395
+					classles += `${i}:${styles[i]};`
396
+				}
397
+				return classles
398
+			},
399
+			setStyleIcon(item) {
400
+				let styles = {}
401
+				let classles = ''
402
+				let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
403
+				styles['background-color'] = item.selected?selectedColor:'#fff'
404
+				styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
405
+
406
+				if(!item.selected && item.disabled){
407
+					styles['background-color'] = '#F2F6FC'
408
+					styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
409
+				}
410
+
411
+				for (let i in styles) {
412
+					classles += `${i}:${styles[i]};`
413
+				}
414
+				return classles
415
+			},
416
+			setStyleIconText(item) {
417
+				let styles = {}
418
+				let classles = ''
419
+				let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
420
+				if (this.mode === 'tag') {
421
+					styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666'
422
+				} else {
423
+					styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666'
424
+				}
425
+				if(!item.selected && item.disabled){
426
+					styles.color = '#999'
427
+				}
428
+
429
+				for (let i in styles) {
430
+					classles += `${i}:${styles[i]};`
431
+				}
432
+				return classles
433
+			},
434
+			setStyleRightIcon(item) {
435
+				let styles = {}
436
+				let classles = ''
437
+				if (this.mode === 'list') {
438
+					styles['border-color'] = item.selected?this.styles.selectedColor:'#DCDFE6'
439
+				}
440
+				for (let i in styles) {
441
+					classles += `${i}:${styles[i]};`
442
+				}
443
+
444
+				return classles
445
+			}
446
+		}
447
+	}
448
+</script>
449
+
450
+<style lang="scss">
451
+	$checked-color: #2979ff;
452
+	$border-color: #DCDFE6;
453
+	$disable:0.4;
454
+
455
+	@mixin flex {
456
+		/* #ifndef APP-NVUE */
457
+		display: flex;
458
+		/* #endif */
459
+	}
460
+
461
+	.uni-data-loading {
462
+		@include flex;
463
+		flex-direction: row;
464
+		justify-content: center;
465
+		align-items: center;
466
+		height: 36px;
467
+		padding-left: 10px;
468
+		color: #999;
469
+	}
470
+
471
+	.uni-data-checklist {
472
+		position: relative;
473
+		z-index: 0;
474
+		flex: 1;
475
+		// 多选样式
476
+		.checklist-group {
477
+			@include flex;
478
+			flex-direction: row;
479
+			flex-wrap: wrap;
480
+
481
+			&.is-list {
482
+				flex-direction: column;
483
+			}
484
+
485
+			.checklist-box {
486
+				@include flex;
487
+				flex-direction: row;
488
+				align-items: center;
489
+				position: relative;
490
+				margin: 5px 0;
491
+				margin-right: 25px;
492
+
493
+				.hidden {
494
+					position: absolute;
495
+					opacity: 0;
496
+				}
497
+
498
+				// 文字样式
499
+				.checklist-content {
500
+					@include flex;
501
+					flex: 1;
502
+					flex-direction: row;
503
+					align-items: center;
504
+					justify-content: space-between;
505
+					.checklist-text {
506
+						font-size: 14px;
507
+						color: #666;
508
+						margin-left: 5px;
509
+						line-height: 14px;
510
+					}
511
+
512
+					.checkobx__list {
513
+						border-right-width: 1px;
514
+						border-right-color: #007aff;
515
+						border-right-style: solid;
516
+						border-bottom-width:1px;
517
+						border-bottom-color: #007aff;
518
+						border-bottom-style: solid;
519
+						height: 12px;
520
+						width: 6px;
521
+						left: -5px;
522
+						transform-origin: center;
523
+						transform: rotate(45deg);
524
+						opacity: 0;
525
+					}
526
+				}
527
+
528
+				// 多选样式
529
+				.checkbox__inner {
530
+					/* #ifndef APP-NVUE */
531
+					flex-shrink: 0;
532
+					box-sizing: border-box;
533
+					/* #endif */
534
+					position: relative;
535
+					width: 16px;
536
+					height: 16px;
537
+					border: 1px solid $border-color;
538
+					border-radius: 4px;
539
+					background-color: #fff;
540
+					z-index: 1;
541
+					.checkbox__inner-icon {
542
+						position: absolute;
543
+						/* #ifdef APP-NVUE */
544
+						top: 2px;
545
+						/* #endif */
546
+						/* #ifndef APP-NVUE */
547
+						top: 1px;
548
+						/* #endif */
549
+						left: 5px;
550
+						height: 8px;
551
+						width: 4px;
552
+						border-right-width: 1px;
553
+						border-right-color: #fff;
554
+						border-right-style: solid;
555
+						border-bottom-width:1px ;
556
+						border-bottom-color: #fff;
557
+						border-bottom-style: solid;
558
+						opacity: 0;
559
+						transform-origin: center;
560
+						transform: rotate(40deg);
561
+					}
562
+				}
563
+
564
+				// 单选样式
565
+				.radio__inner {
566
+					@include flex;
567
+					/* #ifndef APP-NVUE */
568
+					flex-shrink: 0;
569
+					box-sizing: border-box;
570
+					/* #endif */
571
+					justify-content: center;
572
+					align-items: center;
573
+					position: relative;
574
+					width: 16px;
575
+					height: 16px;
576
+					border: 1px solid $border-color;
577
+					border-radius: 16px;
578
+					background-color: #fff;
579
+					z-index: 1;
580
+
581
+					.radio__inner-icon {
582
+						width: 8px;
583
+						height: 8px;
584
+						border-radius: 10px;
585
+						opacity: 0;
586
+					}
587
+				}
588
+
589
+				// 默认样式
590
+				&.is--default {
591
+
592
+					// 禁用
593
+					&.is-disable {
594
+						/* #ifdef H5 */
595
+						cursor: not-allowed;
596
+						/* #endif */
597
+						.checkbox__inner {
598
+							background-color: #F2F6FC;
599
+							border-color: $border-color;
600
+							/* #ifdef H5 */
601
+							cursor: not-allowed;
602
+							/* #endif */
603
+						}
604
+
605
+						.radio__inner {
606
+							background-color: #F2F6FC;
607
+							border-color: $border-color;
608
+						}
609
+						.checklist-text {
610
+							color: #999;
611
+						}
612
+					}
613
+
614
+					// 选中
615
+					&.is-checked {
616
+						.checkbox__inner {
617
+							border-color: $checked-color;
618
+							background-color: $checked-color;
619
+
620
+							.checkbox__inner-icon {
621
+								opacity: 1;
622
+								transform: rotate(45deg);
623
+							}
624
+						}
625
+						.radio__inner {
626
+							border-color: $checked-color;
627
+							.radio__inner-icon {
628
+								opacity: 1;
629
+								background-color: $checked-color;
630
+							}
631
+						}
632
+						.checklist-text {
633
+							color: $checked-color;
634
+						}
635
+						// 选中禁用
636
+						&.is-disable {
637
+							.checkbox__inner {
638
+								opacity: $disable;
639
+							}
640
+
641
+							.checklist-text {
642
+								opacity: $disable;
643
+							}
644
+							.radio__inner {
645
+								opacity: $disable;
646
+							}
647
+						}
648
+					}
649
+				}
650
+
651
+				// 按钮样式
652
+				&.is--button {
653
+					margin-right: 10px;
654
+					padding: 5px 10px;
655
+					border: 1px $border-color solid;
656
+					border-radius: 3px;
657
+					transition: border-color 0.2s;
658
+
659
+					// 禁用
660
+					&.is-disable {
661
+						/* #ifdef H5 */
662
+						cursor: not-allowed;
663
+						/* #endif */
664
+						border: 1px #eee solid;
665
+						opacity: $disable;
666
+						.checkbox__inner {
667
+							background-color: #F2F6FC;
668
+							border-color: $border-color;
669
+							/* #ifdef H5 */
670
+							cursor: not-allowed;
671
+							/* #endif */
672
+						}
673
+						.radio__inner {
674
+							background-color: #F2F6FC;
675
+							border-color: $border-color;
676
+							/* #ifdef H5 */
677
+							cursor: not-allowed;
678
+							/* #endif */
679
+						}
680
+						.checklist-text {
681
+							color: #999;
682
+						}
683
+					}
684
+
685
+					&.is-checked {
686
+						border-color: $checked-color;
687
+						.checkbox__inner {
688
+							border-color: $checked-color;
689
+							background-color: $checked-color;
690
+							.checkbox__inner-icon {
691
+								opacity: 1;
692
+								transform: rotate(45deg);
693
+							}
694
+						}
695
+
696
+						.radio__inner {
697
+							border-color: $checked-color;
698
+
699
+							.radio__inner-icon {
700
+								opacity: 1;
701
+								background-color: $checked-color;
702
+							}
703
+						}
704
+
705
+						.checklist-text {
706
+							color: $checked-color;
707
+						}
708
+
709
+						// 选中禁用
710
+						&.is-disable {
711
+							opacity: $disable;
712
+						}
713
+					}
714
+				}
715
+
716
+				// 标签样式
717
+				&.is--tag {
718
+					margin-right: 10px;
719
+					padding: 5px 10px;
720
+					border: 1px $border-color solid;
721
+					border-radius: 3px;
722
+					background-color: #f5f5f5;
723
+
724
+					.checklist-text {
725
+						margin: 0;
726
+						color: #666;
727
+					}
728
+
729
+					// 禁用
730
+					&.is-disable {
731
+						/* #ifdef H5 */
732
+						cursor: not-allowed;
733
+						/* #endif */
734
+						opacity: $disable;
735
+					}
736
+
737
+					&.is-checked {
738
+						background-color: $checked-color;
739
+						border-color: $checked-color;
740
+
741
+						.checklist-text {
742
+							color: #fff;
743
+						}
744
+					}
745
+				}
746
+				// 列表样式
747
+				&.is--list {
748
+					/* #ifndef APP-NVUE */
749
+					display: flex;
750
+					/* #endif */
751
+					padding: 10px 15px;
752
+					padding-left: 0;
753
+					margin: 0;
754
+
755
+					&.is-list-border {
756
+						border-top: 1px #eee solid;
757
+					}
758
+
759
+					// 禁用
760
+					&.is-disable {
761
+						/* #ifdef H5 */
762
+						cursor: not-allowed;
763
+						/* #endif */
764
+						.checkbox__inner {
765
+							background-color: #F2F6FC;
766
+							border-color: $border-color;
767
+							/* #ifdef H5 */
768
+							cursor: not-allowed;
769
+							/* #endif */
770
+						}
771
+						.checklist-text {
772
+							color: #999;
773
+						}
774
+					}
775
+
776
+					&.is-checked {
777
+						.checkbox__inner {
778
+							border-color: $checked-color;
779
+							background-color: $checked-color;
780
+
781
+							.checkbox__inner-icon {
782
+								opacity: 1;
783
+								transform: rotate(45deg);
784
+							}
785
+						}
786
+						.radio__inner {
787
+							.radio__inner-icon {
788
+								opacity: 1;
789
+							}
790
+						}
791
+						.checklist-text {
792
+							color: $checked-color;
793
+						}
794
+
795
+						.checklist-content {
796
+							.checkobx__list {
797
+								opacity: 1;
798
+								border-color: $checked-color;
799
+							}
800
+						}
801
+
802
+						// 选中禁用
803
+						&.is-disable {
804
+							.checkbox__inner {
805
+								opacity: $disable;
806
+							}
807
+
808
+							.checklist-text {
809
+								opacity: $disable;
810
+							}
811
+						}
812
+					}
813
+				}
814
+			}
815
+		}
816
+	}
817
+</style>

+ 87 - 0
uni_modules/uni-data-checkbox/package.json

@@ -0,0 +1,87 @@
1
+{
2
+  "id": "uni-data-checkbox",
3
+  "displayName": "uni-data-checkbox 数据选择器",
4
+  "version": "1.0.1",
5
+  "description": "通过数据驱动的单选框和复选框",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "checkbox",
9
+    "单选",
10
+    "多选",
11
+    "单选多选"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": "^3.1.1"
16
+  },
17
+  "directories": {
18
+    "example": "../../temps/example_temps"
19
+  },
20
+  "dcloudext": {
21
+    "category": [
22
+      "前端组件",
23
+      "通用组件"
24
+    ],
25
+    "sale": {
26
+      "regular": {
27
+        "price": "0.00"
28
+      },
29
+      "sourcecode": {
30
+        "price": "0.00"
31
+      }
32
+    },
33
+    "contact": {
34
+      "qq": ""
35
+    },
36
+    "declaration": {
37
+      "ads": "无",
38
+      "data": "无",
39
+      "permissions": "无"
40
+    },
41
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
42
+  },
43
+  "uni_modules": {
44
+    "dependencies": ["uni-load-more","uni-scss"],
45
+    "encrypt": [],
46
+    "platforms": {
47
+      "cloud": {
48
+        "tcb": "y",
49
+        "aliyun": "y"
50
+      },
51
+      "client": {
52
+        "App": {
53
+          "app-vue": "y",
54
+          "app-nvue": "y"
55
+        },
56
+        "H5-mobile": {
57
+          "Safari": "y",
58
+          "Android Browser": "y",
59
+          "微信浏览器(Android)": "y",
60
+          "QQ浏览器(Android)": "y"
61
+        },
62
+        "H5-pc": {
63
+          "Chrome": "y",
64
+          "IE": "y",
65
+          "Edge": "y",
66
+          "Firefox": "y",
67
+          "Safari": "y"
68
+        },
69
+        "小程序": {
70
+          "微信": "y",
71
+          "阿里": "y",
72
+          "百度": "y",
73
+          "字节跳动": "y",
74
+          "QQ": "y"
75
+        },
76
+        "快应用": {
77
+          "华为": "u",
78
+          "联盟": "u"
79
+        },
80
+        "Vue": {
81
+            "vue2": "y",
82
+            "vue3": "y"
83
+        }
84
+      }
85
+    }
86
+  }
87
+}

+ 18 - 0
uni_modules/uni-data-checkbox/readme.md

@@ -0,0 +1,18 @@
1
+
2
+
3
+## DataCheckbox 数据驱动的单选复选框
4
+> **组件名:uni-data-checkbox**
5
+> 代码块: `uDataCheckbox`
6
+
7
+
8
+本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括:
9
+
10
+1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能
11
+2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验
12
+3. 本组件合并了单选多选
13
+4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性
14
+
15
+在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data
16
+
17
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
18
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 52 - 0
uni_modules/uni-data-picker/changelog.md

@@ -0,0 +1,52 @@
1
+## 1.0.1(2021-11-23)
2
+- 修复 由上个版本引发的map、v-model等属性不生效的bug
3
+## 1.0.0(2021-11-19)
4
+- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
5
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
6
+## 0.4.9(2021-10-28)
7
+- 修复 VUE2 v-model 概率无效的 bug
8
+## 0.4.8(2021-10-27)
9
+- 修复 v-model 概率无效的 bug
10
+## 0.4.7(2021-10-25)
11
+- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
12
+- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
13
+## 0.4.6(2021-10-19)
14
+- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
15
+## 0.4.5(2021-09-26)
16
+- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
17
+- 修复 readonly 为 true 时报错的 bug
18
+## 0.4.4(2021-09-26)
19
+- 修复 上一版本造成的 map 属性失效的 bug
20
+- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
21
+## 0.4.3(2021-09-24)
22
+- 修复 某些情况下级联未触发的 bug
23
+## 0.4.2(2021-09-23)
24
+- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
25
+- 新增 选项内容过长自动添加省略号
26
+## 0.4.1(2021-09-15)
27
+- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
28
+## 0.4.0(2021-07-13)
29
+- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
30
+## 0.3.5(2021-06-04)
31
+- 修复 无法加载云端数据的问题
32
+## 0.3.4(2021-05-28)
33
+- 修复 v-model 无效问题
34
+- 修复 loaddata 为空数据组时加载时间过长问题
35
+- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
36
+## 0.3.3(2021-05-12)
37
+- 新增 组件示例地址
38
+## 0.3.2(2021-04-22)
39
+- 修复 非树形数据有 where 属性查询报错的问题
40
+## 0.3.1(2021-04-15)
41
+- 修复 本地数据概率无法回显时问题
42
+## 0.3.0(2021-04-07)
43
+- 新增 支持云端非树形表结构数据
44
+- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
45
+## 0.2.0(2021-03-15)
46
+- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
47
+## 0.1.9(2021-03-09)
48
+- 修复 微信小程序某些情况下无法选择的问题
49
+## 0.1.8(2021-02-05)
50
+- 优化 部分样式在 nvue 上的兼容表现
51
+## 0.1.7(2021-02-05)
52
+- 调整为 uni_modules 目录规范

+ 45 - 0
uni_modules/uni-data-picker/components/uni-data-picker/keypress.js

@@ -0,0 +1,45 @@
1
+// #ifdef H5
2
+export default {
3
+  name: 'Keypress',
4
+  props: {
5
+    disable: {
6
+      type: Boolean,
7
+      default: false
8
+    }
9
+  },
10
+  mounted () {
11
+    const keyNames = {
12
+      esc: ['Esc', 'Escape'],
13
+      tab: 'Tab',
14
+      enter: 'Enter',
15
+      space: [' ', 'Spacebar'],
16
+      up: ['Up', 'ArrowUp'],
17
+      left: ['Left', 'ArrowLeft'],
18
+      right: ['Right', 'ArrowRight'],
19
+      down: ['Down', 'ArrowDown'],
20
+      delete: ['Backspace', 'Delete', 'Del']
21
+    }
22
+    const listener = ($event) => {
23
+      if (this.disable) {
24
+        return
25
+      }
26
+      const keyName = Object.keys(keyNames).find(key => {
27
+        const keyName = $event.key
28
+        const value = keyNames[key]
29
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
30
+      })
31
+      if (keyName) {
32
+        // 避免和其他按键事件冲突
33
+        setTimeout(() => {
34
+          this.$emit(keyName, {})
35
+        }, 0)
36
+      }
37
+    }
38
+    document.addEventListener('keyup', listener)
39
+    this.$once('hook:beforeDestroy', () => {
40
+      document.removeEventListener('keyup', listener)
41
+    })
42
+  },
43
+	render: () => {}
44
+}
45
+// #endif

+ 537 - 0
uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue

@@ -0,0 +1,537 @@
1
+<template>
2
+	<view class="uni-data-tree">
3
+		<view class="uni-data-tree-input" @click="handleInput">
4
+			<slot :options="options" :data="inputSelected" :error="errorMessage">
5
+				<view class="input-value" :class="{'input-value-border': border}">
6
+					<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text>
7
+					<view v-else-if="loading && !isOpened" class="selected-area">
8
+						<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
9
+					</view>
10
+					<scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true">
11
+						<view class="selected-list">
12
+							<view class="selected-item" v-for="(item,index) in inputSelected" :key="index">
13
+								<text>{{item.text}}</text><text v-if="index<inputSelected.length-1"
14
+									class="input-split-line">{{split}}</text>
15
+							</view>
16
+						</view>
17
+					</scroll-view>
18
+					<text v-else class="selected-area placeholder">{{placeholder}}</text>
19
+					<view v-show="clearIcon && !readonly && inputSelected.length" class="icon-clear"
20
+						@click.stop="clear">
21
+						<uni-icons type="clear" color="#e1e1e1" size="14"></uni-icons>
22
+					</view>
23
+					<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly ">
24
+						<view class="input-arrow"></view>
25
+					</view>
26
+				</view>
27
+			</slot>
28
+		</view>
29
+		<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
30
+		<view class="uni-data-tree-dialog" v-if="isOpened">
31
+			<view class="uni-popper__arrow"></view>
32
+			<view class="dialog-caption">
33
+				<view class="title-area">
34
+					<text class="dialog-title">{{popupTitle}}</text>
35
+				</view>
36
+				<view class="dialog-close" @click="handleClose">
37
+					<view class="dialog-close-plus" data-id="close"></view>
38
+					<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
39
+				</view>
40
+			</view>
41
+			<data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata"
42
+				:preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where"
43
+				:step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true"
44
+				:map="map" :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick">
45
+			</data-picker-view>
46
+		</view>
47
+	</view>
48
+</template>
49
+
50
+<script>
51
+	import dataPicker from "../uni-data-pickerview/uni-data-picker.js"
52
+	import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue"
53
+
54
+	/**
55
+	 * DataPicker 级联选择
56
+	 * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
57
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3796
58
+	 * @property {String} popup-title 弹出窗口标题
59
+	 * @property {Array} localdata 本地数据,参考
60
+	 * @property {Boolean} border = [true|false] 是否有边框
61
+	 * @property {Boolean} readonly = [true|false] 是否仅读
62
+	 * @property {Boolean} preload = [true|false] 是否预加载数据
63
+	 * @value true 开启预加载数据,点击弹出窗口后显示已加载数据
64
+	 * @value false 关闭预加载数据,点击弹出窗口后开始加载数据
65
+	 * @property {Boolean} step-searh = [true|false] 是否分布查询
66
+	 * @value true 启用分布查询,仅查询当前选中节点
67
+	 * @value false 关闭分布查询,一次查询出所有数据
68
+	 * @property {String|DBFieldString} self-field 分布查询当前字段名称
69
+	 * @property {String|DBFieldString} parent-field 分布查询父字段名称
70
+	 * @property {String|DBCollectionString} collection 表名
71
+	 * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
72
+	 * @property {String} orderby 排序字段及正序倒叙设置
73
+	 * @property {String|JQLString} where 查询条件
74
+	 * @event {Function} popupshow 弹出的选择窗口打开时触发此事件
75
+	 * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
76
+	 */
77
+	export default {
78
+		name: 'UniDataPicker',
79
+		emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue'],
80
+		mixins: [dataPicker],
81
+		components: {
82
+			DataPickerView
83
+		},
84
+		props: {
85
+			options: {
86
+				type: [Object, Array],
87
+				default () {
88
+					return {}
89
+				}
90
+			},
91
+			popupTitle: {
92
+				type: String,
93
+				default: '请选择'
94
+			},
95
+			placeholder: {
96
+				type: String,
97
+				default: '请选择'
98
+			},
99
+			heightMobile: {
100
+				type: String,
101
+				default: ''
102
+			},
103
+			readonly: {
104
+				type: Boolean,
105
+				default: false
106
+			},
107
+			clearIcon: {
108
+				type: Boolean,
109
+				default: true
110
+			},
111
+			border: {
112
+				type: Boolean,
113
+				default: true
114
+			},
115
+			split: {
116
+				type: String,
117
+				default: '/'
118
+			},
119
+			ellipsis: {
120
+				type: Boolean,
121
+				default: true
122
+			}
123
+		},
124
+		data() {
125
+			return {
126
+				isOpened: false,
127
+				inputSelected: []
128
+			}
129
+		},
130
+		created() {
131
+			this.form = this.getForm('uniForms')
132
+			this.formItem = this.getForm('uniFormsItem')
133
+			if (this.formItem) {
134
+				if (this.formItem.name) {
135
+					this.rename = this.formItem.name
136
+					this.form.inputChildrens.push(this)
137
+				}
138
+			}
139
+
140
+			this.$nextTick(() => {
141
+				this.load()
142
+			})
143
+		},
144
+		methods: {
145
+			clear() {
146
+				this.inputSelected.splice(0)
147
+				this._dispatchEvent([])
148
+			},
149
+			onPropsChange() {
150
+				this._treeData = []
151
+				this.selectedIndex = 0
152
+				this.load()
153
+			},
154
+			load() {
155
+				if (this.readonly) {
156
+					this._processReadonly(this.localdata, this.dataValue)
157
+					return
158
+				}
159
+
160
+				if (this.isLocaldata) {
161
+					this.loadData()
162
+					this.inputSelected = this.selected.slice(0)
163
+				} else if (!this.parentField && !this.selfField && this.hasValue) {
164
+					this.getNodeData(() => {
165
+						this.inputSelected = this.selected.slice(0)
166
+					})
167
+				} else if (this.hasValue) {
168
+					this.getTreePath(() => {
169
+						this.inputSelected = this.selected.slice(0)
170
+					})
171
+				}
172
+			},
173
+			getForm(name = 'uniForms') {
174
+				let parent = this.$parent;
175
+				let parentName = parent.$options.name;
176
+				while (parentName !== name) {
177
+					parent = parent.$parent;
178
+					if (!parent) return false;
179
+					parentName = parent.$options.name;
180
+				}
181
+				return parent;
182
+			},
183
+			show() {
184
+				this.isOpened = true
185
+				this.$nextTick(() => {
186
+					this.$refs.pickerView.updateData({
187
+						treeData: this._treeData,
188
+						selected: this.selected,
189
+						selectedIndex: this.selectedIndex
190
+					})
191
+				})
192
+				this.$emit('popupopened')
193
+			},
194
+			hide() {
195
+				this.isOpened = false
196
+				this.$emit('popupclosed')
197
+			},
198
+			handleInput() {
199
+				if (this.readonly) {
200
+					return
201
+				}
202
+				this.show()
203
+			},
204
+			handleClose(e) {
205
+				this.hide()
206
+			},
207
+			onnodeclick(e) {
208
+				this.$emit('nodeclick', e)
209
+			},
210
+			ondatachange(e) {
211
+				this._treeData = this.$refs.pickerView._treeData
212
+			},
213
+			onchange(e) {
214
+				this.hide()
215
+				this.inputSelected = e
216
+				this._dispatchEvent(e)
217
+			},
218
+			_processReadonly(dataList, value) {
219
+				var isTree = dataList.findIndex((item) => {
220
+					return item.children
221
+				})
222
+				if (isTree > -1) {
223
+					let inputValue
224
+					if (Array.isArray(value)) {
225
+						inputValue = value[value.length - 1]
226
+						if (typeof inputValue === 'object' && inputValue.value) {
227
+							inputValue = inputValue.value
228
+						}
229
+					} else {
230
+						inputValue = value
231
+					}
232
+					this.inputSelected = this._findNodePath(inputValue, this.localdata)
233
+					return
234
+				}
235
+
236
+				if (!this.hasValue) {
237
+					this.inputSelected = []
238
+					return
239
+				}
240
+
241
+				let result = []
242
+				for (let i = 0; i < value.length; i++) {
243
+					var val = value[i]
244
+					var item = dataList.find((v) => {
245
+						return v.value == val
246
+					})
247
+					if (item) {
248
+						result.push(item)
249
+					}
250
+				}
251
+				if (result.length) {
252
+					this.inputSelected = result
253
+				}
254
+			},
255
+			_filterForArray(data, valueArray) {
256
+				var result = []
257
+				for (let i = 0; i < valueArray.length; i++) {
258
+					var value = valueArray[i]
259
+					var found = data.find((item) => {
260
+						return item.value == value
261
+					})
262
+					if (found) {
263
+						result.push(found)
264
+					}
265
+				}
266
+				return result
267
+			},
268
+			_dispatchEvent(selected) {
269
+				let item = {}
270
+				if (selected.length) {
271
+					var value = new Array(selected.length)
272
+					for (var i = 0; i < selected.length; i++) {
273
+						value[i] = selected[i].value
274
+					}
275
+					item = selected[selected.length - 1]
276
+				} else {
277
+					item.value = ''
278
+				}
279
+				if (this.formItem) {
280
+					this.formItem.setValue(item.value)
281
+				}
282
+
283
+				this.$emit('input', item.value)
284
+				this.$emit('update:modelValue', item.value)
285
+				this.$emit('change', {
286
+					detail: {
287
+						value: selected
288
+					}
289
+				})
290
+			}
291
+		}
292
+	}
293
+</script>
294
+
295
+<style >
296
+	.uni-data-tree {
297
+		position: relative;
298
+		font-size: 14px;
299
+	}
300
+
301
+	.error-text {
302
+		color: #DD524D;
303
+	}
304
+
305
+	.input-value {
306
+		/* #ifndef APP-NVUE */
307
+		display: flex;
308
+		/* #endif */
309
+		flex-direction: row;
310
+		align-items: center;
311
+		flex-wrap: nowrap;
312
+		font-size: 14px;
313
+		line-height: 38px;
314
+		padding: 0 5px;
315
+		overflow: hidden;
316
+		/* #ifdef APP-NVUE */
317
+		height: 40px;
318
+		/* #endif */
319
+	}
320
+
321
+	.input-value-border {
322
+		border: 1px solid #e5e5e5;
323
+		border-radius: 5px;
324
+	}
325
+
326
+	.selected-area {
327
+		flex: 1;
328
+		overflow: hidden;
329
+		/* #ifndef APP-NVUE */
330
+		display: flex;
331
+		/* #endif */
332
+		flex-direction: row;
333
+	}
334
+
335
+	.load-more {
336
+		/* #ifndef APP-NVUE */
337
+		margin-right: auto;
338
+		/* #endif */
339
+		/* #ifdef APP-NVUE */
340
+		width: 40px;
341
+		/* #endif */
342
+	}
343
+
344
+	.selected-list {
345
+		/* #ifndef APP-NVUE */
346
+		display: flex;
347
+		/* #endif */
348
+		flex-direction: row;
349
+		flex-wrap: nowrap;
350
+		padding: 0 5px;
351
+	}
352
+
353
+	.selected-item {
354
+		flex-direction: row;
355
+		padding: 0 1px;
356
+		/* #ifndef APP-NVUE */
357
+		white-space: nowrap;
358
+		/* #endif */
359
+	}
360
+
361
+	.placeholder {
362
+		color: grey;
363
+	}
364
+
365
+	.input-split-line {
366
+		opacity: .5;
367
+	}
368
+
369
+	.arrow-area {
370
+		position: relative;
371
+		width: 20px;
372
+		/* #ifndef APP-NVUE */
373
+		margin-bottom: 5px;
374
+		margin-left: auto;
375
+		display: flex;
376
+		/* #endif */
377
+		justify-content: center;
378
+		transform: rotate(-45deg);
379
+		transform-origin: center;
380
+	}
381
+
382
+	.input-arrow {
383
+		width: 7px;
384
+		height: 7px;
385
+		border-left: 1px solid #999;
386
+		border-bottom: 1px solid #999;
387
+	}
388
+
389
+	.uni-data-tree-cover {
390
+		position: fixed;
391
+		left: 0;
392
+		top: 0;
393
+		right: 0;
394
+		bottom: 0;
395
+		background-color: rgba(0, 0, 0, .4);
396
+		/* #ifndef APP-NVUE */
397
+		display: flex;
398
+		/* #endif */
399
+		flex-direction: column;
400
+		z-index: 100;
401
+	}
402
+
403
+	.uni-data-tree-dialog {
404
+		position: fixed;
405
+		left: 0;
406
+		top: 20%;
407
+		right: 0;
408
+		bottom: 0;
409
+		background-color: #FFFFFF;
410
+		border-top-left-radius: 10px;
411
+		border-top-right-radius: 10px;
412
+		/* #ifndef APP-NVUE */
413
+		display: flex;
414
+		/* #endif */
415
+		flex-direction: column;
416
+		z-index: 102;
417
+		overflow: hidden;
418
+		/* #ifdef APP-NVUE */
419
+		width: 750rpx;
420
+		/* #endif */
421
+	}
422
+
423
+	.dialog-caption {
424
+		position: relative;
425
+		/* #ifndef APP-NVUE */
426
+		display: flex;
427
+		/* #endif */
428
+		flex-direction: row;
429
+		/* border-bottom: 1px solid #f0f0f0; */
430
+	}
431
+
432
+	.title-area {
433
+		/* #ifndef APP-NVUE */
434
+		display: flex;
435
+		/* #endif */
436
+		align-items: center;
437
+		/* #ifndef APP-NVUE */
438
+		margin: auto;
439
+		/* #endif */
440
+		padding: 0 10px;
441
+	}
442
+
443
+	.dialog-title {
444
+		/* font-weight: bold; */
445
+		line-height: 44px;
446
+	}
447
+
448
+	.dialog-close {
449
+		position: absolute;
450
+		top: 0;
451
+		right: 0;
452
+		bottom: 0;
453
+		/* #ifndef APP-NVUE */
454
+		display: flex;
455
+		/* #endif */
456
+		flex-direction: row;
457
+		align-items: center;
458
+		padding: 0 15px;
459
+	}
460
+
461
+	.dialog-close-plus {
462
+		width: 16px;
463
+		height: 2px;
464
+		background-color: #666;
465
+		border-radius: 2px;
466
+		transform: rotate(45deg);
467
+	}
468
+
469
+	.dialog-close-rotate {
470
+		position: absolute;
471
+		transform: rotate(-45deg);
472
+	}
473
+
474
+	.picker-view {
475
+		flex: 1;
476
+		overflow: hidden;
477
+	}
478
+
479
+	/* #ifdef H5 */
480
+	@media all and (min-width: 768px) {
481
+		.uni-data-tree-cover {
482
+			background-color: transparent;
483
+		}
484
+
485
+		.uni-data-tree-dialog {
486
+			position: absolute;
487
+			top: 55px;
488
+			height: auto;
489
+			min-height: 400px;
490
+			max-height: 50vh;
491
+			background-color: #fff;
492
+			border: 1px solid #EBEEF5;
493
+			box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
494
+			border-radius: 4px;
495
+			overflow: unset;
496
+		}
497
+
498
+		.dialog-caption {
499
+			display: none;
500
+		}
501
+
502
+		.icon-clear {
503
+			margin-right: 5px;
504
+		}
505
+	}
506
+
507
+	/* #endif */
508
+	
509
+	/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
510
+	.uni-popper__arrow,
511
+	.uni-popper__arrow::after {
512
+		position: absolute;
513
+		display: block;
514
+		width: 0;
515
+		height: 0;
516
+		border-color: transparent;
517
+		border-style: solid;
518
+		border-width: 6px;
519
+	}
520
+
521
+	.uni-popper__arrow {
522
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
523
+		top: -6px;
524
+		left: 10%;
525
+		margin-right: 3px;
526
+		border-top-width: 0;
527
+		border-bottom-color: #EBEEF5;
528
+	}
529
+
530
+	.uni-popper__arrow::after {
531
+		content: " ";
532
+		top: 1px;
533
+		margin-left: -6px;
534
+		border-top-width: 0;
535
+		border-bottom-color: #fff;
536
+	}
537
+	</style>

+ 563 - 0
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js

@@ -0,0 +1,563 @@
1
+export default {
2
+  props: {
3
+    localdata: {
4
+      type: [Array, Object],
5
+      default () {
6
+        return []
7
+      }
8
+    },
9
+    spaceInfo: {
10
+      type: Object,
11
+      default () {
12
+        return {}
13
+      }
14
+    },
15
+    collection: {
16
+      type: String,
17
+      default: ''
18
+    },
19
+    action: {
20
+      type: String,
21
+      default: ''
22
+    },
23
+    field: {
24
+      type: String,
25
+      default: ''
26
+    },
27
+    orderby: {
28
+      type: String,
29
+      default: ''
30
+    },
31
+    where: {
32
+      type: [String, Object],
33
+      default: ''
34
+    },
35
+    pageData: {
36
+      type: String,
37
+      default: 'add'
38
+    },
39
+    pageCurrent: {
40
+      type: Number,
41
+      default: 1
42
+    },
43
+    pageSize: {
44
+      type: Number,
45
+      default: 20
46
+    },
47
+    getcount: {
48
+      type: [Boolean, String],
49
+      default: false
50
+    },
51
+    getone: {
52
+      type: [Boolean, String],
53
+      default: false
54
+    },
55
+    gettree: {
56
+      type: [Boolean, String],
57
+      default: false
58
+    },
59
+    manual: {
60
+      type: Boolean,
61
+      default: false
62
+    },
63
+    value: {
64
+      type: [Array, String, Number],
65
+      default () {
66
+        return []
67
+      }
68
+    },
69
+    modelValue: {
70
+      type: [Array, String, Number],
71
+      default () {
72
+        return []
73
+      }
74
+    },
75
+    preload: {
76
+      type: Boolean,
77
+      default: false
78
+    },
79
+    stepSearh: {
80
+      type: Boolean,
81
+      default: true
82
+    },
83
+    selfField: {
84
+      type: String,
85
+      default: ''
86
+    },
87
+    parentField: {
88
+      type: String,
89
+      default: ''
90
+    },
91
+    multiple: {
92
+      type: Boolean,
93
+      default: false
94
+    },
95
+    map: {
96
+      type: Object,
97
+      default() {
98
+        return {
99
+          text: "text",
100
+          value: "value"
101
+        }
102
+      }
103
+    }
104
+  },
105
+  data() {
106
+    return {
107
+      loading: false,
108
+      errorMessage: '',
109
+      loadMore: {
110
+        contentdown: '',
111
+        contentrefresh: '',
112
+        contentnomore: ''
113
+      },
114
+      dataList: [],
115
+      selected: [],
116
+      selectedIndex: 0,
117
+      page: {
118
+        current: this.pageCurrent,
119
+        size: this.pageSize,
120
+        count: 0
121
+      }
122
+    }
123
+  },
124
+  computed: {
125
+    isLocaldata() {
126
+      return !this.collection.length
127
+    },
128
+    postField() {
129
+      let fields = [this.field];
130
+      if (this.parentField) {
131
+        fields.push(`${this.parentField} as parent_value`);
132
+      }
133
+      return fields.join(',');
134
+    },
135
+    dataValue() {
136
+      let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined)
137
+      return isModelValue ? this.modelValue : this.value
138
+    },
139
+    hasValue() {
140
+      if (typeof this.dataValue === 'number') {
141
+        return true
142
+      }
143
+      return (this.dataValue != null) && (this.dataValue.length > 0)
144
+    }
145
+  },
146
+  created() {
147
+    this.$watch(() => {
148
+      var al = [];
149
+      ['pageCurrent',
150
+        'pageSize',
151
+        'spaceInfo',
152
+        'value',
153
+        'modelValue',
154
+        'localdata',
155
+        'collection',
156
+        'action',
157
+        'field',
158
+        'orderby',
159
+        'where',
160
+        'getont',
161
+        'getcount',
162
+        'gettree'
163
+      ].forEach(key => {
164
+        al.push(this[key])
165
+      });
166
+      return al
167
+    }, (newValue, oldValue) => {
168
+      let needReset = false
169
+      for (let i = 2; i < newValue.length; i++) {
170
+        if (newValue[i] != oldValue[i]) {
171
+          needReset = true
172
+          break
173
+        }
174
+      }
175
+      if (newValue[0] != oldValue[0]) {
176
+        this.page.current = this.pageCurrent
177
+      }
178
+      this.page.size = this.pageSize
179
+
180
+      this.onPropsChange()
181
+    })
182
+    this._treeData = []
183
+  },
184
+  methods: {
185
+    onPropsChange() {
186
+      this._treeData = []
187
+    },
188
+    getCommand(options = {}) {
189
+      /* eslint-disable no-undef */
190
+      let db = uniCloud.database(this.spaceInfo)
191
+
192
+      const action = options.action || this.action
193
+      if (action) {
194
+        db = db.action(action)
195
+      }
196
+
197
+      const collection = options.collection || this.collection
198
+      db = db.collection(collection)
199
+
200
+      const where = options.where || this.where
201
+      if (!(!where || !Object.keys(where).length)) {
202
+        db = db.where(where)
203
+      }
204
+
205
+      const field = options.field || this.field
206
+      if (field) {
207
+        db = db.field(field)
208
+      }
209
+
210
+      const orderby = options.orderby || this.orderby
211
+      if (orderby) {
212
+        db = db.orderBy(orderby)
213
+      }
214
+
215
+      const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
216
+      const size = options.pageSize !== undefined ? options.pageSize : this.page.size
217
+      const getCount = options.getcount !== undefined ? options.getcount : this.getcount
218
+      const getTree = options.gettree !== undefined ? options.gettree : this.gettree
219
+
220
+      const getOptions = {
221
+        getCount,
222
+        getTree
223
+      }
224
+      if (options.getTreePath) {
225
+        getOptions.getTreePath = options.getTreePath
226
+      }
227
+
228
+      db = db.skip(size * (current - 1)).limit(size).get(getOptions)
229
+
230
+      return db
231
+    },
232
+		getNodeData(callback) {
233
+		  if (this.loading) {
234
+		    return
235
+		  }
236
+		  this.loading = true
237
+		  this.getCommand({
238
+		    field: this.postField,
239
+				where: this._pathWhere()
240
+		  }).then((res) => {
241
+		    this.loading = false
242
+		    this.selected = res.result.data
243
+		    callback && callback()
244
+		  }).catch((err) => {
245
+		    this.loading = false
246
+		    this.errorMessage = err
247
+		  })
248
+		},
249
+    getTreePath(callback) {
250
+      if (this.loading) {
251
+        return
252
+      }
253
+      this.loading = true
254
+
255
+      this.getCommand({
256
+        field: this.postField,
257
+        getTreePath: {
258
+          startWith: `${this.selfField}=='${this.dataValue}'`
259
+        }
260
+      }).then((res) => {
261
+        this.loading = false
262
+        let treePath = []
263
+        this._extractTreePath(res.result.data, treePath)
264
+        this.selected = treePath
265
+        callback && callback()
266
+      }).catch((err) => {
267
+        this.loading = false
268
+        this.errorMessage = err
269
+      })
270
+    },
271
+    loadData() {
272
+      if (this.isLocaldata) {
273
+        this._processLocalData()
274
+        return
275
+      }
276
+
277
+      if (this.dataValue != null) {
278
+        this._loadNodeData((data) => {
279
+          this._treeData = data
280
+          this._updateBindData()
281
+          this._updateSelected()
282
+        })
283
+        return
284
+      }
285
+
286
+      if (this.stepSearh) {
287
+        this._loadNodeData((data) => {
288
+          this._treeData = data
289
+          this._updateBindData()
290
+        })
291
+      } else {
292
+        this._loadAllData((data) => {
293
+          this._treeData = []
294
+          this._extractTree(data, this._treeData, null)
295
+          this._updateBindData()
296
+        })
297
+      }
298
+    },
299
+    _loadAllData(callback) {
300
+      if (this.loading) {
301
+        return
302
+      }
303
+      this.loading = true
304
+
305
+      this.getCommand({
306
+        field: this.postField,
307
+        gettree: true,
308
+        startwith: `${this.selfField}=='${this.dataValue}'`
309
+      }).then((res) => {
310
+        this.loading = false
311
+        callback(res.result.data)
312
+        this.onDataChange()
313
+      }).catch((err) => {
314
+        this.loading = false
315
+        this.errorMessage = err
316
+      })
317
+    },
318
+    _loadNodeData(callback, pw) {
319
+      if (this.loading) {
320
+        return
321
+      }
322
+      this.loading = true
323
+
324
+      this.getCommand({
325
+        field: this.postField,
326
+        where: pw || this._postWhere(),
327
+        pageSize: 500
328
+      }).then((res) => {
329
+        this.loading = false
330
+        callback(res.result.data)
331
+        this.onDataChange()
332
+      }).catch((err) => {
333
+        this.loading = false
334
+        this.errorMessage = err
335
+      })
336
+    },
337
+    _pathWhere() {
338
+      let result = []
339
+      let where_field = this._getParentNameByField();
340
+      if (where_field) {
341
+        result.push(`${where_field} == '${this.dataValue}'`)
342
+      }
343
+
344
+      if (this.where) {
345
+        return `(${this.where}) && (${result.join(' || ')})`
346
+      }
347
+
348
+      return result.join(' || ')
349
+    },
350
+    _postWhere() {
351
+      let result = []
352
+      let selected = this.selected
353
+      let parentField = this.parentField
354
+      if (parentField) {
355
+        result.push(`${parentField} == null || ${parentField} == ""`)
356
+      }
357
+      if (selected.length) {
358
+        for (var i = 0; i < selected.length - 1; i++) {
359
+          result.push(`${parentField} == '${selected[i].value}'`)
360
+        }
361
+      }
362
+
363
+      let where = []
364
+      if (this.where) {
365
+        where.push(`(${this.where})`)
366
+      }
367
+      if (result.length) {
368
+        where.push(`(${result.join(' || ')})`)
369
+      }
370
+
371
+      return where.join(' && ')
372
+    },
373
+    _nodeWhere() {
374
+      let result = []
375
+      let selected = this.selected
376
+      if (selected.length) {
377
+        result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`)
378
+      }
379
+
380
+      if (this.where) {
381
+        return `(${this.where}) && (${result.join(' || ')})`
382
+      }
383
+
384
+      return result.join(' || ')
385
+    },
386
+    _getParentNameByField() {
387
+      const fields = this.field.split(',');
388
+      let where_field = null;
389
+      for (let i = 0; i < fields.length; i++) {
390
+        const items = fields[i].split('as');
391
+        if (items.length < 2) {
392
+          continue;
393
+        }
394
+        if (items[1].trim() === 'value') {
395
+          where_field = items[0].trim();
396
+          break;
397
+        }
398
+      }
399
+      return where_field
400
+    },
401
+    _isTreeView() {
402
+      return (this.parentField && this.selfField)
403
+    },
404
+    _updateSelected() {
405
+      var dl = this.dataList
406
+      var sl = this.selected
407
+      let textField = this.map.text
408
+      let valueField = this.map.value
409
+      for (var i = 0; i < sl.length; i++) {
410
+        var value = sl[i].value
411
+        var dl2 = dl[i]
412
+        for (var j = 0; j < dl2.length; j++) {
413
+          var item2 = dl2[j]
414
+          if (item2[valueField] === value) {
415
+            sl[i].text = item2[textField]
416
+            break
417
+          }
418
+        }
419
+      }
420
+    },
421
+    _updateBindData(node) {
422
+      const {
423
+        dataList,
424
+        hasNodes
425
+      } = this._filterData(this._treeData, this.selected)
426
+
427
+      let isleaf = this._stepSearh === false && !hasNodes
428
+
429
+      if (node) {
430
+        node.isleaf = isleaf
431
+      }
432
+
433
+      this.dataList = dataList
434
+      this.selectedIndex = dataList.length - 1
435
+
436
+      if (!isleaf && this.selected.length < dataList.length) {
437
+        this.selected.push({
438
+          value: null,
439
+          text: "请选择"
440
+        })
441
+      }
442
+
443
+      return {
444
+        isleaf,
445
+        hasNodes
446
+      }
447
+    },
448
+    _filterData(data, paths) {
449
+      let dataList = []
450
+      let hasNodes = true
451
+
452
+      dataList.push(data.filter((item) => {
453
+        return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
454
+      }))
455
+      for (let i = 0; i < paths.length; i++) {
456
+        var value = paths[i].value
457
+        var nodes = data.filter((item) => {
458
+          return item.parent_value === value
459
+        })
460
+
461
+        if (nodes.length) {
462
+          dataList.push(nodes)
463
+        } else {
464
+          hasNodes = false
465
+        }
466
+      }
467
+
468
+      return {
469
+        dataList,
470
+        hasNodes
471
+      }
472
+    },
473
+    _extractTree(nodes, result, parent_value) {
474
+      let list = result || []
475
+      let valueField = this.map.value
476
+      for (let i = 0; i < nodes.length; i++) {
477
+        let node = nodes[i]
478
+
479
+        let child = {}
480
+        for (let key in node) {
481
+          if (key !== 'children') {
482
+            child[key] = node[key]
483
+          }
484
+        }
485
+        if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
486
+          child.parent_value = parent_value
487
+        }
488
+        result.push(child)
489
+
490
+        let children = node.children
491
+        if (children) {
492
+          this._extractTree(children, result, node[valueField])
493
+        }
494
+      }
495
+    },
496
+    _extractTreePath(nodes, result) {
497
+      let list = result || []
498
+      for (let i = 0; i < nodes.length; i++) {
499
+        let node = nodes[i]
500
+
501
+        let child = {}
502
+        for (let key in node) {
503
+          if (key !== 'children') {
504
+            child[key] = node[key]
505
+          }
506
+        }
507
+        result.push(child)
508
+
509
+        let children = node.children
510
+        if (children) {
511
+          this._extractTreePath(children, result)
512
+        }
513
+      }
514
+    },
515
+    _findNodePath(key, nodes, path = []) {
516
+      let textField = this.map.text
517
+      let valueField = this.map.value
518
+      for (let i = 0; i < nodes.length; i++) {
519
+        let node = nodes[i]
520
+        let children = node.children
521
+        let text = node[textField]
522
+        let value = node[valueField]
523
+
524
+        path.push({
525
+          value,
526
+          text
527
+        })
528
+
529
+        if (value === key) {
530
+          return path
531
+        }
532
+
533
+        if (children) {
534
+          const p = this._findNodePath(key, children, path)
535
+          if (p.length) {
536
+            return p
537
+          }
538
+        }
539
+
540
+        path.pop()
541
+      }
542
+      return []
543
+    },
544
+    _processLocalData() {
545
+      this._treeData = []
546
+      this._extractTree(this.localdata, this._treeData)
547
+
548
+      var inputValue = this.dataValue
549
+      if (inputValue === undefined) {
550
+        return
551
+      }
552
+
553
+      if (Array.isArray(inputValue)) {
554
+        inputValue = inputValue[inputValue.length - 1]
555
+        if (typeof inputValue === 'object' && inputValue[this.map.value]) {
556
+          inputValue = inputValue[this.map.value]
557
+        }
558
+      }
559
+
560
+      this.selected = this._findNodePath(inputValue, this.localdata)
561
+    }
562
+  }
563
+}

+ 333 - 0
uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue

@@ -0,0 +1,333 @@
1
+<template>
2
+	<view class="uni-data-pickerview">
3
+		<scroll-view class="selected-area" scroll-x="true" scroll-y="false" :show-scrollbar="false">
4
+			<view class="selected-list">
5
+				<template v-for="(item,index) in selected">
6
+					<view class="selected-item"
7
+						:class="{'selected-item-active':index==selectedIndex, 'selected-item-text-overflow': ellipsis}"
8
+						:key="index" v-if="item.text" @click="handleSelect(index)">
9
+						<text class="">{{item.text}}</text>
10
+					</view>
11
+				</template>
12
+			</view>
13
+		</scroll-view>
14
+		<view class="tab-c">
15
+			<template v-for="(child, i) in dataList">
16
+				<scroll-view class="list" :key="i" v-if="i==selectedIndex" :scroll-y="true">
17
+					<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in child" :key="j"
18
+						@click="handleNodeClick(item, i, j)">
19
+						<text class="item-text item-text-overflow">{{item[map.text]}}</text>
20
+						<view class="check" v-if="selected.length > i && item[map.value] == selected[i].value"></view>
21
+					</view>
22
+				</scroll-view>
23
+			</template>
24
+
25
+			<view class="loading-cover" v-if="loading">
26
+				<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
27
+			</view>
28
+			<view class="error-message" v-if="errorMessage">
29
+				<text class="error-text">{{errorMessage}}</text>
30
+			</view>
31
+		</view>
32
+	</view>
33
+</template>
34
+
35
+<script>
36
+	import dataPicker from "./uni-data-picker.js"
37
+
38
+	/**
39
+	 * DataPickerview
40
+	 * @description uni-data-pickerview
41
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3796
42
+	 * @property {Array} localdata 本地数据,参考
43
+	 * @property {Boolean} step-searh = [true|false] 是否分布查询
44
+	 * @value true 启用分布查询,仅查询当前选中节点
45
+	 * @value false 关闭分布查询,一次查询出所有数据
46
+	 * @property {String|DBFieldString} self-field 分布查询当前字段名称
47
+	 * @property {String|DBFieldString} parent-field 分布查询父字段名称
48
+	 * @property {String|DBCollectionString} collection 表名
49
+	 * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
50
+	 * @property {String} orderby 排序字段及正序倒叙设置
51
+	 * @property {String|JQLString} where 查询条件
52
+	 */
53
+	export default {
54
+		name: 'UniDataPickerView',
55
+		emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'],
56
+		mixins: [dataPicker],
57
+		props: {
58
+			managedMode: {
59
+				type: Boolean,
60
+				default: false
61
+			},
62
+			ellipsis: {
63
+				type: Boolean,
64
+				default: true
65
+			}
66
+		},
67
+		data() {
68
+			return {}
69
+		},
70
+		created() {
71
+			if (this.managedMode) {
72
+				return
73
+			}
74
+
75
+			this.$nextTick(() => {
76
+				this.load()
77
+			})
78
+		},
79
+		methods: {
80
+			onPropsChange() {
81
+				this._treeData = []
82
+				this.selectedIndex = 0
83
+				this.load()
84
+			},
85
+			load() {
86
+				if (this.isLocaldata) {
87
+					this.loadData()
88
+				} else if (this.dataValue.length) {
89
+					this.getTreePath((res) => {
90
+						this.loadData()
91
+					})
92
+				}
93
+			},
94
+			handleSelect(index) {
95
+				this.selectedIndex = index
96
+			},
97
+			handleNodeClick(item, i, j) {
98
+				if (item.disable) {
99
+					return
100
+				}
101
+				const node = this.dataList[i][j]
102
+				const text = node[this.map.text]
103
+				const value = node[this.map.value]
104
+				if (i < this.selected.length - 1) {
105
+					this.selected.splice(i, this.selected.length - i)
106
+					this.selected.push({
107
+						text,
108
+						value
109
+					})
110
+				} else if (i === this.selected.length - 1) {
111
+					this.selected.splice(i, 1, {
112
+						text,
113
+						value
114
+					})
115
+				}
116
+
117
+				if (node.isleaf) {
118
+					this.onSelectedChange(node, node.isleaf)
119
+					return
120
+				}
121
+
122
+				const {
123
+					isleaf,
124
+					hasNodes
125
+				} = this._updateBindData()
126
+
127
+				if (!this._isTreeView() && !hasNodes) {
128
+					this.onSelectedChange(node, true)
129
+					return
130
+				}
131
+
132
+				if (this.isLocaldata && (!hasNodes || isleaf)) {
133
+					this.onSelectedChange(node, true)
134
+					return
135
+				}
136
+
137
+				if (!isleaf && !hasNodes) {
138
+					this._loadNodeData((data) => {
139
+						if (!data.length) {
140
+							node.isleaf = true
141
+						} else {
142
+							this._treeData.push(...data)
143
+							this._updateBindData(node)
144
+						}
145
+						this.onSelectedChange(node, node.isleaf)
146
+					}, this._nodeWhere())
147
+					return
148
+				}
149
+
150
+				this.onSelectedChange(node, false)
151
+			},
152
+			updateData(data) {
153
+				this._treeData = data.treeData
154
+				this.selected = data.selected
155
+				if (!this._treeData.length) {
156
+					this.loadData()
157
+				} else {
158
+					//this.selected = data.selected
159
+					this._updateBindData()
160
+				}
161
+			},
162
+			onDataChange() {
163
+				this.$emit('datachange')
164
+			},
165
+			onSelectedChange(node, isleaf) {
166
+				if (isleaf) {
167
+					this._dispatchEvent()
168
+				}
169
+
170
+				if (node) {
171
+					this.$emit('nodeclick', node)
172
+				}
173
+			},
174
+			_dispatchEvent() {
175
+				this.$emit('change', this.selected.slice(0))
176
+			}
177
+		}
178
+	}
179
+</script>
180
+<style >
181
+	.uni-data-pickerview {
182
+		flex: 1;
183
+		/* #ifndef APP-NVUE */
184
+		display: flex;
185
+		/* #endif */
186
+		flex-direction: column;
187
+		overflow: hidden;
188
+		height: 100%;
189
+	}
190
+
191
+	.error-text {
192
+		color: #DD524D;
193
+	}
194
+
195
+	.loading-cover {
196
+		position: absolute;
197
+		left: 0;
198
+		top: 0;
199
+		right: 0;
200
+		bottom: 0;
201
+		background-color: rgba(255, 255, 255, .5);
202
+		/* #ifndef APP-NVUE */
203
+		display: flex;
204
+		/* #endif */
205
+		flex-direction: column;
206
+		align-items: center;
207
+		z-index: 1001;
208
+	}
209
+
210
+	.load-more {
211
+		/* #ifndef APP-NVUE */
212
+		margin: auto;
213
+		/* #endif */
214
+	}
215
+
216
+	.error-message {
217
+		background-color: #fff;
218
+		position: absolute;
219
+		left: 0;
220
+		top: 0;
221
+		right: 0;
222
+		bottom: 0;
223
+		padding: 15px;
224
+		opacity: .9;
225
+		z-index: 102;
226
+	}
227
+
228
+	/* #ifdef APP-NVUE */
229
+	.selected-area {
230
+		width: 750rpx;
231
+	}
232
+
233
+	/* #endif */
234
+
235
+	.selected-list {
236
+		/* #ifndef APP-NVUE */
237
+		display: flex;
238
+		/* #endif */
239
+		flex-direction: row;
240
+		flex-wrap: nowrap;
241
+		padding: 0 5px;
242
+		border-bottom: 1px solid #f8f8f8;
243
+	}
244
+
245
+	.selected-item {
246
+		margin-left: 10px;
247
+		margin-right: 10px;
248
+		padding: 12px 0;
249
+		text-align: center;
250
+		/* #ifndef APP-NVUE */
251
+		white-space: nowrap;
252
+		/* #endif */
253
+	}
254
+
255
+	.selected-item-text-overflow {
256
+		width: 168px;
257
+		/* fix nvue */
258
+		overflow: hidden;
259
+		/* #ifndef APP-NVUE */
260
+		width: 6em;
261
+		white-space: nowrap;
262
+		text-overflow: ellipsis;
263
+		-o-text-overflow: ellipsis;
264
+		/* #endif */
265
+	}
266
+
267
+	.selected-item-active {
268
+		border-bottom: 2px solid #007aff;
269
+	}
270
+
271
+	.selected-item-text {
272
+		color: #007aff;
273
+	}
274
+
275
+	.tab-c {
276
+		position: relative;
277
+		flex: 1;
278
+		/* #ifndef APP-NVUE */
279
+		display: flex;
280
+		/* #endif */
281
+		flex-direction: row;
282
+		overflow: hidden;
283
+	}
284
+
285
+	.list {
286
+		flex: 1;
287
+	}
288
+
289
+	.item {
290
+		padding: 12px 15px;
291
+		/* border-bottom: 1px solid #f0f0f0; */
292
+		/* #ifndef APP-NVUE */
293
+		display: flex;
294
+		/* #endif */
295
+		flex-direction: row;
296
+		justify-content: space-between;
297
+	}
298
+
299
+	.is-disabled {
300
+		opacity: .5;
301
+	}
302
+
303
+	.item-text {
304
+		/* flex: 1; */
305
+		color: #333333;
306
+	}
307
+
308
+	.item-text-overflow {
309
+		width: 280px;
310
+		/* fix nvue */
311
+		overflow: hidden;
312
+		/* #ifndef APP-NVUE */
313
+		width: 20em;
314
+		white-space: nowrap;
315
+		text-overflow: ellipsis;
316
+		-o-text-overflow: ellipsis;
317
+		/* #endif */
318
+	}
319
+
320
+	.check {
321
+		margin-right: 5px;
322
+		border: 2px solid #007aff;
323
+		border-left: 0;
324
+		border-top: 0;
325
+		height: 12px;
326
+		width: 6px;
327
+		transform-origin: center;
328
+		/* #ifndef APP-NVUE */
329
+		transition: all 0.3s;
330
+		/* #endif */
331
+		transform: rotate(45deg);
332
+	}
333
+</style>

+ 92 - 0
uni_modules/uni-data-picker/package.json

@@ -0,0 +1,92 @@
1
+{
2
+  "id": "uni-data-picker",
3
+  "displayName": "uni-data-picker 数据驱动的picker选择器",
4
+  "version": "1.0.1",
5
+  "description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "picker",
10
+    "级联",
11
+    "省市区",
12
+    ""
13
+],
14
+  "repository": "https://github.com/dcloudio/uni-ui",
15
+  "engines": {
16
+    "HBuilderX": ""
17
+  },
18
+  "directories": {
19
+    "example": "../../temps/example_temps"
20
+  },
21
+  "dcloudext": {
22
+    "category": [
23
+      "前端组件",
24
+      "通用组件"
25
+    ],
26
+    "sale": {
27
+      "regular": {
28
+        "price": "0.00"
29
+      },
30
+      "sourcecode": {
31
+        "price": "0.00"
32
+      }
33
+    },
34
+    "contact": {
35
+      "qq": ""
36
+    },
37
+    "declaration": {
38
+      "ads": "无",
39
+      "data": "无",
40
+      "permissions": "无"
41
+    },
42
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
43
+  },
44
+  "uni_modules": {
45
+    "dependencies": [
46
+      "uni-load-more",
47
+			"uni-icons",
48
+			"uni-scss"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "y",
54
+        "aliyun": "y"
55
+      },
56
+      "client": {
57
+        "App": {
58
+          "app-vue": "y",
59
+          "app-nvue": "y"
60
+        },
61
+        "H5-mobile": {
62
+          "Safari": "y",
63
+          "Android Browser": "y",
64
+          "微信浏览器(Android)": "y",
65
+          "QQ浏览器(Android)": "y"
66
+        },
67
+        "H5-pc": {
68
+          "Chrome": "y",
69
+          "IE": "y",
70
+          "Edge": "y",
71
+          "Firefox": "y",
72
+          "Safari": "y"
73
+        },
74
+        "小程序": {
75
+          "微信": "y",
76
+          "阿里": "y",
77
+          "百度": "y",
78
+          "字节跳动": "y",
79
+          "QQ": "y"
80
+        },
81
+        "快应用": {
82
+          "华为": "u",
83
+          "联盟": "u"
84
+        },
85
+        "Vue": {
86
+            "vue2": "y",
87
+            "vue3": "y"
88
+        }
89
+      }
90
+    }
91
+  }
92
+}

+ 22 - 0
uni_modules/uni-data-picker/readme.md

@@ -0,0 +1,22 @@
1
+## DataPicker 级联选择
2
+> **组件名:uni-data-picker**
3
+> 代码块: `uDataPicker`
4
+> 关联组件:`uni-data-pickerview`、`uni-load-more`。
5
+
6
+
7
+`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。
8
+
9
+支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
10
+
11
+候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。
12
+
13
+`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。
14
+
15
+`<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。
16
+
17
+`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。
18
+
19
+在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。
20
+
21
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
22
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 10 - 0
uni_modules/uni-dateformat/changelog.md

@@ -0,0 +1,10 @@
1
+## 1.0.0(2021-11-19)
2
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
3
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
4
+## 0.0.5(2021-07-08)
5
+- 调整 默认时间不再是当前时间,而是显示'-'字符
6
+## 0.0.4(2021-05-12)
7
+- 新增 组件示例地址
8
+## 0.0.3(2021-02-04)
9
+- 调整为uni_modules目录规范
10
+- 修复 iOS 平台日期格式化出错的问题

+ 200 - 0
uni_modules/uni-dateformat/components/uni-dateformat/date-format.js

@@ -0,0 +1,200 @@
1
+// yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型
2
+function pad(str, length = 2) {
3
+	str += ''
4
+	while (str.length < length) {
5
+		str = '0' + str
6
+	}
7
+	return str.slice(-length)
8
+}
9
+
10
+const parser = {
11
+	yyyy: (dateObj) => {
12
+		return pad(dateObj.year, 4)
13
+	},
14
+	yy: (dateObj) => {
15
+		return pad(dateObj.year)
16
+	},
17
+	MM: (dateObj) => {
18
+		return pad(dateObj.month)
19
+	},
20
+	M: (dateObj) => {
21
+		return dateObj.month
22
+	},
23
+	dd: (dateObj) => {
24
+		return pad(dateObj.day)
25
+	},
26
+	d: (dateObj) => {
27
+		return dateObj.day
28
+	},
29
+	hh: (dateObj) => {
30
+		return pad(dateObj.hour)
31
+	},
32
+	h: (dateObj) => {
33
+		return dateObj.hour
34
+	},
35
+	mm: (dateObj) => {
36
+		return pad(dateObj.minute)
37
+	},
38
+	m: (dateObj) => {
39
+		return dateObj.minute
40
+	},
41
+	ss: (dateObj) => {
42
+		return pad(dateObj.second)
43
+	},
44
+	s: (dateObj) => {
45
+		return dateObj.second
46
+	},
47
+	SSS: (dateObj) => {
48
+		return pad(dateObj.millisecond, 3)
49
+	},
50
+	S: (dateObj) => {
51
+		return dateObj.millisecond
52
+	},
53
+}
54
+
55
+// 这都n年了iOS依然不认识2020-12-12,需要转换为2020/12/12
56
+function getDate(time) {
57
+	if (time instanceof Date) {
58
+		return time
59
+	}
60
+	switch (typeof time) {
61
+		case 'string':
62
+			{
63
+				// 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000
64
+				if (time.indexOf('T') > -1) {
65
+					return new Date(time)
66
+				}
67
+				return new Date(time.replace(/-/g, '/'))
68
+			}
69
+		default:
70
+			return new Date(time)
71
+	}
72
+}
73
+
74
+export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') {
75
+	if (!date && date !== 0) {
76
+		return ''
77
+	}
78
+	date = getDate(date)
79
+	const dateObj = {
80
+		year: date.getFullYear(),
81
+		month: date.getMonth() + 1,
82
+		day: date.getDate(),
83
+		hour: date.getHours(),
84
+		minute: date.getMinutes(),
85
+		second: date.getSeconds(),
86
+		millisecond: date.getMilliseconds()
87
+	}
88
+	const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/
89
+	let flag = true
90
+	let result = format
91
+	while (flag) {
92
+		flag = false
93
+		result = result.replace(tokenRegExp, function(matched) {
94
+			flag = true
95
+			return parser[matched](dateObj)
96
+		})
97
+	}
98
+	return result
99
+}
100
+
101
+export function friendlyDate(time, {
102
+	locale = 'zh',
103
+	threshold = [60000, 3600000],
104
+	format = 'yyyy/MM/dd hh:mm:ss'
105
+}) {
106
+	if (time === '-') {
107
+		return time
108
+	}
109
+	if (!time && time !== 0) {
110
+		return ''
111
+	}
112
+	const localeText = {
113
+		zh: {
114
+			year: '年',
115
+			month: '月',
116
+			day: '天',
117
+			hour: '小时',
118
+			minute: '分钟',
119
+			second: '秒',
120
+			ago: '前',
121
+			later: '后',
122
+			justNow: '刚刚',
123
+			soon: '马上',
124
+			template: '{num}{unit}{suffix}'
125
+		},
126
+		en: {
127
+			year: 'year',
128
+			month: 'month',
129
+			day: 'day',
130
+			hour: 'hour',
131
+			minute: 'minute',
132
+			second: 'second',
133
+			ago: 'ago',
134
+			later: 'later',
135
+			justNow: 'just now',
136
+			soon: 'soon',
137
+			template: '{num} {unit} {suffix}'
138
+		}
139
+	}
140
+	const text = localeText[locale] || localeText.zh
141
+	let date = getDate(time)
142
+	let ms = date.getTime() - Date.now()
143
+	let absMs = Math.abs(ms)
144
+	if (absMs < threshold[0]) {
145
+		return ms < 0 ? text.justNow : text.soon
146
+	}
147
+	if (absMs >= threshold[1]) {
148
+		return formatDate(date, format)
149
+	}
150
+	let num
151
+	let unit
152
+	let suffix = text.later
153
+	if (ms < 0) {
154
+		suffix = text.ago
155
+		ms = -ms
156
+	}
157
+	const seconds = Math.floor((ms) / 1000)
158
+	const minutes = Math.floor(seconds / 60)
159
+	const hours = Math.floor(minutes / 60)
160
+	const days = Math.floor(hours / 24)
161
+	const months = Math.floor(days / 30)
162
+	const years = Math.floor(months / 12)
163
+	switch (true) {
164
+		case years > 0:
165
+			num = years
166
+			unit = text.year
167
+			break
168
+		case months > 0:
169
+			num = months
170
+			unit = text.month
171
+			break
172
+		case days > 0:
173
+			num = days
174
+			unit = text.day
175
+			break
176
+		case hours > 0:
177
+			num = hours
178
+			unit = text.hour
179
+			break
180
+		case minutes > 0:
181
+			num = minutes
182
+			unit = text.minute
183
+			break
184
+		default:
185
+			num = seconds
186
+			unit = text.second
187
+			break
188
+	}
189
+
190
+	if (locale === 'en') {
191
+		if (num === 1) {
192
+			num = 'a'
193
+		} else {
194
+			unit += 's'
195
+		}
196
+	}
197
+
198
+	return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g,
199
+		suffix)
200
+}

+ 88 - 0
uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue

@@ -0,0 +1,88 @@
1
+<template>
2
+	<text>{{dateShow}}</text>
3
+</template>
4
+
5
+<script>
6
+	import {friendlyDate} from './date-format.js'
7
+	/**
8
+	 * Dateformat 日期格式化
9
+	 * @description 日期格式化组件
10
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3279
11
+	 * @property {Object|String|Number} date 日期对象/日期字符串/时间戳
12
+	 * @property {String} locale 格式化使用的语言
13
+	 * 	@value zh 中文
14
+	 * 	@value en 英文
15
+	 * @property {Array} threshold 应用不同类型格式化的阈值
16
+	 * @property {String} format 输出日期字符串时的格式
17
+	 */
18
+	export default {
19
+		name: 'uniDateformat',
20
+		props: {
21
+			date: {
22
+				type: [Object, String, Number],
23
+				default () {
24
+					return '-'
25
+				}
26
+			},
27
+			locale: {
28
+				type: String,
29
+				default: 'zh',
30
+			},
31
+			threshold: {
32
+				type: Array,
33
+				default () {
34
+					return [0, 0]
35
+				}
36
+			},
37
+			format: {
38
+				type: String,
39
+				default: 'yyyy/MM/dd hh:mm:ss'
40
+			},
41
+			// refreshRate使用不当可能导致性能问题,谨慎使用
42
+			refreshRate: {
43
+				type: [Number, String],
44
+				default: 0
45
+			}
46
+		},
47
+		data() {
48
+			return {
49
+				refreshMark: 0
50
+			}
51
+		},
52
+		computed: {
53
+			dateShow() {
54
+				this.refreshMark
55
+				return friendlyDate(this.date, {
56
+					locale: this.locale,
57
+					threshold: this.threshold,
58
+					format: this.format
59
+				})
60
+			}
61
+		},
62
+		watch: {
63
+			refreshRate: {
64
+				handler() {
65
+					this.setAutoRefresh()
66
+				},
67
+				immediate: true
68
+			}
69
+		},
70
+		methods: {
71
+			refresh() {
72
+				this.refreshMark++
73
+			},
74
+			setAutoRefresh() {
75
+				clearInterval(this.refreshInterval)
76
+				if (this.refreshRate) {
77
+					this.refreshInterval = setInterval(() => {
78
+						this.refresh()
79
+					}, parseInt(this.refreshRate))
80
+				}
81
+			}
82
+		}
83
+	}
84
+</script>
85
+
86
+<style>
87
+
88
+</style>

+ 88 - 0
uni_modules/uni-dateformat/package.json

@@ -0,0 +1,88 @@
1
+{
2
+  "id": "uni-dateformat",
3
+  "displayName": "uni-dateformat 日期格式化",
4
+  "version": "1.0.0",
5
+  "description": "日期格式化组件,可以将日期格式化为1分钟前、刚刚等形式",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "日期格式化",
10
+    "时间格式化",
11
+    "格式化时间",
12
+    ""
13
+],
14
+  "repository": "https://github.com/dcloudio/uni-ui",
15
+  "engines": {
16
+    "HBuilderX": ""
17
+  },
18
+  "directories": {
19
+    "example": "../../temps/example_temps"
20
+  },
21
+  "dcloudext": {
22
+    "category": [
23
+      "前端组件",
24
+      "通用组件"
25
+    ],
26
+    "sale": {
27
+      "regular": {
28
+        "price": "0.00"
29
+      },
30
+      "sourcecode": {
31
+        "price": "0.00"
32
+      }
33
+    },
34
+    "contact": {
35
+      "qq": ""
36
+    },
37
+    "declaration": {
38
+      "ads": "无",
39
+      "data": "无",
40
+      "permissions": "无"
41
+    },
42
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
43
+  },
44
+  "uni_modules": {
45
+    "dependencies": ["uni-scss"],
46
+    "encrypt": [],
47
+    "platforms": {
48
+      "cloud": {
49
+        "tcb": "y",
50
+        "aliyun": "y"
51
+      },
52
+      "client": {
53
+        "App": {
54
+          "app-vue": "y",
55
+          "app-nvue": "y"
56
+        },
57
+        "H5-mobile": {
58
+          "Safari": "y",
59
+          "Android Browser": "y",
60
+          "微信浏览器(Android)": "y",
61
+          "QQ浏览器(Android)": "y"
62
+        },
63
+        "H5-pc": {
64
+          "Chrome": "y",
65
+          "IE": "y",
66
+          "Edge": "y",
67
+          "Firefox": "y",
68
+          "Safari": "y"
69
+        },
70
+        "小程序": {
71
+          "微信": "y",
72
+          "阿里": "y",
73
+          "百度": "y",
74
+          "字节跳动": "y",
75
+          "QQ": "y"
76
+        },
77
+        "快应用": {
78
+          "华为": "y",
79
+          "联盟": "y"
80
+        },
81
+        "Vue": {
82
+            "vue2": "y",
83
+            "vue3": "y"
84
+        }
85
+      }
86
+    }
87
+  }
88
+}

+ 11 - 0
uni_modules/uni-dateformat/readme.md

@@ -0,0 +1,11 @@
1
+
2
+
3
+### DateFormat 日期格式化
4
+> **组件名:uni-dateformat**
5
+> 代码块: `uDateformat`
6
+
7
+
8
+日期格式化组件。
9
+
10
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat)
11
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 85 - 0
uni_modules/uni-datetime-picker/changelog.md

@@ -0,0 +1,85 @@
1
+## 2.2.2(2021-12-10)
2
+- 修复 clear-icon 属性在小程序平台不生效的 bug
3
+## 2.2.1(2021-12-10)
4
+- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug
5
+## 2.2.0(2021-11-19)
6
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
7
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
8
+## 2.1.5(2021-11-09) 
9
+- 新增 提供组件设计资源,组件样式调整
10
+## 2.1.4(2021-09-10)
11
+- 修复 hide-second 在移动端的 bug
12
+- 修复 单选赋默认值时,赋值日期未高亮的 bug
13
+- 修复 赋默认值时,移动端未正确显示时间的 bug
14
+## 2.1.3(2021-09-09)
15
+- 新增 hide-second 属性,支持只使用时分,隐藏秒
16
+## 2.1.2(2021-09-03)
17
+- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
18
+- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
19
+- 优化 调整字号大小,美化日历界面
20
+- 修复 因国际化导致的 placeholder 失效的 bug
21
+## 2.1.1(2021-08-24)
22
+- 新增 支持国际化
23
+- 优化 范围选择器在 pc 端过宽的问题
24
+## 2.1.0(2021-08-09)
25
+- 新增 适配 vue3
26
+## 2.0.19(2021-08-09)
27
+- 新增 支持作为 uni-forms 子组件相关功能
28
+- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug
29
+## 2.0.18(2021-08-05)
30
+- 修复 type 属性动态赋值无效的 bug
31
+- 修复 ‘确认’按钮被 tabbar 遮盖 bug
32
+- 修复 组件未赋值时范围选左、右日历相同的 bug
33
+## 2.0.17(2021-08-04)
34
+- 修复 范围选未正确显示当前值的 bug
35
+- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug
36
+## 2.0.16(2021-07-21)
37
+- 新增 return-type 属性支持返回 date 日期对象
38
+## 2.0.15(2021-07-14)
39
+- 修复 单选日期类型,初始赋值后不在当前日历的 bug
40
+- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
41
+- 优化 移动端移除显示框的清空按钮,无实际用途
42
+## 2.0.14(2021-07-14)
43
+- 修复 组件赋值为空,界面未更新的 bug
44
+- 修复 start 和 end 不能动态赋值的 bug
45
+- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug
46
+## 2.0.13(2021-07-08)
47
+- 修复 范围选择不能动态赋值的 bug
48
+## 2.0.12(2021-07-08)
49
+- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
50
+## 2.0.11(2021-07-08)
51
+- 优化 弹出层在超出视窗边缘定位不准确的问题
52
+## 2.0.10(2021-07-08)
53
+- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug
54
+- 优化 弹出层在超出视窗边缘被遮盖的问题
55
+## 2.0.9(2021-07-07)
56
+- 新增 maskClick 事件
57
+- 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px
58
+- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
59
+## 2.0.8(2021-07-07)
60
+- 新增 日期时间显示框支持插槽
61
+## 2.0.7(2021-07-01)
62
+- 优化 添加 uni-icons 依赖
63
+## 2.0.6(2021-05-22)
64
+- 修复 图标在小程序上不显示的 bug
65
+- 优化 重命名引用组件,避免潜在组件命名冲突
66
+## 2.0.5(2021-05-20)
67
+- 优化 代码目录扁平化
68
+## 2.0.4(2021-05-12)
69
+- 新增 组件示例地址
70
+## 2.0.3(2021-05-10)
71
+- 修复 ios 下不识别 '-' 日期格式的 bug
72
+- 优化 pc 下弹出层添加边框和阴影
73
+## 2.0.2(2021-05-08)
74
+- 修复 在 admin 中获取弹出层定位错误的bug
75
+## 2.0.1(2021-05-08)
76
+- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
77
+## 2.0.0(2021-04-30)
78
+- 支持日历形式的日期+时间的范围选择
79
+ > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
80
+## 1.0.6(2021-03-18)
81
+- 新增 hide-second 属性,时间支持仅选择时、分
82
+- 修复 选择跟显示的日期不一样的 bug
83
+- 修复 chang事件触发2次的 bug
84
+- 修复 分、秒 end 范围错误的 bug
85
+- 优化 更好的 nvue 适配

+ 185 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue

@@ -0,0 +1,185 @@
1
+<template>
2
+	<view class="uni-calendar-item__weeks-box" :class="{
3
+		'uni-calendar-item--disable':weeks.disable,
4
+		'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
5
+		'uni-calendar-item--multiple': weeks.multiple,
6
+		'uni-calendar-item--after-checked-x':weeks.afterMultiple,
7
+		}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
8
+		<view class="uni-calendar-item__weeks-box-item" :class="{
9
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
10
+				'uni-calendar-item--checked-range-text': checkHover,
11
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
12
+				'uni-calendar-item--multiple': weeks.multiple,
13
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
14
+				'uni-calendar-item--disable':weeks.disable,
15
+				}">
16
+			<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
17
+			<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
18
+		</view>
19
+		<view :class="{'uni-calendar-item--isDay': weeks.isDay}"></view>
20
+	</view>
21
+</template>
22
+
23
+<script>
24
+	export default {
25
+		props: {
26
+			weeks: {
27
+				type: Object,
28
+				default () {
29
+					return {}
30
+				}
31
+			},
32
+			calendar: {
33
+				type: Object,
34
+				default: () => {
35
+					return {}
36
+				}
37
+			},
38
+			selected: {
39
+				type: Array,
40
+				default: () => {
41
+					return []
42
+				}
43
+			},
44
+			lunar: {
45
+				type: Boolean,
46
+				default: false
47
+			},
48
+			checkHover: {
49
+				type: Boolean,
50
+				default: false
51
+			}
52
+		},
53
+		methods: {
54
+			choiceDate(weeks) {
55
+				this.$emit('change', weeks)
56
+			},
57
+			handleMousemove(weeks) {
58
+				this.$emit('handleMouse', weeks)
59
+			}
60
+		}
61
+	}
62
+</script>
63
+
64
+<style lang="scss" >
65
+	.uni-calendar-item__weeks-box {
66
+		flex: 1;
67
+		/* #ifndef APP-NVUE */
68
+		display: flex;
69
+		/* #endif */
70
+		flex-direction: column;
71
+		justify-content: center;
72
+		align-items: center;
73
+		margin: 1px 0;
74
+		position: relative;
75
+	}
76
+
77
+	.uni-calendar-item__weeks-box-text {
78
+		font-size: 14px;
79
+		// font-family: Lato-Bold, Lato;
80
+		font-weight: bold;
81
+		color: #455997;
82
+	}
83
+
84
+	.uni-calendar-item__weeks-lunar-text {
85
+		font-size: 12px;
86
+		color: #333;
87
+	}
88
+
89
+	.uni-calendar-item__weeks-box-item {
90
+		position: relative;
91
+		/* #ifndef APP-NVUE */
92
+		display: flex;
93
+		/* #endif */
94
+		flex-direction: column;
95
+		justify-content: center;
96
+		align-items: center;
97
+		width: 40px;
98
+		height: 40px;
99
+		/* #ifdef H5 */
100
+		cursor: pointer;
101
+		/* #endif */
102
+	}
103
+
104
+
105
+	.uni-calendar-item__weeks-box-circle {
106
+		position: absolute;
107
+		top: 5px;
108
+		right: 5px;
109
+		width: 8px;
110
+		height: 8px;
111
+		border-radius: 8px;
112
+		background-color: #dd524d;
113
+
114
+	}
115
+
116
+	.uni-calendar-item__weeks-box .uni-calendar-item--disable {
117
+		// background-color: rgba(249, 249, 249, $uni-opacity-disabled);
118
+		cursor: default;
119
+	}
120
+
121
+	.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
122
+		color: #D1D1D1;
123
+	}
124
+
125
+	.uni-calendar-item--isDay {
126
+		position: absolute;
127
+		top: 10px;
128
+		right: 17%;
129
+		background-color: #dd524d;
130
+		width:6px;
131
+		height: 6px;
132
+		border-radius: 50%;
133
+	}
134
+
135
+	.uni-calendar-item--extra {
136
+		color: #dd524d;
137
+		opacity: 0.8;
138
+	}
139
+
140
+	.uni-calendar-item__weeks-box .uni-calendar-item--checked {
141
+		background-color: #007aff;
142
+		border-radius: 50%;
143
+		box-sizing: border-box;
144
+		border: 3px solid #fff;
145
+	}
146
+
147
+	.uni-calendar-item--checked .uni-calendar-item--checked-text {
148
+		color: #fff;
149
+	}
150
+
151
+	.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
152
+		color: #333;
153
+	}
154
+
155
+	.uni-calendar-item--multiple {
156
+		background-color:  #F6F7FC;
157
+		// color: #fff;
158
+	}
159
+
160
+	.uni-calendar-item--multiple .uni-calendar-item--before-checked,
161
+	.uni-calendar-item--multiple .uni-calendar-item--after-checked {
162
+		background-color: #409eff;
163
+		border-radius: 50%;
164
+		box-sizing: border-box;
165
+		border: 3px solid #F6F7FC;
166
+	}
167
+
168
+	.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
169
+	.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
170
+		color: #fff;
171
+	}
172
+
173
+	.uni-calendar-item--before-checked-x {
174
+		border-top-left-radius: 50px;
175
+		border-bottom-left-radius: 50px;
176
+		box-sizing: border-box;
177
+		background-color: #F6F7FC;
178
+	}
179
+
180
+	.uni-calendar-item--after-checked-x {
181
+		border-top-right-radius: 50px;
182
+		border-bottom-right-radius: 50px;
183
+		background-color: #F6F7FC;
184
+	}
185
+</style>

+ 898 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue

@@ -0,0 +1,898 @@
1
+<template>
2
+	<view class="uni-calendar" @mouseleave="leaveCale">
3
+		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
4
+			@click="clean"></view>
5
+		<view v-if="insert || show" class="uni-calendar__content"
6
+			:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
7
+			<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
8
+				<view v-if="left" class="uni-calendar__header-btn-box" @click.stop="pre">
9
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
10
+				</view>
11
+				<picker mode="date" :value="date" fields="month" @change="bindDateChange">
12
+					<text
13
+						class="uni-calendar__header-text">{{ (nowDate.year||'') + ' 年 ' + ( nowDate.month||'') +' 月'}}</text>
14
+				</picker>
15
+				<view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next">
16
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
17
+				</view>
18
+				<view v-if="!insert" class="dialog-close" @click="clean">
19
+					<view class="dialog-close-plus" data-id="close"></view>
20
+					<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
21
+				</view>
22
+
23
+				<!-- <text class="uni-calendar__backtoday" @click="backtoday">回到今天</text> -->
24
+			</view>
25
+			<view class="uni-calendar__box">
26
+				<view v-if="showMonth" class="uni-calendar__box-bg">
27
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
28
+				</view>
29
+				<view class="uni-calendar__weeks" style="padding-bottom: 7px;">
30
+					<view class="uni-calendar__weeks-day">
31
+						<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
32
+					</view>
33
+					<view class="uni-calendar__weeks-day">
34
+						<text class="uni-calendar__weeks-day-text">{{monText}}</text>
35
+					</view>
36
+					<view class="uni-calendar__weeks-day">
37
+						<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
38
+					</view>
39
+					<view class="uni-calendar__weeks-day">
40
+						<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
41
+					</view>
42
+					<view class="uni-calendar__weeks-day">
43
+						<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
44
+					</view>
45
+					<view class="uni-calendar__weeks-day">
46
+						<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
47
+					</view>
48
+					<view class="uni-calendar__weeks-day">
49
+						<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
50
+					</view>
51
+				</view>
52
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
53
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
54
+						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar"
55
+							:selected="selected" :lunar="lunar" :checkHover="range" @change="choiceDate"
56
+							@handleMouse="handleMouse">
57
+						</calendar-item>
58
+					</view>
59
+				</view>
60
+			</view>
61
+			<view v-if="!insert && !range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top"
62
+				style="padding: 0 80px;">
63
+				<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
64
+				<time-picker type="time" :start="reactStartTime" :end="reactEndTime" v-model="time"
65
+					:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
66
+				</time-picker>
67
+			</view>
68
+
69
+			<view v-if="!insert && range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top">
70
+				<view class="uni-date-changed--time-start">
71
+					<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
72
+					</view>
73
+					<time-picker type="time" :start="reactStartTime" v-model="timeRange.startTime" :border="false"
74
+						:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
75
+					</time-picker>
76
+				</view>
77
+				<uni-icons type="arrowthinright" color="#999" style="line-height: 50px;"></uni-icons>
78
+				<view class="uni-date-changed--time-end">
79
+					<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
80
+					<time-picker type="time" :end="reactEndTime" v-model="timeRange.endTime" :border="false"
81
+						:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
82
+					</time-picker>
83
+				</view>
84
+			</view>
85
+			<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
86
+				<!-- <view class="uni-calendar__header-btn-box">
87
+					<text class="uni-calendar__button-text uni-calendar--fixed-width">{{okText}}</text>
88
+				</view> -->
89
+				<view class="uni-datetime-picker--btn" @click="confirm">确认</view>
90
+			</view>
91
+		</view>
92
+	</view>
93
+</template>
94
+
95
+<script>
96
+	import Calendar from './util.js';
97
+	import calendarItem from './calendar-item.vue'
98
+	import timePicker from './time-picker.vue'
99
+	import {
100
+		initVueI18n
101
+	} from '@dcloudio/uni-i18n'
102
+	import messages from './i18n/index.js'
103
+	const {
104
+		t
105
+	} = initVueI18n(messages)
106
+	/**
107
+	 * Calendar 日历
108
+	 * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
109
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
110
+	 * @property {String} date 自定义当前时间,默认为今天
111
+	 * @property {Boolean} lunar 显示农历
112
+	 * @property {String} startDate 日期选择范围-开始日期
113
+	 * @property {String} endDate 日期选择范围-结束日期
114
+	 * @property {Boolean} range 范围选择
115
+	 * @property {Boolean} insert = [true|false] 插入模式,默认为false
116
+	 * 	@value true 弹窗模式
117
+	 * 	@value false 插入模式
118
+	 * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
119
+	 * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
120
+	 * @property {Boolean} showMonth 是否选择月份为背景
121
+	 * @event {Function} change 日期改变,`insert :ture` 时生效
122
+	 * @event {Function} confirm 确认选择`insert :false` 时生效
123
+	 * @event {Function} monthSwitch 切换月份时触发
124
+	 * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
125
+	 */
126
+	export default {
127
+		components: {
128
+			calendarItem,
129
+			timePicker
130
+		},
131
+		props: {
132
+			date: {
133
+				type: String,
134
+				default: ''
135
+			},
136
+			defTime: {
137
+				type: [String, Object],
138
+				default: ''
139
+			},
140
+			selectableTimes: {
141
+				type: [Object],
142
+				default () {
143
+					return {}
144
+				}
145
+			},
146
+			selected: {
147
+				type: Array,
148
+				default () {
149
+					return []
150
+				}
151
+			},
152
+			lunar: {
153
+				type: Boolean,
154
+				default: false
155
+			},
156
+			startDate: {
157
+				type: String,
158
+				default: ''
159
+			},
160
+			endDate: {
161
+				type: String,
162
+				default: ''
163
+			},
164
+			range: {
165
+				type: Boolean,
166
+				default: false
167
+			},
168
+			typeHasTime: {
169
+				type: Boolean,
170
+				default: false
171
+			},
172
+			insert: {
173
+				type: Boolean,
174
+				default: true
175
+			},
176
+			showMonth: {
177
+				type: Boolean,
178
+				default: true
179
+			},
180
+			clearDate: {
181
+				type: Boolean,
182
+				default: true
183
+			},
184
+			left: {
185
+				type: Boolean,
186
+				default: true
187
+			},
188
+			right: {
189
+				type: Boolean,
190
+				default: true
191
+			},
192
+			checkHover: {
193
+				type: Boolean,
194
+				default: true
195
+			},
196
+			hideSecond: {
197
+				type: [Boolean],
198
+				default: false
199
+			},
200
+			pleStatus: {
201
+				type: Object,
202
+				default () {
203
+					return {
204
+						before: '',
205
+						after: '',
206
+						data: [],
207
+						fulldate: ''
208
+					}
209
+				}
210
+			}
211
+		},
212
+		data() {
213
+			return {
214
+				show: false,
215
+				weeks: [],
216
+				calendar: {},
217
+				nowDate: '',
218
+				aniMaskShow: false,
219
+				firstEnter: true,
220
+				time: '',
221
+				timeRange: {
222
+					startTime: '',
223
+					endTime: ''
224
+				},
225
+				tempSingleDate: '',
226
+				tempRange: {
227
+					before: '',
228
+					after: ''
229
+				}
230
+			}
231
+		},
232
+		watch: {
233
+			date: {
234
+				immediate: true,
235
+				handler(newVal, oldVal) {
236
+					if (!this.range) {
237
+						this.tempSingleDate = newVal
238
+						setTimeout(() => {
239
+							this.init(newVal)
240
+						}, 100)
241
+					}
242
+				}
243
+			},
244
+			defTime: {
245
+				immediate: true,
246
+				handler(newVal, oldVal) {
247
+					if (!this.range) {
248
+						this.time = newVal
249
+					} else {
250
+						// console.log('-----', newVal);
251
+						this.timeRange.startTime = newVal.start
252
+						this.timeRange.endTime = newVal.end
253
+					}
254
+				}
255
+			},
256
+			startDate(val) {
257
+				this.cale.resetSatrtDate(val)
258
+				this.cale.setDate(this.nowDate.fullDate)
259
+				this.weeks = this.cale.weeks
260
+			},
261
+			endDate(val) {
262
+				this.cale.resetEndDate(val)
263
+				this.cale.setDate(this.nowDate.fullDate)
264
+				this.weeks = this.cale.weeks
265
+			},
266
+			selected(newVal) {
267
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
268
+				this.weeks = this.cale.weeks
269
+			},
270
+			pleStatus: {
271
+				immediate: true,
272
+				handler(newVal, oldVal) {
273
+					const {
274
+						before,
275
+						after,
276
+						fulldate,
277
+						which
278
+					} = newVal
279
+					this.tempRange.before = before
280
+					this.tempRange.after = after
281
+					setTimeout(() => {
282
+						if (fulldate) {
283
+							this.cale.setHoverMultiple(fulldate)
284
+							if (before && after) {
285
+								this.cale.lastHover = true
286
+								if (this.rangeWithinMonth(after, before)) return
287
+								this.setDate(before)
288
+							} else {
289
+								this.cale.setMultiple(fulldate)
290
+								this.setDate(this.nowDate.fullDate)
291
+								this.calendar.fullDate = ''
292
+								this.cale.lastHover = false
293
+							}
294
+						} else {
295
+							this.cale.setDefaultMultiple(before, after)
296
+							if (which === 'left') {
297
+								this.setDate(before)
298
+								this.weeks = this.cale.weeks
299
+							} else {
300
+								this.setDate(after)
301
+								this.weeks = this.cale.weeks
302
+							}
303
+							this.cale.lastHover = true
304
+						}
305
+					}, 16)
306
+				}
307
+			}
308
+		},
309
+		computed: {
310
+			reactStartTime() {
311
+				const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
312
+				const res = activeDate === this.startDate ? this.selectableTimes.start : ''
313
+				return res
314
+			},
315
+			reactEndTime() {
316
+				const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
317
+				const res = activeDate === this.endDate ? this.selectableTimes.end : ''
318
+				return res
319
+			},
320
+			/**
321
+			 * for i18n
322
+			 */
323
+			selectDateText() {
324
+				return t("uni-datetime-picker.selectDate")
325
+			},
326
+			startDateText() {
327
+				return this.startPlaceholder || t("uni-datetime-picker.startDate")
328
+			},
329
+			endDateText() {
330
+				return this.endPlaceholder || t("uni-datetime-picker.endDate")
331
+			},
332
+			okText() {
333
+				return t("uni-datetime-picker.ok")
334
+			},
335
+			monText() {
336
+				return t("uni-calender.MON")
337
+			},
338
+			TUEText() {
339
+				return t("uni-calender.TUE")
340
+			},
341
+			WEDText() {
342
+				return t("uni-calender.WED")
343
+			},
344
+			THUText() {
345
+				return t("uni-calender.THU")
346
+			},
347
+			FRIText() {
348
+				return t("uni-calender.FRI")
349
+			},
350
+			SATText() {
351
+				return t("uni-calender.SAT")
352
+			},
353
+			SUNText() {
354
+				return t("uni-calender.SUN")
355
+			},
356
+		},
357
+		created() {
358
+			// 获取日历方法实例
359
+			this.cale = new Calendar({
360
+				// date: new Date(),
361
+				selected: this.selected,
362
+				startDate: this.startDate,
363
+				endDate: this.endDate,
364
+				range: this.range,
365
+				// multipleStatus: this.pleStatus
366
+			})
367
+			// 选中某一天
368
+			// this.cale.setDate(this.date)
369
+			this.init(this.date)
370
+			// this.setDay
371
+		},
372
+		methods: {
373
+			leaveCale() {
374
+				this.firstEnter = true
375
+			},
376
+			handleMouse(weeks) {
377
+				if (weeks.disable) return
378
+				if (this.cale.lastHover) return
379
+				let {
380
+					before,
381
+					after
382
+				} = this.cale.multipleStatus
383
+				if (!before) return
384
+				this.calendar = weeks
385
+				// 设置范围选
386
+				this.cale.setHoverMultiple(this.calendar.fullDate)
387
+				this.weeks = this.cale.weeks
388
+				// hover时,进入一个日历,更新另一个
389
+				if (this.firstEnter) {
390
+					this.$emit('firstEnterCale', this.cale.multipleStatus)
391
+					this.firstEnter = false
392
+				}
393
+			},
394
+			rangeWithinMonth(A, B) {
395
+				const [yearA, monthA] = A.split('-')
396
+				const [yearB, monthB] = B.split('-')
397
+				return yearA === yearB && monthA === monthB
398
+			},
399
+
400
+			// 取消穿透
401
+			clean() {
402
+				this.close()
403
+			},
404
+
405
+			clearCalender() {
406
+				if (this.range) {
407
+					this.timeRange.startTime = ''
408
+					this.timeRange.endTime = ''
409
+					this.tempRange.before = ''
410
+					this.tempRange.after = ''
411
+					this.cale.multipleStatus.before = ''
412
+					this.cale.multipleStatus.after = ''
413
+					this.cale.multipleStatus.data = []
414
+					this.cale.lastHover = false
415
+				} else {
416
+					this.time = ''
417
+					this.tempSingleDate = ''
418
+				}
419
+				this.calendar.fullDate = ''
420
+				this.setDate()
421
+			},
422
+
423
+			bindDateChange(e) {
424
+				const value = e.detail.value + '-1'
425
+				this.init(value)
426
+			},
427
+			/**
428
+			 * 初始化日期显示
429
+			 * @param {Object} date
430
+			 */
431
+			init(date) {
432
+				this.cale.setDate(date)
433
+				this.weeks = this.cale.weeks
434
+				this.nowDate = this.calendar = this.cale.getInfo(date)
435
+			},
436
+			// choiceDate(weeks) {
437
+			// 	if (weeks.disable) return
438
+			// 	this.calendar = weeks
439
+			// 	// 设置多选
440
+			// 	this.cale.setMultiple(this.calendar.fullDate, true)
441
+			// 	this.weeks = this.cale.weeks
442
+			// 	this.tempSingleDate = this.calendar.fullDate
443
+			// 	this.tempRange.before = this.cale.multipleStatus.before
444
+			// 	this.tempRange.after = this.cale.multipleStatus.after
445
+			// 	this.change()
446
+			// },
447
+			/**
448
+			 * 打开日历弹窗
449
+			 */
450
+			open() {
451
+				// 弹窗模式并且清理数据
452
+				if (this.clearDate && !this.insert) {
453
+					this.cale.cleanMultipleStatus()
454
+					// this.cale.setDate(this.date)
455
+					this.init(this.date)
456
+				}
457
+				this.show = true
458
+				this.$nextTick(() => {
459
+					setTimeout(() => {
460
+						this.aniMaskShow = true
461
+					}, 50)
462
+				})
463
+			},
464
+			/**
465
+			 * 关闭日历弹窗
466
+			 */
467
+			close() {
468
+				this.aniMaskShow = false
469
+				this.$nextTick(() => {
470
+					setTimeout(() => {
471
+						this.show = false
472
+						this.$emit('close')
473
+					}, 300)
474
+				})
475
+			},
476
+			/**
477
+			 * 确认按钮
478
+			 */
479
+			confirm() {
480
+				this.setEmit('confirm')
481
+				this.close()
482
+			},
483
+			/**
484
+			 * 变化触发
485
+			 */
486
+			change() {
487
+				if (!this.insert) return
488
+				this.setEmit('change')
489
+			},
490
+			/**
491
+			 * 选择月份触发
492
+			 */
493
+			monthSwitch() {
494
+				let {
495
+					year,
496
+					month
497
+				} = this.nowDate
498
+				this.$emit('monthSwitch', {
499
+					year,
500
+					month: Number(month)
501
+				})
502
+			},
503
+			/**
504
+			 * 派发事件
505
+			 * @param {Object} name
506
+			 */
507
+			setEmit(name) {
508
+				let {
509
+					year,
510
+					month,
511
+					date,
512
+					fullDate,
513
+					lunar,
514
+					extraInfo
515
+				} = this.calendar
516
+				this.$emit(name, {
517
+					range: this.cale.multipleStatus,
518
+					year,
519
+					month,
520
+					date,
521
+					time: this.time,
522
+					timeRange: this.timeRange,
523
+					fulldate: fullDate,
524
+					lunar,
525
+					extraInfo: extraInfo || {}
526
+				})
527
+			},
528
+			/**
529
+			 * 选择天触发
530
+			 * @param {Object} weeks
531
+			 */
532
+			choiceDate(weeks) {
533
+				if (weeks.disable) return
534
+				this.calendar = weeks
535
+				this.calendar.userChecked = true
536
+				// 设置多选
537
+				this.cale.setMultiple(this.calendar.fullDate, true)
538
+				this.weeks = this.cale.weeks
539
+				this.tempSingleDate = this.calendar.fullDate
540
+				this.tempRange.before = this.cale.multipleStatus.before
541
+				this.tempRange.after = this.cale.multipleStatus.after
542
+				this.change()
543
+			},
544
+			/**
545
+			 * 回到今天
546
+			 */
547
+			backtoday() {
548
+				let date = this.cale.getDate(new Date()).fullDate
549
+				// this.cale.setDate(date)
550
+				this.init(date)
551
+				this.change()
552
+			},
553
+			/**
554
+			 * 比较时间大小
555
+			 */
556
+			dateCompare(startDate, endDate) {
557
+				// 计算截止时间
558
+				startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
559
+				// 计算详细项的截止时间
560
+				endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
561
+				if (startDate <= endDate) {
562
+					return true
563
+				} else {
564
+					return false
565
+				}
566
+			},
567
+			/**
568
+			 * 上个月
569
+			 */
570
+			pre() {
571
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
572
+				this.setDate(preDate)
573
+				this.monthSwitch()
574
+
575
+			},
576
+			/**
577
+			 * 下个月
578
+			 */
579
+			next() {
580
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
581
+				this.setDate(nextDate)
582
+				this.monthSwitch()
583
+			},
584
+			/**
585
+			 * 设置日期
586
+			 * @param {Object} date
587
+			 */
588
+			setDate(date) {
589
+				this.cale.setDate(date)
590
+				this.weeks = this.cale.weeks
591
+				this.nowDate = this.cale.getInfo(date)
592
+			}
593
+		}
594
+	}
595
+</script>
596
+
597
+<style lang="scss" >
598
+	.uni-calendar {
599
+		/* #ifndef APP-NVUE */
600
+		display: flex;
601
+		/* #endif */
602
+		flex-direction: column;
603
+	}
604
+
605
+	.uni-calendar__mask {
606
+		position: fixed;
607
+		bottom: 0;
608
+		top: 0;
609
+		left: 0;
610
+		right: 0;
611
+		background-color: rgba(0, 0, 0, 0.4);
612
+		transition-property: opacity;
613
+		transition-duration: 0.3s;
614
+		opacity: 0;
615
+		/* #ifndef APP-NVUE */
616
+		z-index: 99;
617
+		/* #endif */
618
+	}
619
+
620
+	.uni-calendar--mask-show {
621
+		opacity: 1
622
+	}
623
+
624
+	.uni-calendar--fixed {
625
+		position: fixed;
626
+		bottom: calc(var(--window-bottom));
627
+		left: 0;
628
+		right: 0;
629
+		transition-property: transform;
630
+		transition-duration: 0.3s;
631
+		transform: translateY(460px);
632
+		/* #ifndef APP-NVUE */
633
+		z-index: 99;
634
+		/* #endif */
635
+	}
636
+
637
+	.uni-calendar--ani-show {
638
+		transform: translateY(0);
639
+	}
640
+
641
+	.uni-calendar__content {
642
+		background-color: #fff;
643
+	}
644
+
645
+	.uni-calendar__content-mobile {
646
+		border-top-left-radius: 10px;
647
+		border-top-right-radius: 10px;
648
+		box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
649
+	}
650
+
651
+	.uni-calendar__header {
652
+		position: relative;
653
+		/* #ifndef APP-NVUE */
654
+		display: flex;
655
+		/* #endif */
656
+		flex-direction: row;
657
+		justify-content: center;
658
+		align-items: center;
659
+		height: 50px;
660
+	}
661
+
662
+	.uni-calendar__header-mobile {
663
+		padding: 10px;
664
+		padding-bottom: 0;
665
+	}
666
+
667
+	.uni-calendar--fixed-top {
668
+		/* #ifndef APP-NVUE */
669
+		display: flex;
670
+		/* #endif */
671
+		flex-direction: row;
672
+		justify-content: space-between;
673
+		border-top-color: rgba(0, 0, 0, 0.4);
674
+		border-top-style: solid;
675
+		border-top-width: 1px;
676
+	}
677
+
678
+	.uni-calendar--fixed-width {
679
+		width: 50px;
680
+	}
681
+
682
+	.uni-calendar__backtoday {
683
+		position: absolute;
684
+		right: 0;
685
+		top: 25rpx;
686
+		padding: 0 5px;
687
+		padding-left: 10px;
688
+		height: 25px;
689
+		line-height: 25px;
690
+		font-size: 12px;
691
+		border-top-left-radius: 25px;
692
+		border-bottom-left-radius: 25px;
693
+		color: #fff;
694
+		background-color: #f1f1f1;
695
+	}
696
+
697
+	.uni-calendar__header-text {
698
+		text-align: center;
699
+		width: 100px;
700
+		font-size: 15px;
701
+		color: #666;
702
+	}
703
+
704
+	.uni-calendar__button-text {
705
+		text-align: center;
706
+		width: 100px;
707
+		font-size: 14px;
708
+		color: #007aff;
709
+		/* #ifndef APP-NVUE */
710
+		letter-spacing: 3px;
711
+		/* #endif */
712
+	}
713
+
714
+	.uni-calendar__header-btn-box {
715
+		/* #ifndef APP-NVUE */
716
+		display: flex;
717
+		/* #endif */
718
+		flex-direction: row;
719
+		align-items: center;
720
+		justify-content: center;
721
+		width: 50px;
722
+		height: 50px;
723
+	}
724
+
725
+	.uni-calendar__header-btn {
726
+		width: 9px;
727
+		height: 9px;
728
+		border-left-color: #808080;
729
+		border-left-style: solid;
730
+		border-left-width: 1px;
731
+		border-top-color: #555555;
732
+		border-top-style: solid;
733
+		border-top-width: 1px;
734
+	}
735
+
736
+	.uni-calendar--left {
737
+		transform: rotate(-45deg);
738
+	}
739
+
740
+	.uni-calendar--right {
741
+		transform: rotate(135deg);
742
+	}
743
+
744
+
745
+	.uni-calendar__weeks {
746
+		position: relative;
747
+		/* #ifndef APP-NVUE */
748
+		display: flex;
749
+		/* #endif */
750
+		flex-direction: row;
751
+	}
752
+
753
+	.uni-calendar__weeks-item {
754
+		flex: 1;
755
+	}
756
+
757
+	.uni-calendar__weeks-day {
758
+		flex: 1;
759
+		/* #ifndef APP-NVUE */
760
+		display: flex;
761
+		/* #endif */
762
+		flex-direction: column;
763
+		justify-content: center;
764
+		align-items: center;
765
+		height: 40px;
766
+		border-bottom-color: #F5F5F5;
767
+		border-bottom-style: solid;
768
+		border-bottom-width: 1px;
769
+	}
770
+
771
+	.uni-calendar__weeks-day-text {
772
+		font-size: 12px;
773
+		color: #B2B2B2;
774
+	}
775
+
776
+	.uni-calendar__box {
777
+		position: relative;
778
+		// padding: 0 10px;
779
+		padding-bottom: 7px;
780
+	}
781
+
782
+	.uni-calendar__box-bg {
783
+		/* #ifndef APP-NVUE */
784
+		display: flex;
785
+		/* #endif */
786
+		justify-content: center;
787
+		align-items: center;
788
+		position: absolute;
789
+		top: 0;
790
+		left: 0;
791
+		right: 0;
792
+		bottom: 0;
793
+	}
794
+
795
+	.uni-calendar__box-bg-text {
796
+		font-size: 200px;
797
+		font-weight: bold;
798
+		color: #999;
799
+		opacity: 0.1;
800
+		text-align: center;
801
+		/* #ifndef APP-NVUE */
802
+		line-height: 1;
803
+		/* #endif */
804
+	}
805
+
806
+	.uni-date-changed {
807
+		padding: 0 10px;
808
+		// line-height: 50px;
809
+		text-align: center;
810
+		color: #333;
811
+		border-top-color: #DCDCDC;
812
+		;
813
+		border-top-style: solid;
814
+		border-top-width: 1px;
815
+		flex: 1;
816
+	}
817
+
818
+	.uni-date-btn--ok {
819
+		padding: 20px 15px;
820
+	}
821
+
822
+	.uni-date-changed--time-start {
823
+		/* #ifndef APP-NVUE */
824
+		display: flex;
825
+		/* #endif */
826
+		align-items: center;
827
+	}
828
+
829
+	.uni-date-changed--time-end {
830
+		/* #ifndef APP-NVUE */
831
+		display: flex;
832
+		/* #endif */
833
+		align-items: center;
834
+	}
835
+
836
+	.uni-date-changed--time-date {
837
+		color: #999;
838
+		line-height: 50px;
839
+		margin-right: 5px;
840
+		// opacity: 0.6;
841
+	}
842
+
843
+	.time-picker-style {
844
+		// width: 62px;
845
+		/* #ifndef APP-NVUE */
846
+		display: flex;
847
+		/* #endif */
848
+		justify-content: center;
849
+		align-items: center
850
+	}
851
+
852
+	.mr-10 {
853
+		margin-right: 10px;
854
+	}
855
+
856
+	.dialog-close {
857
+		position: absolute;
858
+		top: 0;
859
+		right: 0;
860
+		bottom: 0;
861
+		/* #ifndef APP-NVUE */
862
+		display: flex;
863
+		/* #endif */
864
+		flex-direction: row;
865
+		align-items: center;
866
+		padding: 0 25px;
867
+		margin-top: 10px;
868
+	}
869
+
870
+	.dialog-close-plus {
871
+		width: 16px;
872
+		height: 2px;
873
+		background-color: #737987;
874
+		border-radius: 2px;
875
+		transform: rotate(45deg);
876
+	}
877
+
878
+	.dialog-close-rotate {
879
+		position: absolute;
880
+		transform: rotate(-45deg);
881
+	}
882
+
883
+	.uni-datetime-picker--btn {
884
+		border-radius: 100px;
885
+		height: 40px;
886
+		line-height: 40px;
887
+		background-color: #007aff;
888
+		color: #fff;
889
+		font-size: 16px;
890
+		letter-spacing: 5px;
891
+	}
892
+
893
+	/* #ifndef APP-NVUE */
894
+	.uni-datetime-picker--btn:active {
895
+		opacity: 0.7;
896
+	}
897
+	/* #endif */
898
+</style>

+ 19 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json

@@ -0,0 +1,19 @@
1
+{
2
+	"uni-datetime-picker.selectDate": "select date",
3
+	"uni-datetime-picker.selectTime": "select time",
4
+	"uni-datetime-picker.selectDateTime": "select datetime",
5
+	"uni-datetime-picker.startDate": "start date",
6
+	"uni-datetime-picker.endDate": "end date",
7
+	"uni-datetime-picker.startTime": "start time",
8
+	"uni-datetime-picker.endTime": "end time",
9
+	"uni-datetime-picker.ok": "ok",
10
+	"uni-datetime-picker.clear": "clear",
11
+	"uni-datetime-picker.cancel": "cancel",
12
+	"uni-calender.MON": "MON",
13
+	"uni-calender.TUE": "TUE",
14
+	"uni-calender.WED": "WED",
15
+	"uni-calender.THU": "THU",
16
+	"uni-calender.FRI": "FRI",
17
+	"uni-calender.SAT": "SAT",
18
+	"uni-calender.SUN": "SUN"
19
+}

+ 8 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js

@@ -0,0 +1,8 @@
1
+import en from './en.json'
2
+import zhHans from './zh-Hans.json'
3
+import zhHant from './zh-Hant.json'
4
+export default {
5
+	en,
6
+	'zh-Hans': zhHans,
7
+	'zh-Hant': zhHant
8
+}

+ 19 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json

@@ -0,0 +1,19 @@
1
+{
2
+	"uni-datetime-picker.selectDate": "选择日期",
3
+	"uni-datetime-picker.selectTime": "选择时间",
4
+	"uni-datetime-picker.selectDateTime": "选择日期时间",
5
+	"uni-datetime-picker.startDate": "开始日期",
6
+	"uni-datetime-picker.endDate": "结束日期",
7
+	"uni-datetime-picker.startTime": "开始时间",
8
+	"uni-datetime-picker.endTime": "结束时间",
9
+	"uni-datetime-picker.ok": "确定",
10
+	"uni-datetime-picker.clear": "清除",
11
+	"uni-datetime-picker.cancel": "取消",
12
+	"uni-calender.SUN": "日",
13
+	"uni-calender.MON": "一",
14
+	"uni-calender.TUE": "二",
15
+	"uni-calender.WED": "三",
16
+	"uni-calender.THU": "四",
17
+	"uni-calender.FRI": "五",
18
+	"uni-calender.SAT": "六"
19
+}

+ 19 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json

@@ -0,0 +1,19 @@
1
+{
2
+	"uni-datetime-picker.selectDate": "選擇日期",
3
+	"uni-datetime-picker.selectTime": "選擇時間",
4
+	"uni-datetime-picker.selectDateTime": "選擇日期時間",
5
+	"uni-datetime-picker.startDate": "開始日期",
6
+	"uni-datetime-picker.endDate": "結束日期",
7
+	"uni-datetime-picker.startTime": "開始时间",
8
+	"uni-datetime-picker.endTime": "結束时间",
9
+	"uni-datetime-picker.ok": "確定",
10
+	"uni-datetime-picker.clear": "清除",
11
+	"uni-datetime-picker.cancel": "取消",
12
+	"uni-calender.SUN": "日",
13
+	"uni-calender.MON": "一",
14
+	"uni-calender.TUE": "二",
15
+	"uni-calender.WED": "三",
16
+	"uni-calender.THU": "四",
17
+	"uni-calender.FRI": "五",
18
+	"uni-calender.SAT": "六"
19
+}

+ 45 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js

@@ -0,0 +1,45 @@
1
+// #ifdef H5
2
+export default {
3
+  name: 'Keypress',
4
+  props: {
5
+    disable: {
6
+      type: Boolean,
7
+      default: false
8
+    }
9
+  },
10
+  mounted () {
11
+    const keyNames = {
12
+      esc: ['Esc', 'Escape'],
13
+      tab: 'Tab',
14
+      enter: 'Enter',
15
+      space: [' ', 'Spacebar'],
16
+      up: ['Up', 'ArrowUp'],
17
+      left: ['Left', 'ArrowLeft'],
18
+      right: ['Right', 'ArrowRight'],
19
+      down: ['Down', 'ArrowDown'],
20
+      delete: ['Backspace', 'Delete', 'Del']
21
+    }
22
+    const listener = ($event) => {
23
+      if (this.disable) {
24
+        return
25
+      }
26
+      const keyName = Object.keys(keyNames).find(key => {
27
+        const keyName = $event.key
28
+        const value = keyNames[key]
29
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
30
+      })
31
+      if (keyName) {
32
+        // 避免和其他按键事件冲突
33
+        setTimeout(() => {
34
+          this.$emit(keyName, {})
35
+        }, 0)
36
+      }
37
+    }
38
+    document.addEventListener('keyup', listener)
39
+    this.$once('hook:beforeDestroy', () => {
40
+      document.removeEventListener('keyup', listener)
41
+    })
42
+  },
43
+	render: () => {}
44
+}
45
+// #endif

+ 927 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue

@@ -0,0 +1,927 @@
1
+<template>
2
+	<view class="uni-datetime-picker">
3
+		<view @click="initTimePicker">
4
+			<slot>
5
+				<view class="uni-datetime-picker-timebox-pointer"
6
+					:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
7
+					<text class="uni-datetime-picker-text">{{time}}</text>
8
+					<view v-if="!time" class="uni-datetime-picker-time">
9
+						<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
10
+					</view>
11
+				</view>
12
+			</slot>
13
+		</view>
14
+		<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
15
+		<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
16
+			:style="fixNvueBug">
17
+			<view class="uni-title">
18
+				<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
19
+			</view>
20
+			<view v-if="dateShow" class="uni-datetime-picker__container-box">
21
+				<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
22
+					@change="bindDateChange">
23
+					<picker-view-column>
24
+						<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
25
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
26
+						</view>
27
+					</picker-view-column>
28
+					<picker-view-column>
29
+						<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
30
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
31
+						</view>
32
+					</picker-view-column>
33
+					<picker-view-column>
34
+						<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
35
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
36
+						</view>
37
+					</picker-view-column>
38
+				</picker-view>
39
+				<!-- 兼容 nvue 不支持伪类 -->
40
+				<text class="uni-datetime-picker-sign sign-left">-</text>
41
+				<text class="uni-datetime-picker-sign sign-right">-</text>
42
+			</view>
43
+			<view v-if="timeShow" class="uni-datetime-picker__container-box">
44
+				<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
45
+					:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
46
+					<picker-view-column>
47
+						<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
48
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
49
+						</view>
50
+					</picker-view-column>
51
+					<picker-view-column>
52
+						<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
53
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
54
+						</view>
55
+					</picker-view-column>
56
+					<picker-view-column v-if="!hideSecond">
57
+						<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
58
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
59
+						</view>
60
+					</picker-view-column>
61
+				</picker-view>
62
+				<!-- 兼容 nvue 不支持伪类 -->
63
+				<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
64
+				<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
65
+			</view>
66
+			<view class="uni-datetime-picker-btn">
67
+				<view @click="clearTime">
68
+					<text class="uni-datetime-picker-btn-text">{{clearText}}</text>
69
+				</view>
70
+				<view class="uni-datetime-picker-btn-group">
71
+					<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
72
+						<text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
73
+					</view>
74
+					<view @click="setTime">
75
+						<text class="uni-datetime-picker-btn-text">{{okText}}</text>
76
+					</view>
77
+				</view>
78
+			</view>
79
+		</view>
80
+		<!-- #ifdef H5 -->
81
+		<!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> -->
82
+		<!-- #endif -->
83
+	</view>
84
+</template>
85
+
86
+<script>
87
+	// #ifdef H5
88
+	import keypress from './keypress'
89
+	// #endif
90
+	import {
91
+		initVueI18n
92
+	} from '@dcloudio/uni-i18n'
93
+	import messages from './i18n/index.js'
94
+	const {	t	} = initVueI18n(messages)
95
+
96
+	/**
97
+	 * DatetimePicker 时间选择器
98
+	 * @description 可以同时选择日期和时间的选择器
99
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
100
+	 * @property {String} type = [datetime | date | time] 显示模式
101
+	 * @property {Boolean} multiple = [true|false] 是否多选
102
+	 * @property {String|Number} value 默认值
103
+	 * @property {String|Number} start 起始日期或时间
104
+	 * @property {String|Number} end 起始日期或时间
105
+	 * @property {String} return-type = [timestamp | string]
106
+	 * @event {Function} change  选中发生变化触发
107
+	 */
108
+
109
+	export default {
110
+		name: 'UniDatetimePicker',
111
+		components: {
112
+			// #ifdef H5
113
+			keypress
114
+			// #endif
115
+		},
116
+		data() {
117
+			return {
118
+				indicatorStyle: `height: 50px;`,
119
+				visible: false,
120
+				fixNvueBug: {},
121
+				dateShow: true,
122
+				timeShow: true,
123
+				title: '日期和时间',
124
+				// 输入框当前时间
125
+				time: '',
126
+				// 当前的年月日时分秒
127
+				year: 1920,
128
+				month: 0,
129
+				day: 0,
130
+				hour: 0,
131
+				minute: 0,
132
+				second: 0,
133
+				// 起始时间
134
+				startYear: 1920,
135
+				startMonth: 1,
136
+				startDay: 1,
137
+				startHour: 0,
138
+				startMinute: 0,
139
+				startSecond: 0,
140
+				// 结束时间
141
+				endYear: 2120,
142
+				endMonth: 12,
143
+				endDay: 31,
144
+				endHour: 23,
145
+				endMinute: 59,
146
+				endSecond: 59,
147
+			}
148
+		},
149
+		props: {
150
+			type: {
151
+				type: String,
152
+				default: 'datetime'
153
+			},
154
+			value: {
155
+				type: [String, Number],
156
+				default: ''
157
+			},
158
+			modelValue: {
159
+				type: [String, Number],
160
+				default: ''
161
+			},
162
+			start: {
163
+				type: [Number, String],
164
+				default: ''
165
+			},
166
+			end: {
167
+				type: [Number, String],
168
+				default: ''
169
+			},
170
+			returnType: {
171
+				type: String,
172
+				default: 'string'
173
+			},
174
+			disabled: {
175
+				type: [Boolean, String],
176
+				default: false
177
+			},
178
+			border: {
179
+				type: [Boolean, String],
180
+				default: true
181
+			},
182
+			hideSecond: {
183
+				type: [Boolean, String],
184
+				default: false
185
+			}
186
+		},
187
+		watch: {
188
+			value: {
189
+				handler(newVal, oldVal) {
190
+					if (newVal) {
191
+						this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式
192
+						this.initTime(false)
193
+					} else {
194
+						this.time = ''
195
+						this.parseValue(Date.now())
196
+					}
197
+				},
198
+				immediate: true
199
+			},
200
+			type: {
201
+				handler(newValue) {
202
+					if (newValue === 'date') {
203
+						this.dateShow = true
204
+						this.timeShow = false
205
+						this.title = '日期'
206
+					} else if (newValue === 'time') {
207
+						this.dateShow = false
208
+						this.timeShow = true
209
+						this.title = '时间'
210
+					} else {
211
+						this.dateShow = true
212
+						this.timeShow = true
213
+						this.title = '日期和时间'
214
+					}
215
+				},
216
+				immediate: true
217
+			},
218
+			start: {
219
+				handler(newVal) {
220
+					this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式
221
+				},
222
+				immediate: true
223
+			},
224
+			end: {
225
+				handler(newVal) {
226
+					this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式
227
+				},
228
+				immediate: true
229
+			},
230
+
231
+			// 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
232
+			months(newVal) {
233
+				this.checkValue('month', this.month, newVal)
234
+			},
235
+			days(newVal) {
236
+				this.checkValue('day', this.day, newVal)
237
+			},
238
+			hours(newVal) {
239
+				this.checkValue('hour', this.hour, newVal)
240
+			},
241
+			minutes(newVal) {
242
+				this.checkValue('minute', this.minute, newVal)
243
+			},
244
+			seconds(newVal) {
245
+				this.checkValue('second', this.second, newVal)
246
+			}
247
+		},
248
+		computed: {
249
+			// 当前年、月、日、时、分、秒选择范围
250
+			years() {
251
+				return this.getCurrentRange('year')
252
+			},
253
+
254
+			months() {
255
+				return this.getCurrentRange('month')
256
+			},
257
+
258
+			days() {
259
+				return this.getCurrentRange('day')
260
+			},
261
+
262
+			hours() {
263
+				return this.getCurrentRange('hour')
264
+			},
265
+
266
+			minutes() {
267
+				return this.getCurrentRange('minute')
268
+			},
269
+
270
+			seconds() {
271
+				return this.getCurrentRange('second')
272
+			},
273
+
274
+			// picker 当前值数组
275
+			ymd() {
276
+				return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
277
+			},
278
+			hms() {
279
+				return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
280
+			},
281
+
282
+			// 当前 date 是 start
283
+			currentDateIsStart() {
284
+				return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
285
+			},
286
+
287
+			// 当前 date 是 end
288
+			currentDateIsEnd() {
289
+				return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
290
+			},
291
+
292
+			// 当前年、月、日、时、分、秒的最小值和最大值
293
+			minYear() {
294
+				return this.startYear
295
+			},
296
+			maxYear() {
297
+				return this.endYear
298
+			},
299
+			minMonth() {
300
+				if (this.year === this.startYear) {
301
+					return this.startMonth
302
+				} else {
303
+					return 1
304
+				}
305
+			},
306
+			maxMonth() {
307
+				if (this.year === this.endYear) {
308
+					return this.endMonth
309
+				} else {
310
+					return 12
311
+				}
312
+			},
313
+			minDay() {
314
+				if (this.year === this.startYear && this.month === this.startMonth) {
315
+					return this.startDay
316
+				} else {
317
+					return 1
318
+				}
319
+			},
320
+			maxDay() {
321
+				if (this.year === this.endYear && this.month === this.endMonth) {
322
+					return this.endDay
323
+				} else {
324
+					return this.daysInMonth(this.year, this.month)
325
+				}
326
+			},
327
+			minHour() {
328
+				if (this.type === 'datetime') {
329
+					if (this.currentDateIsStart) {
330
+						return this.startHour
331
+					} else {
332
+						return 0
333
+					}
334
+				}
335
+				if (this.type === 'time') {
336
+					return this.startHour
337
+				}
338
+			},
339
+			maxHour() {
340
+				if (this.type === 'datetime') {
341
+					if (this.currentDateIsEnd) {
342
+						return this.endHour
343
+					} else {
344
+						return 23
345
+					}
346
+				}
347
+				if (this.type === 'time') {
348
+					return this.endHour
349
+				}
350
+			},
351
+			minMinute() {
352
+				if (this.type === 'datetime') {
353
+					if (this.currentDateIsStart && this.hour === this.startHour) {
354
+						return this.startMinute
355
+					} else {
356
+						return 0
357
+					}
358
+				}
359
+				if (this.type === 'time') {
360
+					if (this.hour === this.startHour) {
361
+						return this.startMinute
362
+					} else {
363
+						return 0
364
+					}
365
+				}
366
+			},
367
+			maxMinute() {
368
+				if (this.type === 'datetime') {
369
+					if (this.currentDateIsEnd && this.hour === this.endHour) {
370
+						return this.endMinute
371
+					} else {
372
+						return 59
373
+					}
374
+				}
375
+				if (this.type === 'time') {
376
+					if (this.hour === this.endHour) {
377
+						return this.endMinute
378
+					} else {
379
+						return 59
380
+					}
381
+				}
382
+			},
383
+			minSecond() {
384
+				if (this.type === 'datetime') {
385
+					if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
386
+						return this.startSecond
387
+					} else {
388
+						return 0
389
+					}
390
+				}
391
+				if (this.type === 'time') {
392
+					if (this.hour === this.startHour && this.minute === this.startMinute) {
393
+						return this.startSecond
394
+					} else {
395
+						return 0
396
+					}
397
+				}
398
+			},
399
+			maxSecond() {
400
+				if (this.type === 'datetime') {
401
+					if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
402
+						return this.endSecond
403
+					} else {
404
+						return 59
405
+					}
406
+				}
407
+				if (this.type === 'time') {
408
+					if (this.hour === this.endHour && this.minute === this.endMinute) {
409
+						return this.endSecond
410
+					} else {
411
+						return 59
412
+					}
413
+				}
414
+			},
415
+
416
+			/**
417
+			 * for i18n
418
+			 */
419
+			selectTimeText() {
420
+				return t("uni-datetime-picker.selectTime")
421
+			},
422
+			okText() {
423
+				return t("uni-datetime-picker.ok")
424
+			},
425
+			clearText() {
426
+				return t("uni-datetime-picker.clear")
427
+			},
428
+			cancelText() {
429
+				return t("uni-datetime-picker.cancel")
430
+			}
431
+		},
432
+
433
+		mounted() {
434
+			// #ifdef APP-NVUE
435
+			const res = uni.getSystemInfoSync();
436
+			this.fixNvueBug = {
437
+				top: res.windowHeight / 2,
438
+				left: res.windowWidth / 2
439
+			}
440
+			// #endif
441
+		},
442
+
443
+		methods: {
444
+			/**
445
+			 * @param {Object} item
446
+			 * 小于 10 在前面加个 0
447
+			 */
448
+
449
+			lessThanTen(item) {
450
+				return item < 10 ? '0' + item : item
451
+			},
452
+
453
+			/**
454
+			 * 解析时分秒字符串,例如:00:00:00
455
+			 * @param {String} timeString
456
+			 */
457
+			parseTimeType(timeString) {
458
+				if (timeString) {
459
+					let timeArr = timeString.split(':')
460
+					this.hour = Number(timeArr[0])
461
+					this.minute = Number(timeArr[1])
462
+					this.second = Number(timeArr[2])
463
+				}
464
+			},
465
+
466
+			/**
467
+			 * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
468
+			 * @param {String | Number} datetime
469
+			 */
470
+			initPickerValue(datetime) {
471
+				let defaultValue = null
472
+				if (datetime) {
473
+					defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
474
+				} else {
475
+					defaultValue = Date.now()
476
+					defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
477
+				}
478
+				this.parseValue(defaultValue)
479
+			},
480
+
481
+			/**
482
+			 * 初始值规则:
483
+			 * - 用户设置初始值 value
484
+			 * 	- 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start
485
+			 * 	- 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start
486
+			 * 	- 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end
487
+			 * 	- 无起始终止时间,则初始值为 value
488
+			 * - 无初始值 value,则初始值为当前本地时间 Date.now()
489
+			 * @param {Object} value
490
+			 * @param {Object} dateBase
491
+			 */
492
+			compareValueWithStartAndEnd(value, start, end) {
493
+				let winner = null
494
+				value = this.superTimeStamp(value)
495
+				start = this.superTimeStamp(start)
496
+				end = this.superTimeStamp(end)
497
+
498
+				if (start && end) {
499
+					if (value < start) {
500
+						winner = new Date(start)
501
+					} else if (value > end) {
502
+						winner = new Date(end)
503
+					} else {
504
+						winner = new Date(value)
505
+					}
506
+				} else if (start && !end) {
507
+					winner = start <= value ? new Date(value) : new Date(start)
508
+				} else if (!start && end) {
509
+					winner = value <= end ? new Date(value) : new Date(end)
510
+				} else {
511
+					winner = new Date(value)
512
+				}
513
+
514
+				return winner
515
+			},
516
+
517
+			/**
518
+			 * 转换为可比较的时间戳,接受日期、时分秒、时间戳
519
+			 * @param {Object} value
520
+			 */
521
+			superTimeStamp(value) {
522
+				let dateBase = ''
523
+				if (this.type === 'time' && value && typeof value === 'string') {
524
+					const now = new Date()
525
+					const year = now.getFullYear()
526
+					const month = now.getMonth() + 1
527
+					const day = now.getDate()
528
+					dateBase = year + '/' + month + '/' + day + ' '
529
+				}
530
+				if (Number(value) && typeof value !== NaN) {
531
+					value = parseInt(value)
532
+					dateBase = 0
533
+				}
534
+				return this.createTimeStamp(dateBase + value)
535
+			},
536
+
537
+			/**
538
+			 * 解析默认值 value,字符串、时间戳
539
+			 * @param {Object} defaultTime
540
+			 */
541
+			parseValue(value) {
542
+				if (!value) {
543
+					return
544
+				}
545
+				if (this.type === 'time' && typeof value === "string") {
546
+					this.parseTimeType(value)
547
+				} else {
548
+					let defaultDate = null
549
+					defaultDate = new Date(value)
550
+					if (this.type !== 'time') {
551
+						this.year = defaultDate.getFullYear()
552
+						this.month = defaultDate.getMonth() + 1
553
+						this.day = defaultDate.getDate()
554
+					}
555
+					if (this.type !== 'date') {
556
+						this.hour = defaultDate.getHours()
557
+						this.minute = defaultDate.getMinutes()
558
+						this.second = defaultDate.getSeconds()
559
+					}
560
+				}
561
+				if (this.hideSecond) {
562
+					this.second = 0
563
+				}
564
+			},
565
+
566
+			/**
567
+			 * 解析可选择时间范围 start、end,年月日字符串、时间戳
568
+			 * @param {Object} defaultTime
569
+			 */
570
+			parseDatetimeRange(point, pointType) {
571
+				// 时间为空,则重置为初始值
572
+				if (!point) {
573
+					if (pointType === 'start') {
574
+						this.startYear = 1920
575
+						this.startMonth = 1
576
+						this.startDay = 1
577
+						this.startHour = 0
578
+						this.startMinute = 0
579
+						this.startSecond = 0
580
+					}
581
+					if (pointType === 'end') {
582
+						this.endYear = 2120
583
+						this.endMonth = 12
584
+						this.endDay = 31
585
+						this.endHour = 23
586
+						this.endMinute = 59
587
+						this.endSecond = 59
588
+					}
589
+					return
590
+				}
591
+				if (this.type === 'time') {
592
+					const pointArr = point.split(':')
593
+					this[pointType + 'Hour'] = Number(pointArr[0])
594
+					this[pointType + 'Minute'] = Number(pointArr[1])
595
+					this[pointType + 'Second'] = Number(pointArr[2])
596
+				} else {
597
+					if (!point) {
598
+						pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
599
+						return
600
+					}
601
+					if (Number(point) && Number(point) !== NaN) {
602
+						point = parseInt(point)
603
+					}
604
+					// datetime 的 end 没有时分秒, 则不限制
605
+					const hasTime = /[0-9]:[0-9]/
606
+					if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
607
+							point)) {
608
+						point = point + ' 23:59:59'
609
+					}
610
+					const pointDate = new Date(point)
611
+					this[pointType + 'Year'] = pointDate.getFullYear()
612
+					this[pointType + 'Month'] = pointDate.getMonth() + 1
613
+					this[pointType + 'Day'] = pointDate.getDate()
614
+					if (this.type === 'datetime') {
615
+						this[pointType + 'Hour'] = pointDate.getHours()
616
+						this[pointType + 'Minute'] = pointDate.getMinutes()
617
+						this[pointType + 'Second'] = pointDate.getSeconds()
618
+					}
619
+				}
620
+			},
621
+
622
+			// 获取 年、月、日、时、分、秒 当前可选范围
623
+			getCurrentRange(value) {
624
+				const range = []
625
+				for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
626
+					range.push(i)
627
+				}
628
+				return range
629
+			},
630
+
631
+			// 字符串首字母大写
632
+			capitalize(str) {
633
+				return str.charAt(0).toUpperCase() + str.slice(1)
634
+			},
635
+
636
+			// 检查当前值是否在范围内,不在则当前值重置为可选范围第一项
637
+			checkValue(name, value, values) {
638
+				if (values.indexOf(value) === -1) {
639
+					this[name] = values[0]
640
+				}
641
+			},
642
+
643
+			// 每个月的实际天数
644
+			daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
645
+				return new Date(year, month, 0).getDate();
646
+			},
647
+
648
+			//兼容 iOS、safari 日期格式
649
+			fixIosDateFormat(value) {
650
+				if (typeof value === 'string') {
651
+					value = value.replace(/-/g, '/')
652
+				}
653
+				return value
654
+			},
655
+
656
+			/**
657
+			 * 生成时间戳
658
+			 * @param {Object} time
659
+			 */
660
+			createTimeStamp(time) {
661
+				if (!time) return
662
+				if (typeof time === "number") {
663
+					return time
664
+				} else {
665
+					time = time.replace(/-/g, '/')
666
+					if (this.type === 'date') {
667
+						time = time + ' ' + '00:00:00'
668
+					}
669
+					return Date.parse(time)
670
+				}
671
+			},
672
+
673
+			/**
674
+			 * 生成日期或时间的字符串
675
+			 */
676
+			createDomSting() {
677
+				const yymmdd = this.year +
678
+					'-' +
679
+					this.lessThanTen(this.month) +
680
+					'-' +
681
+					this.lessThanTen(this.day)
682
+
683
+				let hhmmss = this.lessThanTen(this.hour) +
684
+					':' +
685
+					this.lessThanTen(this.minute)
686
+
687
+				if (!this.hideSecond) {
688
+					hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
689
+				}
690
+
691
+				if (this.type === 'date') {
692
+					return yymmdd
693
+				} else if (this.type === 'time') {
694
+					return hhmmss
695
+				} else {
696
+					return yymmdd + ' ' + hhmmss
697
+				}
698
+			},
699
+
700
+			/**
701
+			 * 初始化返回值,并抛出 change 事件
702
+			 */
703
+			initTime(emit = true) {
704
+				this.time = this.createDomSting()
705
+				if (!emit) return
706
+				if (this.returnType === 'timestamp' && this.type !== 'time') {
707
+					this.$emit('change', this.createTimeStamp(this.time))
708
+					this.$emit('input', this.createTimeStamp(this.time))
709
+					this.$emit('update:modelValue', this.createTimeStamp(this.time))
710
+				} else {
711
+					this.$emit('change', this.time)
712
+					this.$emit('input', this.time)
713
+					this.$emit('update:modelValue', this.time)
714
+				}
715
+			},
716
+
717
+			/**
718
+			 * 用户选择日期或时间更新 data
719
+			 * @param {Object} e
720
+			 */
721
+			bindDateChange(e) {
722
+				const val = e.detail.value
723
+				this.year = this.years[val[0]]
724
+				this.month = this.months[val[1]]
725
+				this.day = this.days[val[2]]
726
+			},
727
+			bindTimeChange(e) {
728
+				const val = e.detail.value
729
+				this.hour = this.hours[val[0]]
730
+				this.minute = this.minutes[val[1]]
731
+				this.second = this.seconds[val[2]]
732
+			},
733
+
734
+			/**
735
+			 * 初始化弹出层
736
+			 */
737
+			initTimePicker() {
738
+				if (this.disabled) return
739
+				const value = this.fixIosDateFormat(this.value)
740
+				this.initPickerValue(value)
741
+				this.visible = !this.visible
742
+			},
743
+
744
+			/**
745
+			 * 触发或关闭弹框
746
+			 */
747
+			tiggerTimePicker(e) {
748
+				this.visible = !this.visible
749
+			},
750
+
751
+			/**
752
+			 * 用户点击“清空”按钮,清空当前值
753
+			 */
754
+			clearTime() {
755
+				this.time = ''
756
+				this.$emit('change', this.time)
757
+				this.$emit('input', this.time)
758
+				this.$emit('update:modelValue', this.time)
759
+				this.tiggerTimePicker()
760
+			},
761
+
762
+			/**
763
+			 * 用户点击“确定”按钮
764
+			 */
765
+			setTime() {
766
+				this.initTime()
767
+				this.tiggerTimePicker()
768
+			}
769
+		}
770
+	}
771
+</script>
772
+
773
+<style>
774
+	.uni-datetime-picker {
775
+		/* #ifndef APP-NVUE */
776
+		/* width: 100%; */
777
+		/* #endif */
778
+	}
779
+
780
+	.uni-datetime-picker-view {
781
+		height: 130px;
782
+		width: 270px;
783
+		/* #ifndef APP-NVUE */
784
+		cursor: pointer;
785
+		/* #endif */
786
+	}
787
+
788
+	.uni-datetime-picker-item {
789
+		height: 50px;
790
+		line-height: 50px;
791
+		text-align: center;
792
+		font-size: 14px;
793
+	}
794
+
795
+	.uni-datetime-picker-btn {
796
+		margin-top: 60px;
797
+		/* #ifndef APP-NVUE */
798
+		display: flex;
799
+		cursor: pointer;
800
+		/* #endif */
801
+		flex-direction: row;
802
+		justify-content: space-between;
803
+	}
804
+
805
+	.uni-datetime-picker-btn-text {
806
+		font-size: 14px;
807
+		color: #007AFF;
808
+	}
809
+
810
+	.uni-datetime-picker-btn-group {
811
+		/* #ifndef APP-NVUE */
812
+		display: flex;
813
+		/* #endif */
814
+		flex-direction: row;
815
+	}
816
+
817
+	.uni-datetime-picker-cancel {
818
+		margin-right: 30px;
819
+	}
820
+
821
+	.uni-datetime-picker-mask {
822
+		position: fixed;
823
+		bottom: 0px;
824
+		top: 0px;
825
+		left: 0px;
826
+		right: 0px;
827
+		background-color: rgba(0, 0, 0, 0.4);
828
+		transition-duration: 0.3s;
829
+		z-index: 998;
830
+	}
831
+
832
+	.uni-datetime-picker-popup {
833
+		border-radius: 8px;
834
+		padding: 30px;
835
+		width: 270px;
836
+		/* #ifdef APP-NVUE */
837
+		height: 500px;
838
+		/* #endif */
839
+		/* #ifdef APP-NVUE */
840
+		width: 330px;
841
+		/* #endif */
842
+		background-color: #fff;
843
+		position: fixed;
844
+		top: 50%;
845
+		left: 50%;
846
+		transform: translate(-50%, -50%);
847
+		transition-duration: 0.3s;
848
+		z-index: 999;
849
+	}
850
+
851
+	.fix-nvue-height {
852
+		/* #ifdef APP-NVUE */
853
+		height: 330px;
854
+		/* #endif */
855
+	}
856
+
857
+	.uni-datetime-picker-time {
858
+		color: grey;
859
+	}
860
+
861
+	.uni-datetime-picker-column {
862
+		height: 50px;
863
+	}
864
+
865
+	.uni-datetime-picker-timebox {
866
+
867
+		border: 1px solid #E5E5E5;
868
+		border-radius: 5px;
869
+		padding: 7px 10px;
870
+		/* #ifndef APP-NVUE */
871
+		box-sizing: border-box;
872
+		cursor: pointer;
873
+		/* #endif */
874
+	}
875
+
876
+	.uni-datetime-picker-timebox-pointer {
877
+		/* #ifndef APP-NVUE */
878
+		cursor: pointer;
879
+		/* #endif */
880
+	}
881
+
882
+
883
+	.uni-datetime-picker-disabled {
884
+		opacity: 0.4;
885
+		/* #ifdef H5 */
886
+		cursor: not-allowed !important;
887
+		/* #endif */
888
+	}
889
+
890
+	.uni-datetime-picker-text {
891
+		font-size: 14px;
892
+	}
893
+
894
+	.uni-datetime-picker-sign {
895
+		position: absolute;
896
+		top: 53px;
897
+		/* 减掉 10px 的元素高度,兼容nvue */
898
+		color: #999;
899
+		/* #ifdef APP-NVUE */
900
+		font-size: 16px;
901
+		/* #endif */
902
+	}
903
+
904
+	.sign-left {
905
+		left: 86px;
906
+	}
907
+
908
+	.sign-right {
909
+		right: 86px;
910
+	}
911
+
912
+	.sign-center {
913
+		left: 135px;
914
+	}
915
+
916
+	.uni-datetime-picker__container-box {
917
+		position: relative;
918
+		display: flex;
919
+		align-items: center;
920
+		justify-content: center;
921
+		margin-top: 40px;
922
+	}
923
+
924
+	.time-hide-second {
925
+		width: 180px;
926
+	}
927
+</style>

+ 981 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue

@@ -0,0 +1,981 @@
1
+<template>
2
+	<view class="uni-date">
3
+		<view class="uni-date-editor" @click="show">
4
+			<slot>
5
+				<view class="uni-date-editor--x" :class="{'uni-date-editor--x__disabled': disabled,
6
+		'uni-date-x--border': border}">
7
+					<view v-if="!isRange" class="uni-date-x uni-date-single">
8
+						<uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons>
9
+						<input class="uni-date__x-input" type="text" v-model="singleVal"
10
+							:placeholder="singlePlaceholderText" :disabled="true" />
11
+					</view>
12
+					<view v-else class="uni-date-x uni-date-range">
13
+						<uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons>
14
+						<input class="uni-date__x-input t-c" type="text" v-model="range.startDate"
15
+							:placeholder="startPlaceholderText" :disabled="true" />
16
+						<slot>
17
+							<view class="">{{rangeSeparator}}</view>
18
+						</slot>
19
+						<input class="uni-date__x-input t-c" type="text" v-model="range.endDate"
20
+							:placeholder="endPlaceholderText" :disabled="true" />
21
+					</view>
22
+					<view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
23
+						<uni-icons type="clear" color="#e1e1e1" size="18"></uni-icons>
24
+					</view>
25
+				</view>
26
+			</slot>
27
+		</view>
28
+
29
+		<view v-show="popup" class="uni-date-mask" @click="close"></view>
30
+		<view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
31
+			<view v-if="!isRange" class="uni-date-single--x" :style="popover">
32
+				<view class="uni-popper__arrow"></view>
33
+				<view v-if="hasTime" class="uni-date-changed popup-x-header">
34
+					<input class="uni-date__input t-c" type="text" v-model="tempSingleDate"
35
+						:placeholder="selectDateText" />
36
+					<time-picker type="time" v-model="time" :border="false" :disabled="!tempSingleDate"
37
+						:start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
38
+						<input class="uni-date__input t-c" type="text" v-model="time" :placeholder="selectTimeText"
39
+							:disabled="!tempSingleDate" />
40
+					</time-picker>
41
+				</view>
42
+				<calendar ref="pcSingle" :showMonth="false"
43
+					:start-date="caleRange.startDate" :end-date="caleRange.endDate" :date="defSingleDate"
44
+					@change="singleChange" style="padding: 0 8px;" />
45
+				<view v-if="hasTime" class="popup-x-footer">
46
+					<!-- <text class="">此刻</text> -->
47
+					<text class="confirm" @click="confirmSingleChange">{{okText}}</text>
48
+				</view>
49
+				<view class="uni-date-popper__arrow"></view>
50
+			</view>
51
+
52
+			<view v-else class="uni-date-range--x" :style="popover">
53
+				<view class="uni-popper__arrow"></view>
54
+				<view v-if="hasTime" class="popup-x-header uni-date-changed">
55
+					<view class="popup-x-header--datetime">
56
+						<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
57
+							:placeholder="startDateText" />
58
+						<time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false"
59
+							:disabled="!tempRange.startDate" :hideSecond="hideSecond">
60
+							<input class="uni-date__input uni-date-range__input" type="text"
61
+								v-model="tempRange.startTime" :placeholder="startTimeText"
62
+								:disabled="!tempRange.startDate" />
63
+						</time-picker>
64
+					</view>
65
+					<uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
66
+					<view class="popup-x-header--datetime">
67
+						<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
68
+							:placeholder="endDateText" />
69
+						<time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false"
70
+							:disabled="!tempRange.endDate" :hideSecond="hideSecond">
71
+							<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
72
+								:placeholder="endTimeText" :disabled="!tempRange.endDate" />
73
+						</time-picker>
74
+					</view>
75
+				</view>
76
+				<view class="popup-x-body">
77
+					<calendar ref="left" :showMonth="false"
78
+						:start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true"
79
+						@change="leftChange" :pleStatus="endMultipleStatus" @firstEnterCale="updateRightCale"
80
+						@monthSwitch="leftMonthSwitch" style="padding: 0 8px;" />
81
+					<calendar ref="right" :showMonth="false"
82
+						:start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true"
83
+						@change="rightChange" :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
84
+						@monthSwitch="rightMonthSwitch" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
85
+				</view>
86
+				<view v-if="hasTime" class="popup-x-footer">
87
+					<text class="" @click="clear">{{clearText}}</text>
88
+					<text class="confirm" @click="confirmRangeChange">{{okText}}</text>
89
+				</view>
90
+			</view>
91
+		</view>
92
+		<calendar v-show="isPhone" ref="mobile" :clearDate="false" :date="defSingleDate" :defTime="reactMobDefTime"
93
+			:start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime"
94
+			:pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false"
95
+			:hideSecond="hideSecond" @confirm="mobileChange" />
96
+	</view>
97
+</template>
98
+<script>
99
+	/**
100
+	 * DatetimePicker 时间选择器
101
+	 * @description 同时支持 PC 和移动端使用日历选择日期和日期范围
102
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3962
103
+	 * @property {String} type 选择器类型
104
+	 * @property {String|Number|Array|Date} value 绑定值
105
+	 * @property {String} placeholder 单选择时的占位内容
106
+	 * @property {String} start 起始时间
107
+	 * @property {String} end 终止时间
108
+	 * @property {String} start-placeholder 范围选择时开始日期的占位内容
109
+	 * @property {String} end-placeholder 范围选择时结束日期的占位内容
110
+	 * @property {String} range-separator 选择范围时的分隔符
111
+	 * @property {Boolean} border = [true|false] 是否有边框
112
+	 * @property {Boolean} disabled = [true|false] 是否禁用
113
+	 * @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用)
114
+	 * @event {Function} change 确定日期时触发的事件
115
+	 * @event {Function} show 打开弹出层
116
+	 * @event {Function} close 关闭弹出层
117
+	 * @event {Function} clear 清除上次选中的状态和值
118
+	 **/
119
+	import calendar from './calendar.vue'
120
+	import timePicker from './time-picker.vue'
121
+	import {
122
+		initVueI18n
123
+	} from '@dcloudio/uni-i18n'
124
+	import messages from './i18n/index.js'
125
+	const {
126
+		t
127
+	} = initVueI18n(messages)
128
+
129
+	export default {
130
+		name: 'UniDatetimePicker',
131
+		components: {
132
+			calendar,
133
+			timePicker
134
+		},
135
+		data() {
136
+			return {
137
+				isRange: false,
138
+				hasTime: false,
139
+				mobileRange: false,
140
+				// 单选
141
+				singleVal: '',
142
+				tempSingleDate: '',
143
+				defSingleDate: '',
144
+				time: '',
145
+				// 范围选
146
+				caleRange: {
147
+					startDate: '',
148
+					startTime: '',
149
+					endDate: '',
150
+					endTime: ''
151
+				},
152
+				range: {
153
+					startDate: '',
154
+					// startTime: '',
155
+					endDate: '',
156
+					// endTime: ''
157
+				},
158
+				tempRange: {
159
+					startDate: '',
160
+					startTime: '',
161
+					endDate: '',
162
+					endTime: ''
163
+				},
164
+				// 左右日历同步数据
165
+				startMultipleStatus: {
166
+					before: '',
167
+					after: '',
168
+					data: [],
169
+					fulldate: ''
170
+				},
171
+				endMultipleStatus: {
172
+					before: '',
173
+					after: '',
174
+					data: [],
175
+					fulldate: ''
176
+				},
177
+				visible: false,
178
+				popup: false,
179
+				popover: null,
180
+				isEmitValue: false,
181
+				isPhone: false,
182
+				isFirstShow: true,
183
+			}
184
+		},
185
+		props: {
186
+			type: {
187
+				type: String,
188
+				default: 'datetime'
189
+			},
190
+			value: {
191
+				type: [String, Number, Array, Date],
192
+				default: ''
193
+			},
194
+			modelValue: {
195
+				type: [String, Number, Array, Date],
196
+				default: ''
197
+			},
198
+			start: {
199
+				type: [Number, String],
200
+				default: ''
201
+			},
202
+			end: {
203
+				type: [Number, String],
204
+				default: ''
205
+			},
206
+			returnType: {
207
+				type: String,
208
+				default: 'string'
209
+			},
210
+			placeholder: {
211
+				type: String,
212
+				default: ''
213
+			},
214
+			startPlaceholder: {
215
+				type: String,
216
+				default: ''
217
+			},
218
+			endPlaceholder: {
219
+				type: String,
220
+				default: ''
221
+			},
222
+			rangeSeparator: {
223
+				type: String,
224
+				default: '-'
225
+			},
226
+			border: {
227
+				type: [Boolean],
228
+				default: true
229
+			},
230
+			disabled: {
231
+				type: [Boolean],
232
+				default: false
233
+			},
234
+			clearIcon: {
235
+				type: [Boolean],
236
+				default: true
237
+			},
238
+			hideSecond: {
239
+				type: [Boolean],
240
+				default: false
241
+			}
242
+		},
243
+		watch: {
244
+			type: {
245
+				immediate: true,
246
+				handler(newVal, oldVal) {
247
+					if (newVal.indexOf('time') !== -1) {
248
+						this.hasTime = true
249
+					} else {
250
+						this.hasTime = false
251
+					}
252
+					if (newVal.indexOf('range') !== -1) {
253
+						this.isRange = true
254
+					} else {
255
+						this.isRange = false
256
+					}
257
+				}
258
+			},
259
+			value: {
260
+				immediate: true,
261
+				handler(newVal, oldVal) {
262
+					if (this.isEmitValue) {
263
+						this.isEmitValue = false
264
+						return
265
+					}
266
+					this.initPicker(newVal)
267
+				}
268
+			},
269
+
270
+			start: {
271
+				immediate: true,
272
+				handler(newVal, oldVal) {
273
+					if (!newVal) return
274
+					const {
275
+						defDate,
276
+						defTime
277
+					} = this.parseDate(newVal)
278
+					this.caleRange.startDate = defDate
279
+					if (this.hasTime) {
280
+						this.caleRange.startTime = defTime
281
+					}
282
+				}
283
+			},
284
+
285
+			end: {
286
+				immediate: true,
287
+				handler(newVal, oldVal) {
288
+					if (!newVal) return
289
+					const {
290
+						defDate,
291
+						defTime
292
+					} = this.parseDate(newVal)
293
+					this.caleRange.endDate = defDate
294
+					if (this.hasTime) {
295
+						this.caleRange.endTime = defTime
296
+					}
297
+				}
298
+			},
299
+		},
300
+		computed: {
301
+			reactStartTime() {
302
+				const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate
303
+				const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
304
+				return res
305
+			},
306
+			reactEndTime() {
307
+				const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate
308
+				const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
309
+				return res
310
+			},
311
+			reactMobDefTime() {
312
+				const times = {
313
+					start: this.tempRange.startTime,
314
+					end: this.tempRange.endTime
315
+				}
316
+				return this.isRange ? times : this.time
317
+			},
318
+			mobSelectableTime() {
319
+				return {
320
+					start: this.caleRange.startTime,
321
+					end: this.caleRange.endTime
322
+				}
323
+			},
324
+			datePopupWidth() {
325
+				// todo
326
+				return this.isRange ? 653 : 301
327
+			},
328
+
329
+			/**
330
+			 * for i18n
331
+			 */
332
+			singlePlaceholderText() {
333
+				return this.placeholder || (this.type === 'date' ? this.selectDateText : t(
334
+					"uni-datetime-picker.selectDateTime"))
335
+			},
336
+			startPlaceholderText() {
337
+				return this.startPlaceholder || this.startDateText
338
+			},
339
+			endPlaceholderText() {
340
+				return this.endPlaceholder || this.endDateText
341
+			},
342
+			selectDateText() {
343
+				return t("uni-datetime-picker.selectDate")
344
+			},
345
+			selectTimeText() {
346
+				return t("uni-datetime-picker.selectTime")
347
+			},
348
+			startDateText() {
349
+				return this.startPlaceholder || t("uni-datetime-picker.startDate")
350
+			},
351
+			startTimeText() {
352
+				return t("uni-datetime-picker.startTime")
353
+			},
354
+			endDateText() {
355
+				return this.endPlaceholder || t("uni-datetime-picker.endDate")
356
+			},
357
+			endTimeText() {
358
+				return t("uni-datetime-picker.endTime")
359
+			},
360
+			okText() {
361
+				return t("uni-datetime-picker.ok")
362
+			},
363
+			clearText() {
364
+				return t("uni-datetime-picker.clear")
365
+			},
366
+			showClearIcon() {
367
+				const { clearIcon, disabled, singleVal, range } = this
368
+				const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate))
369
+				return bool
370
+			}
371
+		},
372
+		created() {
373
+			this.form = this.getForm('uniForms')
374
+			this.formItem = this.getForm('uniFormsItem')
375
+
376
+			// if (this.formItem) {
377
+			// 	if (this.formItem.name) {
378
+			// 		this.rename = this.formItem.name
379
+			// 		this.form.inputChildrens.push(this)
380
+			// 	}
381
+			// }
382
+		},
383
+		mounted() {
384
+			this.platform()
385
+		},
386
+		methods: {
387
+			/**
388
+			 * 获取父元素实例
389
+			 */
390
+			getForm(name = 'uniForms') {
391
+				let parent = this.$parent;
392
+				let parentName = parent.$options.name;
393
+				while (parentName !== name) {
394
+					parent = parent.$parent;
395
+					if (!parent) return false
396
+					parentName = parent.$options.name;
397
+				}
398
+				return parent;
399
+			},
400
+			initPicker(newVal) {
401
+				if (!newVal || Array.isArray(newVal) && !newVal.length) {
402
+					this.$nextTick(() => {
403
+						this.clear(false)
404
+					})
405
+					return
406
+				}
407
+				if (!Array.isArray(newVal) && !this.isRange) {
408
+					const {
409
+						defDate,
410
+						defTime
411
+					} = this.parseDate(newVal)
412
+					this.singleVal = defDate
413
+					this.tempSingleDate = defDate
414
+					this.defSingleDate = defDate
415
+					if (this.hasTime) {
416
+						this.singleVal = defDate + ' ' + defTime
417
+						this.time = defTime
418
+					}
419
+				} else {
420
+					const [before, after] = newVal
421
+					if (!before && !after) return
422
+					const defBefore = this.parseDate(before)
423
+					const defAfter = this.parseDate(after)
424
+					const startDate = defBefore.defDate
425
+					const endDate = defAfter.defDate
426
+					this.range.startDate = this.tempRange.startDate = startDate
427
+					this.range.endDate = this.tempRange.endDate = endDate
428
+
429
+					if (this.hasTime) {
430
+						this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime
431
+						this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime
432
+						this.tempRange.startTime = defBefore.defTime
433
+						this.tempRange.endTime = defAfter.defTime
434
+					}
435
+					const defaultRange = {
436
+						before: defBefore.defDate,
437
+						after: defAfter.defDate
438
+					}
439
+					this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
440
+						which: 'right'
441
+					})
442
+					this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
443
+						which: 'left'
444
+					})
445
+				}
446
+			},
447
+			updateLeftCale(e) {
448
+				const left = this.$refs.left
449
+				// 设置范围选
450
+				left.cale.setHoverMultiple(e.after)
451
+				left.setDate(this.$refs.left.nowDate.fullDate)
452
+			},
453
+			updateRightCale(e) {
454
+				const right = this.$refs.right
455
+				// 设置范围选
456
+				right.cale.setHoverMultiple(e.after)
457
+				right.setDate(this.$refs.right.nowDate.fullDate)
458
+			},
459
+			platform() {
460
+				const systemInfo = uni.getSystemInfoSync()
461
+				this.isPhone = systemInfo.windowWidth <= 500
462
+				this.windowWidth = systemInfo.windowWidth
463
+			},
464
+			show(event) {
465
+				if (this.disabled) {
466
+					return
467
+				}
468
+				this.platform()
469
+				if (this.isPhone) {
470
+					this.$refs.mobile.open()
471
+					return
472
+				}
473
+				this.popover = {
474
+					top: '10px'
475
+				}
476
+				const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
477
+				dateEditor.boundingClientRect(rect => {
478
+					if (this.windowWidth - rect.left < this.datePopupWidth) {
479
+						this.popover.right = 0
480
+					}
481
+				}).exec()
482
+				setTimeout(() => {
483
+					this.popup = !this.popup
484
+					if (!this.isPhone && this.isRange && this.isFirstShow) {
485
+						this.isFirstShow = false
486
+						const {
487
+							startDate,
488
+							endDate
489
+						} = this.range
490
+						if (startDate && endDate) {
491
+							if (this.diffDate(startDate, endDate) < 30) {
492
+								this.$refs.right.next()
493
+							}
494
+						} else {
495
+							this.$refs.right.next()
496
+							this.$refs.right.cale.lastHover = false
497
+						}
498
+					}
499
+
500
+				}, 50)
501
+			},
502
+
503
+			close() {
504
+				setTimeout(() => {
505
+					this.popup = false
506
+					this.$emit('maskClick', this.value)
507
+				}, 20)
508
+			},
509
+			setEmit(value) {
510
+				if (this.returnType === "timestamp" || this.returnType === "date") {
511
+					if (!Array.isArray(value)) {
512
+						if (!this.hasTime) {
513
+							value = value + ' ' + '00:00:00'
514
+						}
515
+						value = this.createTimestamp(value)
516
+						if (this.returnType === "date") {
517
+							value = new Date(value)
518
+						}
519
+					} else {
520
+						if (!this.hasTime) {
521
+							value[0] = value[0] + ' ' + '00:00:00'
522
+							value[1] = value[1] + ' ' + '00:00:00'
523
+						}
524
+						value[0] = this.createTimestamp(value[0])
525
+						value[1] = this.createTimestamp(value[1])
526
+						if (this.returnType === "date") {
527
+							value[0] = new Date(value[0])
528
+							value[1] = new Date(value[1])
529
+						}
530
+					}
531
+				}
532
+				this.formItem && this.formItem.setValue(value)
533
+				this.$emit('change', value)
534
+				this.$emit('input', value)
535
+				this.$emit('update:modelValue', value)
536
+				this.isEmitValue = true
537
+			},
538
+			createTimestamp(date) {
539
+				date = this.fixIosDateFormat(date)
540
+				return Date.parse(new Date(date))
541
+			},
542
+			singleChange(e) {
543
+				this.tempSingleDate = e.fulldate
544
+				if (this.hasTime) return
545
+				this.confirmSingleChange()
546
+			},
547
+
548
+			confirmSingleChange() {
549
+				if (!this.tempSingleDate) {
550
+					this.popup = false
551
+					return
552
+				}
553
+				if (this.hasTime) {
554
+					this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
555
+				} else {
556
+					this.singleVal = this.tempSingleDate
557
+				}
558
+				this.setEmit(this.singleVal)
559
+				this.popup = false
560
+			},
561
+
562
+			leftChange(e) {
563
+				const {
564
+					before,
565
+					after
566
+				} = e.range
567
+				this.rangeChange(before, after)
568
+				const obj = {
569
+					before: e.range.before,
570
+					after: e.range.after,
571
+					data: e.range.data,
572
+					fulldate: e.fulldate
573
+				}
574
+				this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
575
+			},
576
+
577
+			rightChange(e) {
578
+				const {
579
+					before,
580
+					after
581
+				} = e.range
582
+				this.rangeChange(before, after)
583
+				const obj = {
584
+					before: e.range.before,
585
+					after: e.range.after,
586
+					data: e.range.data,
587
+					fulldate: e.fulldate
588
+				}
589
+				this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
590
+			},
591
+
592
+			mobileChange(e) {
593
+				if (this.isRange) {
594
+					const {
595
+						before,
596
+						after
597
+					} = e.range
598
+					this.handleStartAndEnd(before, after, true)
599
+					if (this.hasTime) {
600
+						const {
601
+							startTime,
602
+							endTime
603
+						} = e.timeRange
604
+						this.tempRange.startTime = startTime
605
+						this.tempRange.endTime = endTime
606
+					}
607
+					this.confirmRangeChange()
608
+
609
+				} else {
610
+					if (this.hasTime) {
611
+						this.singleVal = e.fulldate + ' ' + e.time
612
+					} else {
613
+						this.singleVal = e.fulldate
614
+					}
615
+					this.setEmit(this.singleVal)
616
+				}
617
+				this.$refs.mobile.close()
618
+			},
619
+
620
+			rangeChange(before, after) {
621
+				if (!(before && after)) return
622
+				this.handleStartAndEnd(before, after, true)
623
+				if (this.hasTime) return
624
+				this.confirmRangeChange()
625
+			},
626
+
627
+			confirmRangeChange() {
628
+				if (!this.tempRange.startDate && !this.tempRange.endDate) {
629
+					this.popup = false
630
+					return
631
+				}
632
+				let start, end
633
+				if (!this.hasTime) {
634
+					start = this.range.startDate = this.tempRange.startDate
635
+					end = this.range.endDate = this.tempRange.endDate
636
+				} else {
637
+					start = this.range.startDate = this.tempRange.startDate + ' ' +
638
+						(this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
639
+					end = this.range.endDate = this.tempRange.endDate + ' ' +
640
+						(this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
641
+				}
642
+				const displayRange = [start, end]
643
+				this.setEmit(displayRange)
644
+				this.popup = false
645
+			},
646
+
647
+			handleStartAndEnd(before, after, temp = false) {
648
+				if (!(before && after)) return
649
+				const type = temp ? 'tempRange' : 'range'
650
+				if (this.dateCompare(before, after)) {
651
+					this[type].startDate = before
652
+					this[type].endDate = after
653
+				} else {
654
+					this[type].startDate = after
655
+					this[type].endDate = before
656
+				}
657
+			},
658
+
659
+			/**
660
+			 * 比较时间大小
661
+			 */
662
+			dateCompare(startDate, endDate) {
663
+				// 计算截止时间
664
+				startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
665
+				// 计算详细项的截止时间
666
+				endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
667
+				if (startDate <= endDate) {
668
+					return true
669
+				} else {
670
+					return false
671
+				}
672
+			},
673
+
674
+			/**
675
+			 * 比较时间差
676
+			 */
677
+			diffDate(startDate, endDate) {
678
+				// 计算截止时间
679
+				startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
680
+				// 计算详细项的截止时间
681
+				endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
682
+				const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
683
+				return Math.abs(diff)
684
+			},
685
+
686
+			clear(needEmit = true) {
687
+				if (!this.isRange) {
688
+					this.singleVal = ''
689
+					this.tempSingleDate = ''
690
+					this.time = ''
691
+					if (this.isPhone) {
692
+						this.$refs.mobile && this.$refs.mobile.clearCalender()
693
+					} else {
694
+						this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
695
+					}
696
+					if (needEmit) {
697
+						this.formItem && this.formItem.setValue('')
698
+						this.$emit('change', '')
699
+						this.$emit('input', '')
700
+						this.$emit('update:modelValue', '')
701
+					}
702
+				} else {
703
+					this.range.startDate = ''
704
+					this.range.endDate = ''
705
+					this.tempRange.startDate = ''
706
+					this.tempRange.startTime = ''
707
+					this.tempRange.endDate = ''
708
+					this.tempRange.endTime = ''
709
+					if (this.isPhone) {
710
+						this.$refs.mobile && this.$refs.mobile.clearCalender()
711
+					} else {
712
+						this.$refs.left && this.$refs.left.clearCalender()
713
+						this.$refs.right && this.$refs.right.clearCalender()
714
+						this.$refs.right && this.$refs.right.next()
715
+					}
716
+					if (needEmit) {
717
+						this.formItem && this.formItem.setValue([])
718
+						this.$emit('change', [])
719
+						this.$emit('input', [])
720
+						this.$emit('update:modelValue', [])
721
+					}
722
+				}
723
+			},
724
+
725
+			parseDate(date) {
726
+				date = this.fixIosDateFormat(date)
727
+				const defVal = new Date(date)
728
+				const year = defVal.getFullYear()
729
+				const month = defVal.getMonth() + 1
730
+				const day = defVal.getDate()
731
+				const hour = defVal.getHours()
732
+				const minute = defVal.getMinutes()
733
+				const second = defVal.getSeconds()
734
+				const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
735
+				const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
736
+					.lessTen(second)))
737
+				return {
738
+					defDate,
739
+					defTime
740
+				}
741
+			},
742
+
743
+			lessTen(item) {
744
+				return item < 10 ? '0' + item : item
745
+			},
746
+
747
+			//兼容 iOS、safari 日期格式
748
+			fixIosDateFormat(value) {
749
+				if (typeof value === 'string') {
750
+					value = value.replace(/-/g, '/')
751
+				}
752
+				return value
753
+			},
754
+
755
+			leftMonthSwitch(e) {
756
+				// console.log('leftMonthSwitch 返回:', e)
757
+			},
758
+			rightMonthSwitch(e) {
759
+				// console.log('rightMonthSwitch 返回:', e)
760
+			}
761
+		}
762
+	}
763
+</script>
764
+
765
+<style>
766
+	.uni-date-x {
767
+		display: flex;
768
+		flex-direction: row;
769
+		align-items: center;
770
+		justify-content: center;
771
+		padding: 0 10px;
772
+		border-radius: 4px;
773
+		background-color: #fff;
774
+		color: #666;
775
+		font-size: 14px;
776
+	}
777
+
778
+	.uni-date-x--border {
779
+		box-sizing: border-box;
780
+		border-radius: 4px;
781
+		border: 1px solid #dcdfe6;
782
+	}
783
+
784
+	.uni-date-editor--x {
785
+		position: relative;
786
+	}
787
+
788
+	.uni-date-editor--x .uni-date__icon-clear {
789
+		position: absolute;
790
+		top: 0;
791
+		right: 0;
792
+		display: inline-block;
793
+		box-sizing: border-box;
794
+		border: 9px solid transparent;
795
+		/* #ifdef H5 */
796
+		cursor: pointer;
797
+		/* #endif */
798
+	}
799
+
800
+	.uni-date__x-input {
801
+		padding: 0 8px;
802
+		height: 40px;
803
+		width: 100%;
804
+		line-height: 40px;
805
+		font-size: 14px;
806
+	}
807
+
808
+	.t-c {
809
+		text-align: center;
810
+	}
811
+
812
+	.uni-date__input {
813
+		height: 40px;
814
+		width: 100%;
815
+		line-height: 40px;
816
+		font-size: 14px;
817
+	}
818
+
819
+	.uni-date-range__input {
820
+		text-align: center;
821
+		max-width: 142px;
822
+	}
823
+
824
+	.uni-date-picker__container {
825
+		position: relative;
826
+		/* 		position: fixed;
827
+		left: 0;
828
+		right: 0;
829
+		top: 0;
830
+		bottom: 0;
831
+		box-sizing: border-box;
832
+		z-index: 996;
833
+		font-size: 14px; */
834
+	}
835
+
836
+	.uni-date-mask {
837
+		position: fixed;
838
+		bottom: 0px;
839
+		top: 0px;
840
+		left: 0px;
841
+		right: 0px;
842
+		background-color: rgba(0, 0, 0, 0);
843
+		transition-duration: 0.3s;
844
+		z-index: 996;
845
+	}
846
+
847
+	.uni-date-single--x {
848
+		/* padding: 0 8px; */
849
+		background-color: #fff;
850
+		position: absolute;
851
+		top: 0;
852
+		z-index: 999;
853
+		border: 1px solid #EBEEF5;
854
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
855
+		border-radius: 4px;
856
+	}
857
+
858
+	.uni-date-range--x {
859
+		/* padding: 0 8px; */
860
+		background-color: #fff;
861
+		position: absolute;
862
+		top: 0;
863
+		z-index: 999;
864
+		border: 1px solid #EBEEF5;
865
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
866
+		border-radius: 4px;
867
+	}
868
+
869
+	.uni-date-editor--x__disabled {
870
+		opacity: 0.4;
871
+		cursor: default;
872
+	}
873
+
874
+	.uni-date-editor--logo {
875
+		width: 16px;
876
+		height: 16px;
877
+		vertical-align: middle;
878
+	}
879
+
880
+	/* 添加时间 */
881
+	.popup-x-header {
882
+		/* #ifndef APP-NVUE */
883
+		display: flex;
884
+		/* #endif */
885
+		flex-direction: row;
886
+		/* justify-content: space-between; */
887
+	}
888
+
889
+	.popup-x-header--datetime {
890
+		/* #ifndef APP-NVUE */
891
+		display: flex;
892
+		/* #endif */
893
+		flex-direction: row;
894
+		flex: 1;
895
+	}
896
+
897
+	.popup-x-body {
898
+		display: flex;
899
+	}
900
+
901
+	.popup-x-footer {
902
+		padding: 0 15px;
903
+		border-top-color: #F1F1F1;
904
+		border-top-style: solid;
905
+		border-top-width: 1px;
906
+		/* background-color: #fff; */
907
+		line-height: 40px;
908
+		text-align: right;
909
+		color: #666;
910
+	}
911
+
912
+	.popup-x-footer text:hover {
913
+		color: #007aff;
914
+		cursor: pointer;
915
+		opacity: 0.8;
916
+	}
917
+
918
+	.popup-x-footer .confirm {
919
+		margin-left: 20px;
920
+		color: #007aff;
921
+	}
922
+
923
+	.uni-date-changed {
924
+		/* background-color: #fff; */
925
+		text-align: center;
926
+		color: #333;
927
+		border-bottom-color: #F1F1F1;
928
+		border-bottom-style: solid;
929
+		border-bottom-width: 1px;
930
+		/* padding: 0 50px; */
931
+	}
932
+
933
+	.uni-date-changed--time text {
934
+		/* padding: 0 20px; */
935
+		height: 50px;
936
+		line-height: 50px;
937
+	}
938
+
939
+	.uni-date-changed .uni-date-changed--time {
940
+		/* display: flex; */
941
+		flex: 1;
942
+	}
943
+
944
+	.uni-date-changed--time-date {
945
+		color: #333;
946
+		opacity: 0.6;
947
+	}
948
+
949
+	.mr-50 {
950
+		margin-right: 50px;
951
+	}
952
+
953
+	/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
954
+	.uni-popper__arrow,
955
+	.uni-popper__arrow::after {
956
+		position: absolute;
957
+		display: block;
958
+		width: 0;
959
+		height: 0;
960
+		border-color: transparent;
961
+		border-style: solid;
962
+		border-width: 6px;
963
+	}
964
+
965
+	.uni-popper__arrow {
966
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
967
+		top: -6px;
968
+		left: 10%;
969
+		margin-right: 3px;
970
+		border-top-width: 0;
971
+		border-bottom-color: #EBEEF5;
972
+	}
973
+
974
+	.uni-popper__arrow::after {
975
+		content: " ";
976
+		top: 1px;
977
+		margin-left: -6px;
978
+		border-top-width: 0;
979
+		border-bottom-color: #fff;
980
+	}
981
+</style>

+ 410 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js

@@ -0,0 +1,410 @@
1
+class Calendar {
2
+	constructor({
3
+		date,
4
+		selected,
5
+		startDate,
6
+		endDate,
7
+		range,
8
+		// multipleStatus
9
+	} = {}) {
10
+		// 当前日期
11
+		this.date = this.getDate(new Date()) // 当前初入日期
12
+		// 打点信息
13
+		this.selected = selected || [];
14
+		// 范围开始
15
+		this.startDate = startDate
16
+		// 范围结束
17
+		this.endDate = endDate
18
+		this.range = range
19
+		// 多选状态
20
+		this.cleanMultipleStatus()
21
+		// 每周日期
22
+		this.weeks = {}
23
+		// this._getWeek(this.date.fullDate)
24
+		// this.multipleStatus = multipleStatus
25
+		this.lastHover = false
26
+	}
27
+	/**
28
+	 * 设置日期
29
+	 * @param {Object} date
30
+	 */
31
+	setDate(date) {
32
+		this.selectDate = this.getDate(date)
33
+		this._getWeek(this.selectDate.fullDate)
34
+	}
35
+
36
+	/**
37
+	 * 清理多选状态
38
+	 */
39
+	cleanMultipleStatus() {
40
+		this.multipleStatus = {
41
+			before: '',
42
+			after: '',
43
+			data: []
44
+		}
45
+	}
46
+
47
+	/**
48
+	 * 重置开始日期
49
+	 */
50
+	resetSatrtDate(startDate) {
51
+		// 范围开始
52
+		this.startDate = startDate
53
+
54
+	}
55
+
56
+	/**
57
+	 * 重置结束日期
58
+	 */
59
+	resetEndDate(endDate) {
60
+		// 范围结束
61
+		this.endDate = endDate
62
+	}
63
+
64
+	/**
65
+	 * 获取任意时间
66
+	 */
67
+	getDate(date, AddDayCount = 0, str = 'day') {
68
+		if (!date) {
69
+			date = new Date()
70
+		}
71
+		if (typeof date !== 'object') {
72
+			date = date.replace(/-/g, '/')
73
+		}
74
+		const dd = new Date(date)
75
+		switch (str) {
76
+			case 'day':
77
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
78
+				break
79
+			case 'month':
80
+				if (dd.getDate() === 31) {
81
+					dd.setDate(dd.getDate() + AddDayCount)
82
+				} else {
83
+					dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
84
+				}
85
+				break
86
+			case 'year':
87
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
88
+				break
89
+		}
90
+		const y = dd.getFullYear()
91
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
92
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
93
+		return {
94
+			fullDate: y + '-' + m + '-' + d,
95
+			year: y,
96
+			month: m,
97
+			date: d,
98
+			day: dd.getDay()
99
+		}
100
+	}
101
+
102
+
103
+	/**
104
+	 * 获取上月剩余天数
105
+	 */
106
+	_getLastMonthDays(firstDay, full) {
107
+		let dateArr = []
108
+		for (let i = firstDay; i > 0; i--) {
109
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
110
+			dateArr.push({
111
+				date: beforeDate,
112
+				month: full.month - 1,
113
+				disable: true
114
+			})
115
+		}
116
+		return dateArr
117
+	}
118
+	/**
119
+	 * 获取本月天数
120
+	 */
121
+	_currentMonthDys(dateData, full) {
122
+		let dateArr = []
123
+		let fullDate = this.date.fullDate
124
+		for (let i = 1; i <= dateData; i++) {
125
+			let isinfo = false
126
+			let nowDate = full.year + '-' + (full.month < 10 ?
127
+				full.month : full.month) + '-' + (i < 10 ?
128
+				'0' + i : i)
129
+			// 是否今天
130
+			let isDay = fullDate === nowDate
131
+			// 获取打点信息
132
+			let info = this.selected && this.selected.find((item) => {
133
+				if (this.dateEqual(nowDate, item.date)) {
134
+					return item
135
+				}
136
+			})
137
+
138
+			// 日期禁用
139
+			let disableBefore = true
140
+			let disableAfter = true
141
+			if (this.startDate) {
142
+				// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
143
+				// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
144
+				disableBefore = this.dateCompare(this.startDate, nowDate)
145
+			}
146
+
147
+			if (this.endDate) {
148
+				// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
149
+				// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
150
+				disableAfter = this.dateCompare(nowDate, this.endDate)
151
+			}
152
+			let multiples = this.multipleStatus.data
153
+			let checked = false
154
+			let multiplesStatus = -1
155
+			if (this.range) {
156
+				if (multiples) {
157
+					multiplesStatus = multiples.findIndex((item) => {
158
+						return this.dateEqual(item, nowDate)
159
+					})
160
+				}
161
+				if (multiplesStatus !== -1) {
162
+					checked = true
163
+				}
164
+			}
165
+			let data = {
166
+				fullDate: nowDate,
167
+				year: full.year,
168
+				date: i,
169
+				multiple: this.range ? checked : false,
170
+				beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
171
+				afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
172
+				month: full.month,
173
+				disable: !(disableBefore && disableAfter),
174
+				isDay,
175
+				userChecked: false
176
+			}
177
+			if (info) {
178
+				data.extraInfo = info
179
+			}
180
+
181
+			dateArr.push(data)
182
+		}
183
+		return dateArr
184
+	}
185
+	/**
186
+	 * 获取下月天数
187
+	 */
188
+	_getNextMonthDays(surplus, full) {
189
+		let dateArr = []
190
+		for (let i = 1; i < surplus + 1; i++) {
191
+			dateArr.push({
192
+				date: i,
193
+				month: Number(full.month) + 1,
194
+				disable: true
195
+			})
196
+		}
197
+		return dateArr
198
+	}
199
+
200
+	/**
201
+	 * 获取当前日期详情
202
+	 * @param {Object} date
203
+	 */
204
+	getInfo(date) {
205
+		if (!date) {
206
+			date = new Date()
207
+		}
208
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
209
+		return dateInfo
210
+	}
211
+
212
+	/**
213
+	 * 比较时间大小
214
+	 */
215
+	dateCompare(startDate, endDate) {
216
+		// 计算截止时间
217
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
218
+		// 计算详细项的截止时间
219
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
220
+		if (startDate <= endDate) {
221
+			return true
222
+		} else {
223
+			return false
224
+		}
225
+	}
226
+
227
+	/**
228
+	 * 比较时间是否相等
229
+	 */
230
+	dateEqual(before, after) {
231
+		// 计算截止时间
232
+		before = new Date(before.replace('-', '/').replace('-', '/'))
233
+		// 计算详细项的截止时间
234
+		after = new Date(after.replace('-', '/').replace('-', '/'))
235
+		if (before.getTime() - after.getTime() === 0) {
236
+			return true
237
+		} else {
238
+			return false
239
+		}
240
+	}
241
+
242
+	/**
243
+	 *  比较真实起始日期
244
+	 */
245
+
246
+	isLogicBefore(currentDay, before, after) {
247
+		let logicBefore = before
248
+		if (before && after) {
249
+			logicBefore = this.dateCompare(before, after) ? before : after
250
+		}
251
+		return this.dateEqual(logicBefore, currentDay)
252
+	}
253
+
254
+	isLogicAfter(currentDay, before, after) {
255
+		let logicAfter = after
256
+		if (before && after) {
257
+			logicAfter = this.dateCompare(before, after) ? after : before
258
+		}
259
+		return this.dateEqual(logicAfter, currentDay)
260
+	}
261
+
262
+	/**
263
+	 * 获取日期范围内所有日期
264
+	 * @param {Object} begin
265
+	 * @param {Object} end
266
+	 */
267
+	geDateAll(begin, end) {
268
+		var arr = []
269
+		var ab = begin.split('-')
270
+		var ae = end.split('-')
271
+		var db = new Date()
272
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
273
+		var de = new Date()
274
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
275
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
276
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
277
+		for (var k = unixDb; k <= unixDe;) {
278
+			k = k + 24 * 60 * 60 * 1000
279
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
280
+		}
281
+		return arr
282
+	}
283
+
284
+	/**
285
+	 *  获取多选状态
286
+	 */
287
+	setMultiple(fullDate) {
288
+		let {
289
+			before,
290
+			after
291
+		} = this.multipleStatus
292
+		if (!this.range) return
293
+		if (before && after) {
294
+			if (!this.lastHover) {
295
+				this.lastHover = true
296
+				return
297
+			}
298
+			this.multipleStatus.before = fullDate
299
+			this.multipleStatus.after = ''
300
+			this.multipleStatus.data = []
301
+			this.multipleStatus.fulldate = ''
302
+			this.lastHover = false
303
+		} else {
304
+			if (!before) {
305
+				this.multipleStatus.before = fullDate
306
+				this.lastHover = false
307
+			} else {
308
+				this.multipleStatus.after = fullDate
309
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
310
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
311
+						.after);
312
+				} else {
313
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
314
+						.before);
315
+				}
316
+				this.lastHover = true
317
+			}
318
+		}
319
+		this._getWeek(fullDate)
320
+	}
321
+
322
+	/**
323
+	 *  鼠标 hover 更新多选状态
324
+	 */
325
+	setHoverMultiple(fullDate) {
326
+		let {
327
+			before,
328
+			after
329
+		} = this.multipleStatus
330
+
331
+		if (!this.range) return
332
+		if (this.lastHover) return
333
+
334
+		if (!before) {
335
+			this.multipleStatus.before = fullDate
336
+		} else {
337
+			this.multipleStatus.after = fullDate
338
+			if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
339
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
340
+			} else {
341
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
342
+			}
343
+		}
344
+		this._getWeek(fullDate)
345
+	}
346
+
347
+	/**
348
+	 * 更新默认值多选状态
349
+	 */
350
+	setDefaultMultiple(before, after) {
351
+		this.multipleStatus.before = before
352
+		this.multipleStatus.after = after
353
+		if (before && after) {
354
+			if (this.dateCompare(before, after)) {
355
+				this.multipleStatus.data = this.geDateAll(before, after);
356
+				this._getWeek(after)
357
+			} else {
358
+				this.multipleStatus.data = this.geDateAll(after, before);
359
+				this._getWeek(before)
360
+			}
361
+		}
362
+	}
363
+
364
+	/**
365
+	 * 获取每周数据
366
+	 * @param {Object} dateData
367
+	 */
368
+	_getWeek(dateData) {
369
+		const {
370
+			fullDate,
371
+			year,
372
+			month,
373
+			date,
374
+			day
375
+		} = this.getDate(dateData)
376
+		let firstDay = new Date(year, month - 1, 1).getDay()
377
+		let currentDay = new Date(year, month, 0).getDate()
378
+		let dates = {
379
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
380
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
381
+			nextMonthDays: [], // 下个月开始几天
382
+			weeks: []
383
+		}
384
+		let canlender = []
385
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
386
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
387
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
388
+		let weeks = {}
389
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
390
+		for (let i = 0; i < canlender.length; i++) {
391
+			if (i % 7 === 0) {
392
+				weeks[parseInt(i / 7)] = new Array(7)
393
+			}
394
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
395
+		}
396
+		this.canlender = canlender
397
+		this.weeks = weeks
398
+	}
399
+
400
+	//静态方法
401
+	// static init(date) {
402
+	// 	if (!this.instance) {
403
+	// 		this.instance = new Calendar(date);
404
+	// 	}
405
+	// 	return this.instance;
406
+	// }
407
+}
408
+
409
+
410
+export default Calendar

+ 90 - 0
uni_modules/uni-datetime-picker/package.json

@@ -0,0 +1,90 @@
1
+{
2
+  "id": "uni-datetime-picker",
3
+  "displayName": "uni-datetime-picker 日期选择器",
4
+  "version": "2.2.2",
5
+  "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
6
+  "keywords": [
7
+    "uni-datetime-picker",
8
+    "uni-ui",
9
+    "uniui",
10
+    "日期时间选择器",
11
+    "日期时间"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": ""
16
+  },
17
+  "directories": {
18
+    "example": "../../temps/example_temps"
19
+  },
20
+  "dcloudext": {
21
+    "category": [
22
+      "前端组件",
23
+      "通用组件"
24
+    ],
25
+    "sale": {
26
+      "regular": {
27
+        "price": "0.00"
28
+      },
29
+      "sourcecode": {
30
+        "price": "0.00"
31
+      }
32
+    },
33
+    "contact": {
34
+      "qq": ""
35
+    },
36
+    "declaration": {
37
+      "ads": "无",
38
+      "data": "无",
39
+      "permissions": "无"
40
+    },
41
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
42
+  },
43
+  "uni_modules": {
44
+    "dependencies": [
45
+			"uni-scss",
46
+			"uni-icons"
47
+		],
48
+    "encrypt": [],
49
+    "platforms": {
50
+      "cloud": {
51
+        "tcb": "y",
52
+        "aliyun": "y"
53
+      },
54
+      "client": {
55
+        "App": {
56
+          "app-vue": "y",
57
+          "app-nvue": "n"
58
+        },
59
+        "H5-mobile": {
60
+          "Safari": "y",
61
+          "Android Browser": "y",
62
+          "微信浏览器(Android)": "y",
63
+          "QQ浏览器(Android)": "y"
64
+        },
65
+        "H5-pc": {
66
+          "Chrome": "y",
67
+          "IE": "y",
68
+          "Edge": "y",
69
+          "Firefox": "y",
70
+          "Safari": "y"
71
+        },
72
+        "小程序": {
73
+          "微信": "y",
74
+          "阿里": "y",
75
+          "百度": "y",
76
+          "字节跳动": "y",
77
+          "QQ": "y"
78
+        },
79
+        "快应用": {
80
+          "华为": "u",
81
+          "联盟": "u"
82
+        },
83
+        "Vue": {
84
+            "vue2": "y",
85
+            "vue3": "y"
86
+        }
87
+      }
88
+    }
89
+  }
90
+}

+ 21 - 0
uni_modules/uni-datetime-picker/readme.md

@@ -0,0 +1,21 @@
1
+
2
+
3
+> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
4
+
5
+## DatetimePicker 时间选择器
6
+
7
+> **组件名:uni-datetime-picker**
8
+> 代码块: `uDatetimePicker`
9
+
10
+
11
+该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
12
+
13
+若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
14
+
15
+**_点击 picker 默认值规则:_**
16
+
17
+- 若设置初始值 value, 会显示在 picker 显示框中
18
+- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
19
+
20
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
21
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 13 - 0
uni_modules/uni-drawer/changelog.md

@@ -0,0 +1,13 @@
1
+## 1.2.1(2021-11-22)
2
+- 修复 vue3中个别scss变量无法找到的问题
3
+## 1.2.0(2021-11-19)
4
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
5
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-drawer](https://uniapp.dcloud.io/component/uniui/uni-drawer)
6
+## 1.1.1(2021-07-30)
7
+- 优化 vue3下事件警告的问题
8
+## 1.1.0(2021-07-13)
9
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
10
+## 1.0.7(2021-05-12)
11
+- 新增 组件示例地址
12
+## 1.0.6(2021-02-04)
13
+- 调整为uni_modules目录规范

+ 45 - 0
uni_modules/uni-drawer/components/uni-drawer/keypress.js

@@ -0,0 +1,45 @@
1
+// #ifdef H5
2
+export default {
3
+  name: 'Keypress',
4
+  props: {
5
+    disable: {
6
+      type: Boolean,
7
+      default: false
8
+    }
9
+  },
10
+  mounted () {
11
+    const keyNames = {
12
+      esc: ['Esc', 'Escape'],
13
+      tab: 'Tab',
14
+      enter: 'Enter',
15
+      space: [' ', 'Spacebar'],
16
+      up: ['Up', 'ArrowUp'],
17
+      left: ['Left', 'ArrowLeft'],
18
+      right: ['Right', 'ArrowRight'],
19
+      down: ['Down', 'ArrowDown'],
20
+      delete: ['Backspace', 'Delete', 'Del']
21
+    }
22
+    const listener = ($event) => {
23
+      if (this.disable) {
24
+        return
25
+      }
26
+      const keyName = Object.keys(keyNames).find(key => {
27
+        const keyName = $event.key
28
+        const value = keyNames[key]
29
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
30
+      })
31
+      if (keyName) {
32
+        // 避免和其他按键事件冲突
33
+        setTimeout(() => {
34
+          this.$emit(keyName, {})
35
+        }, 0)
36
+      }
37
+    }
38
+    document.addEventListener('keyup', listener)
39
+    // this.$once('hook:beforeDestroy', () => {
40
+    //   document.removeEventListener('keyup', listener)
41
+    // })
42
+  },
43
+	render: () => {}
44
+}
45
+// #endif

+ 183 - 0
uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue

@@ -0,0 +1,183 @@
1
+<template>
2
+	<view v-if="visibleSync" :class="{ 'uni-drawer--visible': showDrawer }" class="uni-drawer" @touchmove.stop.prevent="clear">
3
+		<view class="uni-drawer__mask" :class="{ 'uni-drawer__mask--visible': showDrawer && mask }" @tap="close('mask')" />
4
+		<view class="uni-drawer__content" :class="{'uni-drawer--right': rightMode,'uni-drawer--left': !rightMode, 'uni-drawer__content--visible': showDrawer}" :style="{width:drawerWidth+'px'}">
5
+			<slot />
6
+		</view>
7
+		<!-- #ifdef H5 -->
8
+		<keypress @esc="close('mask')" />
9
+		<!-- #endif -->
10
+	</view>
11
+</template>
12
+
13
+<script>
14
+	// #ifdef H5
15
+	import keypress from './keypress.js'
16
+	// #endif
17
+	/**
18
+	 * Drawer 抽屉
19
+	 * @description 抽屉侧滑菜单
20
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=26
21
+	 * @property {Boolean} mask = [true | false] 是否显示遮罩
22
+	 * @property {Boolean} maskClick = [true | false] 点击遮罩是否关闭
23
+	 * @property {Boolean} mode = [left | right] Drawer 滑出位置
24
+	 * 	@value left 从左侧滑出
25
+	 * 	@value right 从右侧侧滑出
26
+	 * @property {Number} width 抽屉的宽度 ,仅 vue 页面生效
27
+	 * @event {Function} close 组件关闭时触发事件
28
+	 */
29
+	export default {
30
+		name: 'UniDrawer',
31
+		components: {
32
+			// #ifdef H5
33
+			keypress
34
+			// #endif
35
+		},
36
+		emits:['change'],
37
+		props: {
38
+			/**
39
+			 * 显示模式(左、右),只在初始化生效
40
+			 */
41
+			mode: {
42
+				type: String,
43
+				default: ''
44
+			},
45
+			/**
46
+			 * 蒙层显示状态
47
+			 */
48
+			mask: {
49
+				type: Boolean,
50
+				default: true
51
+			},
52
+			/**
53
+			 * 遮罩是否可点击关闭
54
+			 */
55
+			maskClick:{
56
+				type: Boolean,
57
+				default: true
58
+			},
59
+			/**
60
+			 * 抽屉宽度
61
+			 */
62
+			width: {
63
+				type: Number,
64
+				default: 220
65
+			}
66
+		},
67
+		data() {
68
+			return {
69
+				visibleSync: false,
70
+				showDrawer: false,
71
+				rightMode: false,
72
+				watchTimer: null,
73
+				drawerWidth: 220
74
+			}
75
+		},
76
+		created() {
77
+			// #ifndef APP-NVUE
78
+			this.drawerWidth = this.width
79
+			// #endif
80
+			this.rightMode = this.mode === 'right'
81
+		},
82
+		methods: {
83
+			clear(){},
84
+			close(type) {
85
+				// fixed by mehaotian 抽屉尚未完全关闭或遮罩禁止点击时不触发以下逻辑
86
+				if((type === 'mask' && !this.maskClick) || !this.visibleSync) return
87
+				this._change('showDrawer', 'visibleSync', false)
88
+			},
89
+			open() {
90
+				// fixed by mehaotian 处理重复点击打开的事件
91
+				if(this.visibleSync) return
92
+				this._change('visibleSync', 'showDrawer', true)
93
+			},
94
+			_change(param1, param2, status) {
95
+				this[param1] = status
96
+				if (this.watchTimer) {
97
+					clearTimeout(this.watchTimer)
98
+				}
99
+				this.watchTimer = setTimeout(() => {
100
+					this[param2] = status
101
+					this.$emit('change',status)
102
+				}, status ? 50 : 300)
103
+			}
104
+		}
105
+	}
106
+</script>
107
+
108
+<style lang="scss" >
109
+	$uni-mask: rgba($color: #000000, $alpha: 0.4) ;
110
+	// 抽屉宽度
111
+	$drawer-width: 220px;
112
+
113
+	.uni-drawer {
114
+		/* #ifndef APP-NVUE */
115
+		display: block;
116
+		/* #endif */
117
+		position: fixed;
118
+		top: 0;
119
+		left: 0;
120
+		right: 0;
121
+		bottom: 0;
122
+		overflow: hidden;
123
+		z-index: 999;
124
+	}
125
+
126
+	.uni-drawer__content {
127
+		/* #ifndef APP-NVUE */
128
+		display: block;
129
+		/* #endif */
130
+		position: absolute;
131
+		top: 0;
132
+		width: $drawer-width;
133
+		bottom: 0;
134
+		background-color: $uni-bg-color;
135
+		transition: transform 0.3s ease;
136
+	}
137
+
138
+	.uni-drawer--left {
139
+		left: 0;
140
+		/* #ifdef APP-NVUE */
141
+		transform: translateX(-$drawer-width);
142
+		/* #endif */
143
+		/* #ifndef APP-NVUE */
144
+		transform: translateX(-100%);
145
+		/* #endif */
146
+	}
147
+
148
+	.uni-drawer--right {
149
+		right: 0;
150
+		/* #ifdef APP-NVUE */
151
+		transform: translateX($drawer-width);
152
+		/* #endif */
153
+		/* #ifndef APP-NVUE */
154
+		transform: translateX(100%);
155
+		/* #endif */
156
+	}
157
+
158
+	.uni-drawer__content--visible {
159
+		transform: translateX(0px);
160
+	}
161
+
162
+
163
+	.uni-drawer__mask {
164
+		/* #ifndef APP-NVUE */
165
+		display: block;
166
+		/* #endif */
167
+		opacity: 0;
168
+		position: absolute;
169
+		top: 0;
170
+		left: 0;
171
+		bottom: 0;
172
+		right: 0;
173
+		background-color: $uni-mask;
174
+		transition: opacity 0.3s;
175
+	}
176
+
177
+	.uni-drawer__mask--visible {
178
+		/* #ifndef APP-NVUE */
179
+		display: block;
180
+		/* #endif */
181
+		opacity: 1;
182
+	}
183
+</style>

+ 87 - 0
uni_modules/uni-drawer/package.json

@@ -0,0 +1,87 @@
1
+{
2
+  "id": "uni-drawer",
3
+  "displayName": "uni-drawer 抽屉",
4
+  "version": "1.2.1",
5
+  "description": "抽屉式导航,用于展示侧滑菜单,侧滑导航。",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "drawer",
10
+    "抽屉",
11
+    "侧滑导航"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": ""
16
+  },
17
+  "directories": {
18
+    "example": "../../temps/example_temps"
19
+  },
20
+  "dcloudext": {
21
+    "category": [
22
+      "前端组件",
23
+      "通用组件"
24
+    ],
25
+    "sale": {
26
+      "regular": {
27
+        "price": "0.00"
28
+      },
29
+      "sourcecode": {
30
+        "price": "0.00"
31
+      }
32
+    },
33
+    "contact": {
34
+      "qq": ""
35
+    },
36
+    "declaration": {
37
+      "ads": "无",
38
+      "data": "无",
39
+      "permissions": "无"
40
+    },
41
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
42
+  },
43
+  "uni_modules": {
44
+    "dependencies": ["uni-scss"],
45
+    "encrypt": [],
46
+    "platforms": {
47
+      "cloud": {
48
+        "tcb": "y",
49
+        "aliyun": "y"
50
+      },
51
+      "client": {
52
+        "App": {
53
+          "app-vue": "y",
54
+          "app-nvue": "y"
55
+        },
56
+        "H5-mobile": {
57
+          "Safari": "y",
58
+          "Android Browser": "y",
59
+          "微信浏览器(Android)": "y",
60
+          "QQ浏览器(Android)": "y"
61
+        },
62
+        "H5-pc": {
63
+          "Chrome": "y",
64
+          "IE": "y",
65
+          "Edge": "y",
66
+          "Firefox": "y",
67
+          "Safari": "y"
68
+        },
69
+        "小程序": {
70
+          "微信": "y",
71
+          "阿里": "y",
72
+          "百度": "y",
73
+          "字节跳动": "y",
74
+          "QQ": "y"
75
+        },
76
+        "快应用": {
77
+          "华为": "u",
78
+          "联盟": "u"
79
+        },
80
+        "Vue": {
81
+            "vue2": "y",
82
+            "vue3": "y"
83
+        }
84
+      }
85
+    }
86
+  }
87
+}

+ 10 - 0
uni_modules/uni-drawer/readme.md

@@ -0,0 +1,10 @@
1
+
2
+
3
+## Drawer 抽屉
4
+> **组件名:uni-drawer**
5
+> 代码块: `uDrawer`
6
+
7
+抽屉侧滑菜单。
8
+
9
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-drawer)
10
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 31 - 0
uni_modules/uni-easyinput/changelog.md

@@ -0,0 +1,31 @@
1
+## 1.0.0(2021-11-19)
2
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
3
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
4
+## 0.1.4(2021-08-20)
5
+- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
6
+## 0.1.3(2021-08-11)
7
+- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
8
+## 0.1.2(2021-07-30)
9
+- 优化 vue3下事件警告的问题
10
+## 0.1.1
11
+- 优化 errorMessage 属性支持 Boolean 类型
12
+## 0.1.0(2021-07-13)
13
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
14
+## 0.0.16(2021-06-29)
15
+- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
16
+## 0.0.15(2021-06-21)
17
+- 修复 passwordIcon 属性拼写错误的 bug
18
+## 0.0.14(2021-06-18)
19
+- 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标
20
+- 修复 confirmType 属性不生效的问题
21
+## 0.0.13(2021-06-04)
22
+- 修复 disabled 状态可清出内容的 bug
23
+## 0.0.12(2021-05-12)
24
+- 新增 组件示例地址
25
+## 0.0.11(2021-05-07)
26
+- 修复 input-border 属性不生效的问题
27
+## 0.0.10(2021-04-30)
28
+- 修复 ios 遮挡文字、显示一半的问题
29
+## 0.0.9(2021-02-05)
30
+- 调整为uni_modules目录规范
31
+- 优化 兼容 nvue 页面

+ 56 - 0
uni_modules/uni-easyinput/components/uni-easyinput/common.js

@@ -0,0 +1,56 @@
1
+/**
2
+ * @desc 函数防抖
3
+ * @param func 目标函数
4
+ * @param wait 延迟执行毫秒数
5
+ * @param immediate true - 立即执行, false - 延迟执行
6
+ */
7
+export const debounce = function(func, wait = 1000, immediate = true) {
8
+	let timer;
9
+	console.log(1);
10
+	return function() {
11
+		console.log(123);
12
+		let context = this,
13
+			args = arguments;
14
+		if (timer) clearTimeout(timer);
15
+		if (immediate) {
16
+			let callNow = !timer;
17
+			timer = setTimeout(() => {
18
+				timer = null;
19
+			}, wait);
20
+			if (callNow) func.apply(context, args);
21
+		} else {
22
+			timer = setTimeout(() => {
23
+				func.apply(context, args);
24
+			}, wait)
25
+		}
26
+	}
27
+}
28
+/**
29
+ * @desc 函数节流
30
+ * @param func 函数
31
+ * @param wait 延迟执行毫秒数
32
+ * @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发
33
+ */
34
+export const throttle = (func, wait = 1000, type = 1) => {
35
+	let previous = 0;
36
+	let timeout;
37
+	return function() {
38
+		let context = this;
39
+		let args = arguments;
40
+		if (type === 1) {
41
+			let now = Date.now();
42
+
43
+			if (now - previous > wait) {
44
+				func.apply(context, args);
45
+				previous = now;
46
+			}
47
+		} else if (type === 2) {
48
+			if (!timeout) {
49
+				timeout = setTimeout(() => {
50
+					timeout = null;
51
+					func.apply(context, args)
52
+				}, wait)
53
+			}
54
+		}
55
+	}
56
+}

+ 461 - 0
uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue

@@ -0,0 +1,461 @@
1
+<template>
2
+	<view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="{color:inputBorder && msg?'#e43d33':styles.color}">
3
+		<view class="uni-easyinput__content" :class="{'is-input-border':inputBorder ,'is-input-error-border':inputBorder && msg,'is-textarea':type==='textarea','is-disabled':disabled}"
4
+		 :style="{'border-color':inputBorder && msg?'#dd524d':styles.borderColor,'background-color':disabled?styles.disableColor:''}">
5
+			<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')"></uni-icons>
6
+			<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{'input-padding':inputBorder}"
7
+			 :name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class"
8
+			 :maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" @input="onInput" @blur="onBlur"  @focus="onFocus"
9
+			 @confirm="onConfirm"></textarea>
10
+			<input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input" :style="{
11
+				 'padding-right':type === 'password' ||clearable || prefixIcon?'':'10px',
12
+				 'padding-left':prefixIcon?'':'10px'
13
+			 }"
14
+			 :name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder"
15
+			 :placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" @focus="onFocus"
16
+			 @blur="onBlur" @input="onInput" @confirm="onConfirm" />
17
+			<template v-if="type === 'password' && passwordIcon" >
18
+				<uni-icons v-if="val != '' " class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" :type="showPassword?'eye-slash-filled':'eye-filled'"
19
+				 :size="18" color="#c0c4cc" @click="onEyes"></uni-icons>
20
+			</template>
21
+			<template v-else-if="suffixIcon">
22
+				<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')"></uni-icons>
23
+			</template>
24
+			<template v-else>
25
+				<uni-icons class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize"
26
+				 v-if="clearable && val && !disabled" color="#c0c4cc" @click="onClear"></uni-icons>
27
+			</template>
28
+			<slot name="right"></slot>
29
+		</view>
30
+	</view>
31
+</template>
32
+
33
+<script>
34
+	// import {
35
+	// 	debounce,
36
+	// 	throttle
37
+	// } from './common.js'
38
+	/**
39
+	 * Easyinput 输入框
40
+	 * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
41
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3455
42
+	 * @property {String}	value	输入内容
43
+	 * @property {String }	type	输入框的类型(默认text) password/text/textarea/..
44
+	 * 	@value text			文本输入键盘
45
+	 * 	@value textarea	多行文本输入键盘
46
+	 * 	@value password	密码输入键盘
47
+	 * 	@value number		数字输入键盘,注意iOS上app-vue弹出的数字键盘并非9宫格方式
48
+	 * 	@value idcard		身份证输入键盘,信、支付宝、百度、QQ小程序
49
+	 * 	@value digit		带小数点的数字键盘	,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持
50
+	 * @property {Boolean}	clearable	是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true)
51
+	 * @property {Boolean}	autoHeight	是否自动增高输入区域,type为textarea时有效(默认false)
52
+	 * @property {String }	placeholder	输入框的提示文字
53
+	 * @property {String }	placeholderStyle	placeholder的样式(内联样式,字符串),如"color: #ddd"
54
+	 * @property {Boolean}	focus	是否自动获得焦点(默认false)
55
+	 * @property {Boolean}	disabled	是否禁用(默认false)
56
+	 * @property {Number }	maxlength	最大输入长度,设置为 -1 的时候不限制最大长度(默认140)
57
+	 * @property {String }	confirmType	设置键盘右下角按钮的文字,仅在type="text"时生效(默认done)
58
+	 * @property {Number }	clearSize	清除图标的大小,单位px(默认15)
59
+	 * @property {String}	prefixIcon	输入框头部图标
60
+	 * @property {String}	suffixIcon	输入框尾部图标
61
+	 * @property {Boolean}	trim	是否自动去除两端的空格
62
+	 * @value both	去除两端空格
63
+	 * @value left	去除左侧空格
64
+	 * @value right	去除右侧空格
65
+	 * @value start	去除左侧空格
66
+	 * @value end		去除右侧空格
67
+	 * @value all		去除全部空格
68
+	 * @value none	不去除空格
69
+	 * @property {Boolean}	inputBorder	是否显示input输入框的边框(默认true)
70
+	 * @property {Boolean}	passwordIcon	type=password时是否显示小眼睛图标
71
+	 * @property {Object}	styles	自定义颜色
72
+	 * @event {Function}	input	输入框内容发生变化时触发
73
+	 * @event {Function}	focus	输入框获得焦点时触发
74
+	 * @event {Function}	blur	输入框失去焦点时触发
75
+	 * @event {Function}	confirm	点击完成按钮时触发
76
+	 * @event {Function}	iconClick	点击图标时触发
77
+	 * @example <uni-easyinput v-model="mobile"></uni-easyinput>
78
+	 */
79
+
80
+	 export default {
81
+		name: 'uni-easyinput',
82
+		emits:['click','iconClick','update:modelValue','input','focus','blur','confirm'],
83
+		model:{
84
+			prop:'modelValue',
85
+			event:'update:modelValue'
86
+		},
87
+		props: {
88
+			name: String,
89
+			value: [Number, String],
90
+			modelValue: [Number, String],
91
+			type: {
92
+				type: String,
93
+				default: 'text'
94
+			},
95
+			clearable: {
96
+				type: Boolean,
97
+				default: true
98
+			},
99
+			autoHeight: {
100
+				type: Boolean,
101
+				default: false
102
+			},
103
+			placeholder: String,
104
+			placeholderStyle: String,
105
+			focus: {
106
+				type: Boolean,
107
+				default: false
108
+			},
109
+			disabled: {
110
+				type: Boolean,
111
+				default: false
112
+			},
113
+			maxlength: {
114
+				type: [Number, String],
115
+				default: 140
116
+			},
117
+			confirmType: {
118
+				type: String,
119
+				default: 'done'
120
+			},
121
+			clearSize: {
122
+				type: [Number, String],
123
+				default: 15
124
+			},
125
+			inputBorder: {
126
+				type: Boolean,
127
+				default: true
128
+			},
129
+			prefixIcon: {
130
+				type: String,
131
+				default: ''
132
+			},
133
+			suffixIcon: {
134
+				type: String,
135
+				default: ''
136
+			},
137
+			trim: {
138
+				type: [Boolean, String],
139
+				default: true
140
+			},
141
+			passwordIcon:{
142
+				type: Boolean,
143
+				default: true
144
+			},
145
+			styles: {
146
+				type: Object,
147
+				default () {
148
+					return {
149
+						color: '#333',
150
+						disableColor: '#F7F6F6',
151
+						borderColor: '#e5e5e5'
152
+					}
153
+				}
154
+			},
155
+			errorMessage:{
156
+				type:[String,Boolean],
157
+				default:''
158
+			}
159
+		},
160
+		data() {
161
+			return {
162
+				focused: false,
163
+				errMsg: '',
164
+				val: '',
165
+				showMsg: '',
166
+				border: false,
167
+				isFirstBorder: false,
168
+				showClearIcon: false,
169
+				showPassword: false
170
+			};
171
+		},
172
+		computed: {
173
+			msg() {
174
+				return this.errorMessage || this.errMsg;
175
+			},
176
+			// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值
177
+			inputMaxlength() {
178
+				return Number(this.maxlength);
179
+			},
180
+		},
181
+		watch: {
182
+			value(newVal) {
183
+				if (this.errMsg) this.errMsg = ''
184
+				this.val = newVal
185
+				// fix by mehaotian is_reset 在 uni-forms 中定义
186
+				if (this.form && this.formItem &&!this.is_reset) {
187
+					this.is_reset = false
188
+					this.formItem.setValue(newVal)
189
+				}
190
+			},
191
+			modelValue(newVal) {
192
+				if (this.errMsg) this.errMsg = ''
193
+				this.val = newVal
194
+				if (this.form && this.formItem &&!this.is_reset) {
195
+					this.is_reset = false
196
+					this.formItem.setValue(newVal)
197
+				}
198
+			},
199
+			focus(newVal) {
200
+				this.$nextTick(() => {
201
+					this.focused = this.focus
202
+				})
203
+			}
204
+		},
205
+		created() {
206
+			if(!this.value){
207
+				this.val = this.modelValue
208
+			}
209
+			if(!this.modelValue){
210
+				this.val = this.value
211
+			}
212
+			this.form = this.getForm('uniForms')
213
+			this.formItem = this.getForm('uniFormsItem')
214
+			if (this.form && this.formItem) {
215
+				if (this.formItem.name) {
216
+					if(!this.is_reset){
217
+						this.is_reset = false
218
+						this.formItem.setValue(this.val)
219
+					}
220
+					this.rename = this.formItem.name
221
+					this.form.inputChildrens.push(this)
222
+				}
223
+			}
224
+		},
225
+		mounted() {
226
+			this.$nextTick(() => {
227
+				this.focused = this.focus
228
+			})
229
+		},
230
+		methods: {
231
+			/**
232
+			 * 初始化变量值
233
+			 */
234
+			init() {
235
+
236
+			},
237
+			onClickIcon(type) {
238
+				this.$emit('iconClick', type)
239
+			},
240
+			/**
241
+			 * 获取父元素实例
242
+			 */
243
+			getForm(name = 'uniForms') {
244
+				let parent = this.$parent;
245
+				let parentName = parent.$options.name;
246
+				while (parentName !== name) {
247
+					parent = parent.$parent;
248
+					if (!parent) return false;
249
+					parentName = parent.$options.name;
250
+				}
251
+				return parent;
252
+			},
253
+
254
+			onEyes() {
255
+				this.showPassword = !this.showPassword
256
+			},
257
+			onInput(event) {
258
+				let value = event.detail.value;
259
+				// 判断是否去除空格
260
+				if (this.trim) {
261
+					if (typeof(this.trim) === 'boolean' && this.trim) {
262
+						value = this.trimStr(value)
263
+					}
264
+					if (typeof(this.trim) === 'string') {
265
+						value = this.trimStr(value, this.trim)
266
+					}
267
+				};
268
+				if (this.errMsg) this.errMsg = ''
269
+				this.val = value
270
+				// TODO 兼容 vue2
271
+				this.$emit('input', value);
272
+				// TODO 兼容 vue3
273
+				this.$emit('update:modelValue',value)
274
+			},
275
+
276
+			onFocus(event) {
277
+				this.$emit('focus', event);
278
+			},
279
+			onBlur(event) {
280
+				let value = event.detail.value;
281
+				this.$emit('blur', event);
282
+			},
283
+			onConfirm(e) {
284
+				this.$emit('confirm', e.detail.value);
285
+			},
286
+			onClear(event) {
287
+				this.val = '';
288
+				// TODO 兼容 vue2
289
+				this.$emit('input', '');
290
+				// TODO 兼容 vue2
291
+				// TODO 兼容 vue3
292
+				this.$emit('update:modelValue','')
293
+			},
294
+			fieldClick() {
295
+				this.$emit('click');
296
+			},
297
+			trimStr(str, pos = 'both') {
298
+				if (pos === 'both') {
299
+					return str.trim();
300
+				} else if (pos === 'left') {
301
+					return str.trimLeft();
302
+				} else if (pos === 'right') {
303
+					return str.trimRight();
304
+				} else if (pos === 'start') {
305
+					return str.trimStart()
306
+				} else if (pos === 'end') {
307
+					return str.trimEnd()
308
+				} else if (pos === 'all') {
309
+					return str.replace(/\s+/g, '');
310
+				} else if (pos === 'none') {
311
+					return str;
312
+				}
313
+				return str;
314
+			}
315
+		}
316
+	};
317
+</script>
318
+
319
+<style lang="scss" >
320
+	$uni-error: #e43d33;
321
+	$uni-border-1: #DCDFE6 !default;
322
+	.uni-easyinput {
323
+		/* #ifndef APP-NVUE */
324
+		width: 100%;
325
+		/* #endif */
326
+		flex: 1;
327
+		position: relative;
328
+		text-align: left;
329
+		color: #333;
330
+		font-size: 14px;
331
+	}
332
+
333
+	.uni-easyinput__content {
334
+		flex: 1;
335
+		/* #ifndef APP-NVUE */
336
+		width: 100%;
337
+		display: flex;
338
+		box-sizing: border-box;
339
+		min-height: 36px;
340
+		/* #endif */
341
+		flex-direction: row;
342
+		align-items: center;
343
+	}
344
+
345
+	.uni-easyinput__content-input {
346
+		/* #ifndef APP-NVUE */
347
+		width: auto;
348
+		/* #endif */
349
+		position: relative;
350
+		overflow: hidden;
351
+		flex: 1;
352
+		line-height: 1;
353
+		font-size: 14px;
354
+	}
355
+	.uni-easyinput__placeholder-class {
356
+		color: #999;
357
+		font-size: 12px;
358
+		font-weight: 200;
359
+	}
360
+	.is-textarea {
361
+		align-items: flex-start;
362
+	}
363
+
364
+	.is-textarea-icon {
365
+		margin-top: 5px;
366
+	}
367
+
368
+	.uni-easyinput__content-textarea {
369
+		position: relative;
370
+		overflow: hidden;
371
+		flex: 1;
372
+		line-height: 1.5;
373
+		font-size: 14px;
374
+		padding-top: 6px;
375
+		padding-bottom: 10px;
376
+		height: 80px;
377
+		/* #ifndef APP-NVUE */
378
+		min-height: 80px;
379
+		width: auto;
380
+		/* #endif */
381
+	}
382
+
383
+	.input-padding {
384
+		padding-left: 10px;
385
+	}
386
+
387
+	.content-clear-icon {
388
+		padding: 0 5px;
389
+	}
390
+
391
+	.label-icon {
392
+		margin-right: 5px;
393
+		margin-top: -1px;
394
+	}
395
+
396
+	// 显示边框
397
+	.is-input-border {
398
+		/* #ifndef APP-NVUE */
399
+		display: flex;
400
+		box-sizing: border-box;
401
+		/* #endif */
402
+		flex-direction: row;
403
+		align-items: center;
404
+		border: 1px solid $uni-border-1;
405
+		border-radius: 4px;
406
+	}
407
+
408
+	.uni-error-message {
409
+		position: absolute;
410
+		bottom: -17px;
411
+		left: 0;
412
+		line-height: 12px;
413
+		color: $uni-error;
414
+		font-size: 12px;
415
+		text-align: left;
416
+	}
417
+
418
+	.uni-error-msg--boeder {
419
+		position: relative;
420
+		bottom: 0;
421
+		line-height: 22px;
422
+	}
423
+
424
+	.is-input-error-border {
425
+		border-color: $uni-error;
426
+		.uni-easyinput__placeholder-class {
427
+			color: mix(#fff, $uni-error, 50%);;
428
+		}
429
+	}
430
+
431
+
432
+	.uni-easyinput--border {
433
+		margin-bottom: 0;
434
+		padding: 10px 15px;
435
+		// padding-bottom: 0;
436
+		border-top: 1px #eee solid;
437
+	}
438
+
439
+	.uni-easyinput-error {
440
+		padding-bottom: 0;
441
+	}
442
+
443
+	.is-first-border {
444
+		/* #ifndef APP-NVUE */
445
+		border: none;
446
+		/* #endif */
447
+		/* #ifdef APP-NVUE */
448
+		border-width: 0;
449
+		/* #endif */
450
+	}
451
+
452
+	.is-disabled {
453
+		border-color: red;
454
+		background-color: #F7F6F6;
455
+		color: #D5D5D5;
456
+		.uni-easyinput__placeholder-class {
457
+			color: #D5D5D5;
458
+			font-size: 12px;
459
+		}
460
+	}
461
+</style>

+ 90 - 0
uni_modules/uni-easyinput/package.json

@@ -0,0 +1,90 @@
1
+{
2
+  "id": "uni-easyinput",
3
+  "displayName": "uni-easyinput 增强输入框",
4
+  "version": "1.0.0",
5
+  "description": "Easyinput 组件是对原生input组件的增强",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "input",
10
+    "uni-easyinput",
11
+    "输入框"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": ""
16
+  },
17
+  "directories": {
18
+    "example": "../../temps/example_temps"
19
+  },
20
+  "dcloudext": {
21
+    "category": [
22
+      "前端组件",
23
+      "通用组件"
24
+    ],
25
+    "sale": {
26
+      "regular": {
27
+        "price": "0.00"
28
+      },
29
+      "sourcecode": {
30
+        "price": "0.00"
31
+      }
32
+    },
33
+    "contact": {
34
+      "qq": ""
35
+    },
36
+    "declaration": {
37
+      "ads": "无",
38
+      "data": "无",
39
+      "permissions": "无"
40
+    },
41
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
42
+  },
43
+  "uni_modules": {
44
+    "dependencies": [
45
+			"uni-scss",
46
+      "uni-icons"
47
+    ],
48
+    "encrypt": [],
49
+    "platforms": {
50
+      "cloud": {
51
+        "tcb": "y",
52
+        "aliyun": "y"
53
+      },
54
+      "client": {
55
+        "App": {
56
+          "app-vue": "y",
57
+          "app-nvue": "y"
58
+        },
59
+        "H5-mobile": {
60
+          "Safari": "y",
61
+          "Android Browser": "y",
62
+          "微信浏览器(Android)": "y",
63
+          "QQ浏览器(Android)": "y"
64
+        },
65
+        "H5-pc": {
66
+          "Chrome": "y",
67
+          "IE": "y",
68
+          "Edge": "y",
69
+          "Firefox": "y",
70
+          "Safari": "y"
71
+        },
72
+        "小程序": {
73
+          "微信": "y",
74
+          "阿里": "y",
75
+          "百度": "y",
76
+          "字节跳动": "y",
77
+          "QQ": "y"
78
+        },
79
+        "快应用": {
80
+          "华为": "u",
81
+          "联盟": "u"
82
+        },
83
+        "Vue": {
84
+            "vue2": "y",
85
+            "vue3": "y"
86
+        }
87
+      }
88
+    }
89
+  }
90
+}

+ 11 - 0
uni_modules/uni-easyinput/readme.md

@@ -0,0 +1,11 @@
1
+
2
+
3
+### Easyinput 增强输入框
4
+> **组件名:uni-easyinput**
5
+> 代码块: `uEasyinput`
6
+
7
+
8
+easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能
9
+
10
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
11
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 17 - 0
uni_modules/uni-fab/changelog.md

@@ -0,0 +1,17 @@
1
+## 1.2.2(2021-12-29)
2
+- 更新 组件依赖
3
+## 1.2.1(2021-11-19)
4
+- 修复 阴影颜色不正确的bug
5
+## 1.2.0(2021-11-19)
6
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
7
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab)
8
+## 1.1.1(2021-11-09) 
9
+- 新增 提供组件设计资源,组件样式调整
10
+## 1.1.0(2021-07-30)
11
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
12
+## 1.0.7(2021-05-12)
13
+- 新增 组件示例地址
14
+## 1.0.6(2021-02-05)
15
+- 调整为uni_modules目录规范
16
+- 优化 按钮背景色调整
17
+- 优化 兼容pc端

+ 0 - 0
uni_modules/uni-fab/components/uni-fab/uni-fab.vue


Some files were not shown because too many files changed in this diff