Browse Source

first submit

wangxx 4 months ago
parent
commit
979c8171f1
100 changed files with 16773 additions and 0 deletions
  1. 214 0
      src/uni_modules/mix-tree/components/mix-tree/mix-tree.vue
  2. 11 0
      src/uni_modules/mix-tree/main.js
  3. 63 0
      src/uni_modules/mix-tree/manifest.json
  4. 15 0
      src/uni_modules/mix-tree/mix-tree.md
  5. 40 0
      src/uni_modules/mix-tree/pages.json
  6. 76 0
      src/uni_modules/mix-tree/uni.scss
  7. 33 0
      src/uni_modules/uni-badge/changelog.md
  8. 268 0
      src/uni_modules/uni-badge/components/uni-badge/uni-badge.vue
  9. 106 0
      src/uni_modules/uni-badge/package.json
  10. 10 0
      src/uni_modules/uni-badge/readme.md
  11. 6 0
      src/uni_modules/uni-breadcrumb/changelog.md
  12. 121 0
      src/uni_modules/uni-breadcrumb/components/uni-breadcrumb-item/uni-breadcrumb-item.vue
  13. 41 0
      src/uni_modules/uni-breadcrumb/components/uni-breadcrumb/uni-breadcrumb.vue
  14. 103 0
      src/uni_modules/uni-breadcrumb/package.json
  15. 66 0
      src/uni_modules/uni-breadcrumb/readme.md
  16. 30 0
      src/uni_modules/uni-calendar/changelog.md
  17. 544 0
      src/uni_modules/uni-calendar/components/uni-calendar/calendar.js
  18. 12 0
      src/uni_modules/uni-calendar/components/uni-calendar/i18n/en.json
  19. 8 0
      src/uni_modules/uni-calendar/components/uni-calendar/i18n/index.js
  20. 12 0
      src/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json
  21. 12 0
      src/uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json
  22. 195 0
      src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue
  23. 567 0
      src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue
  24. 360 0
      src/uni_modules/uni-calendar/components/uni-calendar/util.js
  25. 105 0
      src/uni_modules/uni-calendar/package.json
  26. 103 0
      src/uni_modules/uni-calendar/readme.md
  27. 26 0
      src/uni_modules/uni-card/changelog.md
  28. 270 0
      src/uni_modules/uni-card/components/uni-card/uni-card.vue
  29. 106 0
      src/uni_modules/uni-card/package.json
  30. 12 0
      src/uni_modules/uni-card/readme.md
  31. 44 0
      src/uni_modules/uni-collapse/changelog.md
  32. 402 0
      src/uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue
  33. 145 0
      src/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue
  34. 106 0
      src/uni_modules/uni-collapse/package.json
  35. 12 0
      src/uni_modules/uni-collapse/readme.md
  36. 17 0
      src/uni_modules/uni-combox/changelog.md
  37. 301 0
      src/uni_modules/uni-combox/components/uni-combox/uni-combox.vue
  38. 107 0
      src/uni_modules/uni-combox/package.json
  39. 11 0
      src/uni_modules/uni-combox/readme.md
  40. 30 0
      src/uni_modules/uni-countdown/changelog.md
  41. 6 0
      src/uni_modules/uni-countdown/components/uni-countdown/i18n/en.json
  42. 8 0
      src/uni_modules/uni-countdown/components/uni-countdown/i18n/index.js
  43. 6 0
      src/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json
  44. 6 0
      src/uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json
  45. 278 0
      src/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue
  46. 105 0
      src/uni_modules/uni-countdown/package.json
  47. 10 0
      src/uni_modules/uni-countdown/readme.md
  48. 51 0
      src/uni_modules/uni-data-checkbox/changelog.md
  49. 316 0
      src/uni_modules/uni-data-checkbox/components/uni-data-checkbox/clientdb.js
  50. 853 0
      src/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue
  51. 107 0
      src/uni_modules/uni-data-checkbox/package.json
  52. 18 0
      src/uni_modules/uni-data-checkbox/readme.md
  53. 79 0
      src/uni_modules/uni-data-picker/changelog.md
  54. 45 0
      src/uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
  55. 380 0
      src/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue
  56. 575 0
      src/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
  57. 1 0
      src/uni_modules/uni-data-picker/components/uni-data-pickerview/loading.uts
  58. 622 0
      src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
  59. 693 0
      src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts
  60. 76 0
      src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css
  61. 69 0
      src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue
  62. 323 0
      src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
  63. 109 0
      src/uni_modules/uni-data-picker/package.json
  64. 22 0
      src/uni_modules/uni-data-picker/readme.md
  65. 51 0
      src/uni_modules/uni-data-select/changelog.md
  66. 838 0
      src/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue
  67. 106 0
      src/uni_modules/uni-data-select/package.json
  68. 8 0
      src/uni_modules/uni-data-select/readme.md
  69. 10 0
      src/uni_modules/uni-dateformat/changelog.md
  70. 200 0
      src/uni_modules/uni-dateformat/components/uni-dateformat/date-format.js
  71. 88 0
      src/uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue
  72. 106 0
      src/uni_modules/uni-dateformat/package.json
  73. 11 0
      src/uni_modules/uni-dateformat/readme.md
  74. 173 0
      src/uni_modules/uni-datetime-picker/changelog.md
  75. 177 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
  76. 947 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
  77. 22 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
  78. 8 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
  79. 22 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
  80. 22 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
  81. 940 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
  82. 1074 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
  83. 421 0
      src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
  84. 107 0
      src/uni_modules/uni-datetime-picker/package.json
  85. 21 0
      src/uni_modules/uni-datetime-picker/readme.md
  86. 13 0
      src/uni_modules/uni-drawer/changelog.md
  87. 45 0
      src/uni_modules/uni-drawer/components/uni-drawer/keypress.js
  88. 183 0
      src/uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
  89. 105 0
      src/uni_modules/uni-drawer/package.json
  90. 10 0
      src/uni_modules/uni-drawer/readme.md
  91. 119 0
      src/uni_modules/uni-easyinput/changelog.md
  92. 54 0
      src/uni_modules/uni-easyinput/components/uni-easyinput/common.js
  93. 674 0
      src/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
  94. 107 0
      src/uni_modules/uni-easyinput/package.json
  95. 11 0
      src/uni_modules/uni-easyinput/readme.md
  96. 25 0
      src/uni_modules/uni-fab/changelog.md
  97. 491 0
      src/uni_modules/uni-fab/components/uni-fab/uni-fab.vue
  98. 107 0
      src/uni_modules/uni-fab/package.json
  99. 9 0
      src/uni_modules/uni-fab/readme.md
  100. 0 0
      src/uni_modules/uni-fav/changelog.md

+ 214 - 0
src/uni_modules/mix-tree/components/mix-tree/mix-tree.vue

@@ -0,0 +1,214 @@
1
+<template>
2
+	<view class="content">
3
+		<view class="mix-tree-list">
4
+			<block v-for="(item, index) in treeList" :key="index">
5
+				<view class="mix-tree-item" :style="[{
6
+					paddingLeft: item.rank * 15 + 'px',
7
+					zIndex: item.rank * -1 + 50
8
+				}]" :class="{
9
+							border: treeParams.border === true,
10
+							show: item.show,
11
+							last: !item.children || item.children.length === 0,
12
+							showchild: item.showChild,
13
+							selected: selectedId === item.id
14
+						}" @click.stop="treeItemTap(item, index)">
15
+					<view class="mix-tree-icon"></view>
16
+					{{ item.name }}
17
+				</view>
18
+			</block>
19
+		</view>
20
+	</view>
21
+</template>
22
+
23
+<script>
24
+export default {
25
+	props: {
26
+		list: {
27
+			type: Array,
28
+			default() {
29
+				return [];
30
+			}
31
+		},
32
+		params: {
33
+			type: Object,
34
+			default() {
35
+				return {}
36
+			}
37
+		},
38
+		expandAll: {
39
+			type: Boolean,
40
+			default: false
41
+		}
42
+	},
43
+	data() {
44
+		return {
45
+			treeList: [],
46
+			treeParams: {
47
+				defaultIcon: '',
48
+				currentIcon: '',
49
+				lastIcon: '',
50
+				border: false
51
+			},
52
+			selectedId: null  // 当前选中的节点id
53
+		}
54
+	},
55
+	watch: {
56
+		list(list) {
57
+			this.treeParams = Object.assign(this.treeParams, this.params);
58
+			this.treeList = [];
59
+			this.renderTreeList(list);
60
+			
61
+			// 如果expandAll为true,自动展开所有节点
62
+			if (this.expandAll) {
63
+				this.expandAllNodes();
64
+			}
65
+		},
66
+		expandAll: {
67
+			handler(newVal) {
68
+				if (newVal && this.treeList.length > 0) {
69
+					this.expandAllNodes();
70
+				}
71
+			},
72
+			immediate: true
73
+		}
74
+	},
75
+	methods: {
76
+		//扁平化树结构
77
+		renderTreeList(list = [], rank = 0, parentId = [], parentShowChild = false) {
78
+			list.forEach(item => {
79
+				// 如果父节点是展开状态,子节点应该显示
80
+				const shouldShow = rank === 0 || parentShowChild;
81
+				// 获取当前节点的showChild值
82
+				const currentShowChild = item.showChild !== undefined ? item.showChild : false;
83
+				
84
+				this.treeList.push({
85
+					...item,
86
+					id: item.id,
87
+					name: item.label,
88
+					parentId,  // 父级id数组
89
+					rank,  // 层级
90
+					showChild: currentShowChild,  //子级是否显示,保留传入的值
91
+					show: shouldShow,  // 自身是否显示(第一层或父节点展开时显示)
92
+					children: item.children  // 保存children信息用于判断最后一级节点
93
+				})
94
+				if (Array.isArray(item.children) && item.children.length > 0) {
95
+					let parents = [...parentId];
96
+					parents.push(item.id);
97
+					// 递归处理子节点,传递当前节点的实际showChild状态
98
+					this.renderTreeList(item.children, rank + 1, parents, currentShowChild);
99
+				} else {
100
+					this.treeList[this.treeList.length - 1].lastRank = true;
101
+				}
102
+			})
103
+		},
104
+		// 点击
105
+		treeItemTap(item) {
106
+			let list = this.treeList;
107
+			let id = item.id;
108
+
109
+			// 设置选中状态
110
+			this.selectedId = id;
111
+
112
+			// 无论是否是最后一级,都触发点击事件
113
+			this.$emit('treeItemClick', item);
114
+
115
+			if (item.lastRank === true) {
116
+				//如果是最后一级,直接返回,不处理展开/折叠
117
+				return;
118
+			}
119
+
120
+			// 非最后一级节点,处理展开/折叠逻辑
121
+			item.showChild = !item.showChild;
122
+			list.forEach(childItem => {
123
+				if (item.showChild === false) {
124
+					//隐藏所有子级
125
+					if (!childItem.parentId.includes(id)) {
126
+						return;
127
+					}
128
+					if (childItem.lastRank !== true) {
129
+						childItem.showChild = false;
130
+					}
131
+					childItem.show = false;
132
+				} else {
133
+					if (childItem.parentId[childItem.parentId.length - 1] === id) {
134
+						childItem.show = true;
135
+					}
136
+				}
137
+			})
138
+		},
139
+		
140
+		// 展开所有节点
141
+		expandAllNodes() {
142
+			this.treeList.forEach(item => {
143
+				// 非最后一级节点都展开
144
+				if (item.lastRank !== true) {
145
+					item.showChild = true;
146
+				}
147
+				// 所有节点都显示
148
+				item.show = true;
149
+			})
150
+		}
151
+	}
152
+}
153
+</script>
154
+
155
+<style>
156
+.mix-tree-list {
157
+	display: flex;
158
+	flex-direction: column;
159
+	padding-left: 30upx;
160
+}
161
+
162
+.mix-tree-item {
163
+	display: flex;
164
+	align-items: center;
165
+	font-size: 30upx;
166
+	color: #333;
167
+	height: 0;
168
+	opacity: 0;
169
+	transition: .2s;
170
+	position: relative;
171
+}
172
+
173
+.mix-tree-item.border {
174
+	border-bottom: 1px solid #eee;
175
+}
176
+
177
+.mix-tree-item.show {
178
+	height: 80upx;
179
+	opacity: 1;
180
+}
181
+
182
+.mix-tree-icon {
183
+	width: 32upx;
184
+	height: 32upx;
185
+	margin-right: 8upx;
186
+	position: relative;
187
+}
188
+
189
+.mix-tree-icon:before {
190
+	content: '';
191
+	position: absolute;
192
+	top: 50%;
193
+	left: 50%;
194
+	width: 0;
195
+	height: 0;
196
+	border-left: 12upx solid #666;
197
+	border-top: 8upx solid transparent;
198
+	border-bottom: 8upx solid transparent;
199
+	transform: translate(-50%, -50%);
200
+	transition: transform 0.2s ease;
201
+}
202
+
203
+.mix-tree-item.showchild .mix-tree-icon:before {
204
+	transform: translate(-50%, -50%) rotate(90deg);
205
+}
206
+
207
+.mix-tree-item.last .mix-tree-icon:before {
208
+	opacity: 0;
209
+}
210
+
211
+.mix-tree-item.selected {
212
+	background-color: #e6f7ff;
213
+}
214
+</style>

+ 11 - 0
src/uni_modules/mix-tree/main.js

@@ -0,0 +1,11 @@
1
+import Vue from 'vue'
2
+import App from './App'
3
+
4
+Vue.config.productionTip = false
5
+
6
+App.mpType = 'app'
7
+
8
+const app = new Vue({
9
+    ...App
10
+})
11
+app.$mount()

+ 63 - 0
src/uni_modules/mix-tree/manifest.json

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

+ 15 - 0
src/uni_modules/mix-tree/mix-tree.md

@@ -0,0 +1,15 @@
1
+## 树形组件
2
+
3
+	. 基本上不会更新这玩意了,用的人应该不多 (◔ ⊖ ◔)つ
4
+	. 只是抛砖引玉,其他功能可以自己实现
5
+	
6
+### 测试
7
+	
8
+	. chrome ios 微信小程序开发工具
9
+	
10
+### 使用方式
11
+
12
+	. 项目中有完整使用示例
13
+
14
+
15
+

+ 40 - 0
src/uni_modules/mix-tree/pages.json

@@ -0,0 +1,40 @@
1
+{
2
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3
+		{
4
+			"path": "pages/index/index",
5
+			"style": {
6
+				"navigationBarTitleText": "uniapp 树形组件"
7
+			}
8
+		}
9
+	    ,{
10
+            "path" : "pages/course/course",
11
+            "style" : {}
12
+        }
13
+    ],
14
+	"globalStyle": {
15
+		"navigationBarTextStyle": "black",
16
+		"navigationBarTitleText": "uni-app",
17
+		"navigationBarBackgroundColor": "#F8F8F8",
18
+		"backgroundColor": "#F8F8F8"
19
+	},
20
+	"tabBar" : {
21
+	    "color" : "#C0C4CC",
22
+	    "selectedColor" : "#fa436a",
23
+	    "borderStyle" : "black",
24
+	    "backgroundColor" : "#ffffff",
25
+	    "list" : [
26
+	        {
27
+	            "pagePath" : "pages/index/index",
28
+	            "iconPath" : "static/tab-home.png",
29
+	            "selectedIconPath" : "static/tab-home-current.png",
30
+	            "text" : "首页"
31
+	        },
32
+	        {
33
+	            "pagePath" : "pages/course/course",
34
+	            "iconPath" : "static/tab-home.png",
35
+	            "selectedIconPath" : "static/tab-home-current.png",
36
+	            "text" : "示例1"
37
+	        }
38
+	    ]
39
+	}
40
+}

+ 76 - 0
src/uni_modules/mix-tree/uni.scss

@@ -0,0 +1,76 @@
1
+/**
2
+ * 这里是uni-app内置的常用样式变量
3
+ *
4
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6
+ *
7
+ */
8
+
9
+/**
10
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11
+ *
12
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13
+ */
14
+
15
+/* 颜色变量 */
16
+
17
+/* 行为相关颜色 */
18
+$uni-color-primary: #007aff;
19
+$uni-color-success: #4cd964;
20
+$uni-color-warning: #f0ad4e;
21
+$uni-color-error: #dd524d;
22
+
23
+/* 文字基本颜色 */
24
+$uni-text-color:#333;//基本色
25
+$uni-text-color-inverse:#fff;//反色
26
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27
+$uni-text-color-placeholder: #808080;
28
+$uni-text-color-disable:#c0c0c0;
29
+
30
+/* 背景颜色 */
31
+$uni-bg-color:#ffffff;
32
+$uni-bg-color-grey:#f8f8f8;
33
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
34
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35
+
36
+/* 边框颜色 */
37
+$uni-border-color:#c8c7cc;
38
+
39
+/* 尺寸变量 */
40
+
41
+/* 文字尺寸 */
42
+$uni-font-size-sm:24upx;
43
+$uni-font-size-base:28upx;
44
+$uni-font-size-lg:32upx;
45
+
46
+/* 图片尺寸 */
47
+$uni-img-size-sm:40upx;
48
+$uni-img-size-base:52upx;
49
+$uni-img-size-lg:80upx;
50
+
51
+/* Border Radius */
52
+$uni-border-radius-sm: 4upx;
53
+$uni-border-radius-base: 6upx;
54
+$uni-border-radius-lg: 12upx;
55
+$uni-border-radius-circle: 50%;
56
+
57
+/* 水平间距 */
58
+$uni-spacing-row-sm: 10px;
59
+$uni-spacing-row-base: 20upx;
60
+$uni-spacing-row-lg: 30upx;
61
+
62
+/* 垂直间距 */
63
+$uni-spacing-col-sm: 8upx;
64
+$uni-spacing-col-base: 16upx;
65
+$uni-spacing-col-lg: 24upx;
66
+
67
+/* 透明度 */
68
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69
+
70
+/* 文章场景相关 */
71
+$uni-color-title: #2C405A; // 文章标题颜色
72
+$uni-font-size-title:40upx;
73
+$uni-color-subtitle: #555555; // 二级标题颜色
74
+$uni-font-size-subtitle:36upx;
75
+$uni-color-paragraph: #3F536E; // 文章段落颜色
76
+$uni-font-size-paragraph:30upx;

+ 33 - 0
src/uni_modules/uni-badge/changelog.md

@@ -0,0 +1,33 @@
1
+## 1.2.2(2023-01-28)
2
+- 修复 运行/打包 控制台警告问题
3
+## 1.2.1(2022-09-05)
4
+- 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
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-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
8
+## 1.1.7(2021-11-08)
9
+- 优化 升级ui
10
+- 修改 size 属性默认值调整为 small
11
+- 修改 type 属性,默认值调整为 error,info 替换 default
12
+## 1.1.6(2021-09-22)
13
+- 修复 在字节小程序上样式不生效的 bug
14
+## 1.1.5(2021-07-30)
15
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
16
+## 1.1.4(2021-07-29)
17
+- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性
18
+## 1.1.3(2021-06-24)
19
+- 优化 示例项目
20
+## 1.1.1(2021-05-12)
21
+- 新增 组件示例地址
22
+## 1.1.0(2021-05-12)
23
+- 新增 uni-badge 的 absolute 属性,支持定位
24
+- 新增 uni-badge 的 offset 属性,支持定位偏移
25
+- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
26
+- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
27
+- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式
28
+## 1.0.7(2021-05-07)
29
+- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug
30
+- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug
31
+- 新增 uni-badge 属性 custom-style, 支持自定义样式
32
+## 1.0.6(2021-02-04)
33
+- 调整为uni_modules目录规范

+ 268 - 0
src/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="[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
+			dotStyle() {
134
+				if (!this.isDot) return {}
135
+				return {
136
+					width: '10px',
137
+					minWidth: '0',
138
+					height: '10px',
139
+					padding: '0',
140
+					borderRadius: '10px'
141
+				}
142
+			},
143
+			displayValue() {
144
+				const {
145
+					isDot,
146
+					text,
147
+					maxNum
148
+				} = this
149
+				return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
150
+			}
151
+		},
152
+		methods: {
153
+			onClick() {
154
+				this.$emit('click');
155
+			}
156
+		}
157
+	};
158
+</script>
159
+
160
+<style lang="scss" >
161
+	$uni-primary: #2979ff !default;
162
+	$uni-success: #4cd964 !default;
163
+	$uni-warning: #f0ad4e !default;
164
+	$uni-error: #dd524d !default;
165
+	$uni-info: #909399 !default;
166
+
167
+
168
+	$bage-size: 12px;
169
+	$bage-small: scale(0.8);
170
+
171
+	.uni-badge--x {
172
+		/* #ifdef APP-NVUE */
173
+		// align-self: flex-start;
174
+		/* #endif */
175
+		/* #ifndef APP-NVUE */
176
+		display: inline-block;
177
+		/* #endif */
178
+		position: relative;
179
+	}
180
+
181
+	.uni-badge--absolute {
182
+		position: absolute;
183
+	}
184
+
185
+	.uni-badge--small {
186
+		transform: $bage-small;
187
+		transform-origin: center center;
188
+	}
189
+
190
+	.uni-badge {
191
+		/* #ifndef APP-NVUE */
192
+		display: flex;
193
+		overflow: hidden;
194
+		box-sizing: border-box;
195
+		font-feature-settings: "tnum";
196
+		min-width: 20px;
197
+		/* #endif */
198
+		justify-content: center;
199
+		flex-direction: row;
200
+		height: 20px;
201
+		padding: 0 4px;
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>

+ 106 - 0
src/uni_modules/uni-badge/package.json

@@ -0,0 +1,106 @@
1
+{
2
+  "id": "uni-badge",
3
+  "displayName": "uni-badge 数字角标",
4
+  "version": "1.2.2",
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
+    "uni-app": "^3.1.0",
18
+    "uni-app-x": "^3.1.0"
19
+  },
20
+  "directories": {
21
+    "example": "../../temps/example_temps"
22
+  },
23
+  "dcloudext": {
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
+    "type": "component-vue",
42
+    "darkmode": "-",
43
+    "i18n": "-",
44
+    "widescreen": "-"
45
+  },
46
+  "uni_modules": {
47
+    "dependencies": [
48
+      "uni-scss"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "√",
54
+        "aliyun": "√"
55
+      },
56
+      "client": {
57
+        "uni-app": {
58
+          "vue": {
59
+            "vue2": "-",
60
+            "vue3": "-"
61
+          },
62
+          "web": {
63
+            "safari": "-",
64
+            "chrome": "-"
65
+          },
66
+          "app": {
67
+            "vue": "-",
68
+            "nvue": "-",
69
+            "android": "-",
70
+            "ios": "-",
71
+            "harmony": "-"
72
+          },
73
+          "mp": {
74
+            "weixin": "-",
75
+            "alipay": "-",
76
+            "toutiao": "-",
77
+            "baidu": "-",
78
+            "kuaishou": "-",
79
+            "jd": "-",
80
+            "harmony": "-",
81
+            "qq": "-",
82
+            "lark": "-"
83
+          },
84
+          "quickapp": {
85
+            "huawei": "-",
86
+            "union": "-"
87
+          }
88
+        },
89
+        "uni-app-x": {
90
+          "web": {
91
+            "safari": "-",
92
+            "chrome": "-"
93
+          },
94
+          "app": {
95
+            "android": "-",
96
+            "ios": "-",
97
+            "harmony": "-"
98
+          },
99
+          "mp": {
100
+            "weixin": "-"
101
+          }
102
+        }
103
+      }
104
+    }
105
+  }
106
+}

+ 10 - 0
src/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
+

+ 6 - 0
src/uni_modules/uni-breadcrumb/changelog.md

@@ -0,0 +1,6 @@
1
+## 0.1.2(2022-06-08)
2
+- 修复 微信小程序 separator 不显示的Bug
3
+## 0.1.1(2022-06-02)
4
+- 新增 支持 uni.scss 修改颜色
5
+## 0.1.0(2022-04-21)
6
+- 初始化

+ 121 - 0
src/uni_modules/uni-breadcrumb/components/uni-breadcrumb-item/uni-breadcrumb-item.vue

@@ -0,0 +1,121 @@
1
+<template>
2
+	<view class="uni-breadcrumb-item">
3
+		<view :class="{
4
+			'uni-breadcrumb-item--slot': true,
5
+			'uni-breadcrumb-item--slot-link': to && currentPage !== to
6
+			}" @click="navTo">
7
+			<slot />
8
+		</view>
9
+		<i v-if="separatorClass" class="uni-breadcrumb-item--separator" :class="separatorClass" />
10
+		<text v-else class="uni-breadcrumb-item--separator">{{ separator }}</text>
11
+	</view>
12
+</template>
13
+<script>
14
+	/**
15
+	 * BreadcrumbItem 面包屑导航子组件
16
+	 * @property {String/Object} to 路由跳转页面路径/对象
17
+	 * @property {Boolean} replace 在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持)
18
+	 */
19
+	export default {
20
+		data() {
21
+			return {
22
+				currentPage: ""
23
+			}
24
+		},
25
+		options: {
26
+			virtualHost: true
27
+		},
28
+		props: {
29
+			to: {
30
+				type: String,
31
+				default: ''
32
+			},
33
+			replace:{
34
+				type: Boolean,
35
+				default: false
36
+			}
37
+		},
38
+		inject: {
39
+			uniBreadcrumb: {
40
+				from: "uniBreadcrumb",
41
+				default: null
42
+			}
43
+		},
44
+		created(){
45
+			const pages = getCurrentPages()
46
+			const page = pages[pages.length-1]
47
+
48
+			if(page){
49
+				this.currentPage = `/${page.route}`
50
+			}
51
+		},
52
+		computed: {
53
+			separator() {
54
+				return this.uniBreadcrumb.separator
55
+			},
56
+			separatorClass() {
57
+				return this.uniBreadcrumb.separatorClass
58
+			}
59
+		},
60
+		methods: {
61
+			navTo() {
62
+				const { to } = this
63
+
64
+				if (!to || this.currentPage === to){
65
+					return
66
+				}
67
+
68
+				if(this.replace){
69
+					uni.redirectTo({
70
+						url:to
71
+					})
72
+				}else{
73
+					uni.navigateTo({
74
+						url:to
75
+					})
76
+				}
77
+			}
78
+		}
79
+	}
80
+</script>
81
+<style lang="scss">
82
+	$uni-primary: #2979ff !default;
83
+	$uni-base-color: #6a6a6a !default;
84
+	$uni-main-color: #3a3a3a !default;
85
+	.uni-breadcrumb-item {
86
+		display: flex;
87
+		align-items: center;
88
+		white-space: nowrap;
89
+		font-size: 14px;
90
+
91
+		&--slot {
92
+			color: $uni-base-color;
93
+			padding: 0 10px;
94
+
95
+			&-link {
96
+				color: $uni-main-color;
97
+				font-weight: bold;
98
+				/* #ifndef APP-NVUE */
99
+				cursor: pointer;
100
+				/* #endif */
101
+
102
+				&:hover {
103
+					color: $uni-primary;
104
+				}
105
+			}
106
+		}
107
+
108
+		&--separator {
109
+			font-size: 12px;
110
+			color: $uni-base-color;
111
+		}
112
+
113
+		&:first-child &--slot {
114
+			padding-left: 0;
115
+		}
116
+		
117
+		&:last-child &--separator {
118
+			display: none;
119
+		}
120
+	}
121
+</style>

+ 41 - 0
src/uni_modules/uni-breadcrumb/components/uni-breadcrumb/uni-breadcrumb.vue

@@ -0,0 +1,41 @@
1
+<template>
2
+	<view class="uni-breadcrumb">
3
+		<slot />
4
+	</view>
5
+</template>
6
+<script>
7
+	/**
8
+	 * Breadcrumb 面包屑导航父组件
9
+	 * @description 显示当前页面的路径,快速返回之前的任意页面
10
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
11
+	 * @property {String} separator 分隔符,默认为斜杠'/'
12
+	 * @property {String} separatorClass 图标分隔符 class
13
+	 */
14
+	export default {
15
+		options: {
16
+			virtualHost: true
17
+		},
18
+		props: {
19
+			separator: {
20
+				type: String,
21
+				default: '/'
22
+			},
23
+			separatorClass: {
24
+				type: String,
25
+				default: ''
26
+			}
27
+		},
28
+
29
+		provide() {
30
+			return {
31
+				uniBreadcrumb: this
32
+			}
33
+		}
34
+
35
+	}
36
+</script>
37
+<style lang="scss">
38
+	.uni-breadcrumb {
39
+		display: flex;
40
+	}
41
+</style>

+ 103 - 0
src/uni_modules/uni-breadcrumb/package.json

@@ -0,0 +1,103 @@
1
+{
2
+  "id": "uni-breadcrumb",
3
+  "displayName": "uni-breadcrumb 面包屑",
4
+  "version": "0.1.2",
5
+  "description": "Breadcrumb  面包屑",
6
+  "keywords": [
7
+    "uni-breadcrumb",
8
+    "breadcrumb",
9
+    "uni-ui",
10
+    "面包屑导航",
11
+    "面包屑"
12
+  ],
13
+  "repository": "",
14
+  "engines": {
15
+    "HBuilderX": "^3.1.0",
16
+    "uni-app": "^3.1.0",
17
+    "uni-app-x": "^3.1.0"
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "",
40
+    "type": "component-vue",
41
+    "darkmode": "-",
42
+    "i18n": "-",
43
+    "widescreen": "-"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [],
47
+    "encrypt": [],
48
+    "platforms": {
49
+      "cloud": {
50
+        "tcb": "√",
51
+        "aliyun": "√"
52
+      },
53
+      "client": {
54
+        "uni-app": {
55
+          "vue": {
56
+            "vue2": "-",
57
+            "vue3": "-"
58
+          },
59
+          "web": {
60
+            "safari": "-",
61
+            "chrome": "-"
62
+          },
63
+          "app": {
64
+            "vue": "-",
65
+            "nvue": "-",
66
+            "android": "-",
67
+            "ios": "-",
68
+            "harmony": "-"
69
+          },
70
+          "mp": {
71
+            "weixin": "-",
72
+            "alipay": "-",
73
+            "toutiao": "-",
74
+            "baidu": "-",
75
+            "kuaishou": "-",
76
+            "jd": "-",
77
+            "harmony": "-",
78
+            "qq": "-",
79
+            "lark": "-"
80
+          },
81
+          "quickapp": {
82
+            "huawei": "-",
83
+            "union": "-"
84
+          }
85
+        },
86
+        "uni-app-x": {
87
+          "web": {
88
+            "safari": "-",
89
+            "chrome": "-"
90
+          },
91
+          "app": {
92
+            "android": "-",
93
+            "ios": "-",
94
+            "harmony": "-"
95
+          },
96
+          "mp": {
97
+            "weixin": "-"
98
+          }
99
+        }
100
+      }
101
+    }
102
+  }
103
+}

+ 66 - 0
src/uni_modules/uni-breadcrumb/readme.md

@@ -0,0 +1,66 @@
1
+
2
+## breadcrumb 面包屑导航
3
+> **组件名:uni-breadcrumb**
4
+> 代码块: `ubreadcrumb`
5
+
6
+显示当前页面的路径,快速返回之前的任意页面。
7
+
8
+### 安装方式
9
+
10
+本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
11
+
12
+如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
13
+
14
+### 基本用法
15
+
16
+在 ``template`` 中使用组件
17
+
18
+```html
19
+<uni-breadcrumb separator="/">
20
+	<uni-breadcrumb-item v-for="(route,index) in routes" :key="index" :to="route.to">{{route.name}}</uni-breadcrumb-item>
21
+</uni-breadcrumb>
22
+```
23
+
24
+```js
25
+export default {
26
+		name: "uni-stat-breadcrumb",
27
+		data() {
28
+			return {
29
+				routes: [{
30
+					to: '/A',
31
+					name: 'A页面'
32
+				}, {
33
+					to: '/B',
34
+					name: 'B页面'
35
+				}, {
36
+					to: '/C',
37
+					name: 'C页面'
38
+				}]
39
+			};
40
+		}
41
+	}
42
+```
43
+
44
+
45
+## API
46
+
47
+### Breadcrumb Props
48
+
49
+|属性名			|类型	|默认值	|说明				|
50
+|:-:			|:-:	|:-:	|:-:				|
51
+|separator		|String	|斜杠'/' |分隔符				|
52
+|separatorClass	|String	|		|图标分隔符 class	    |
53
+
54
+### Breadcrumb Item Props
55
+
56
+|属性名	|类型			|默认值	|说明																			|
57
+|:-:	|:-:			|:-:	|:-:																			|
58
+|to		|String     	|		|路由跳转页面路径           														|
59
+|replace|Boolean		|		|在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持)         |
60
+
61
+
62
+
63
+
64
+## 组件示例
65
+
66
+点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb](https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb)

+ 30 - 0
src/uni_modules/uni-calendar/changelog.md

@@ -0,0 +1,30 @@
1
+## 1.4.12(2024-09-21)
2
+- 修复 calendar在选择日期范围后重新选择日期需要点两次的Bug
3
+## 1.4.11(2024-01-10)
4
+- 修复 回到今天时,月份显示不一致问题
5
+## 1.4.10(2023-04-10)
6
+- 修复 某些情况 monthSwitch 未触发的Bug
7
+## 1.4.9(2023-02-02)
8
+- 修复 某些情况切换月份错误的Bug
9
+## 1.4.8(2023-01-30)
10
+- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964)
11
+## 1.4.7(2022-09-16)
12
+- 优化 支持使用 uni-scss 控制主题色
13
+## 1.4.6(2022-09-08)
14
+- 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件的Bug
15
+## 1.4.5(2022-02-25)
16
+- 修复 条件编译 nvue 不支持的 css 样式的Bug
17
+## 1.4.4(2022-02-25)
18
+- 修复 条件编译 nvue 不支持的 css 样式的Bug
19
+## 1.4.3(2021-09-22)
20
+- 修复 startDate、 endDate 属性失效的Bug
21
+## 1.4.2(2021-08-24)
22
+- 新增 支持国际化
23
+## 1.4.1(2021-08-05)
24
+- 修复 弹出层被 tabbar 遮盖的Bug
25
+## 1.4.0(2021-07-30)
26
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
27
+## 1.3.16(2021-05-12)
28
+- 新增 组件示例地址
29
+## 1.3.15(2021-02-04)
30
+- 调整为uni_modules目录规范

+ 544 - 0
src/uni_modules/uni-calendar/components/uni-calendar/calendar.js

@@ -0,0 +1,544 @@
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
+      case 30:
355
+        s = '\u4e09\u5341'; break
356
+      default :
357
+        s = this.nStr2[Math.floor(d / 10)]
358
+        s += this.nStr1[d % 10]
359
+    }
360
+    return (s)
361
+  },
362
+
363
+  /**
364
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
365
+      * @param y year
366
+      * @return Cn string
367
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
368
+      */
369
+  getAnimal: function (y) {
370
+    return this.Animals[(y - 4) % 12]
371
+  },
372
+
373
+  /**
374
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
375
+      * @param y  solar year
376
+      * @param m  solar month
377
+      * @param d  solar day
378
+      * @return JSON object
379
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
380
+      */
381
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
382
+    // 年份限定、上限
383
+    if (y < 1900 || y > 2100) {
384
+      return -1// undefined转换为数字变为NaN
385
+    }
386
+    // 公历传参最下限
387
+    if (y == 1900 && m == 1 && d < 31) {
388
+      return -1
389
+    }
390
+    // 未传参  获得当天
391
+    if (!y) {
392
+      var objDate = new Date()
393
+    } else {
394
+      var objDate = new Date(y, parseInt(m) - 1, d)
395
+    }
396
+    var i; var leap = 0; var temp = 0
397
+    // 修正ymd参数
398
+    var y = objDate.getFullYear()
399
+    var m = objDate.getMonth() + 1
400
+    var d = objDate.getDate()
401
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
402
+    for (i = 1900; i < 2101 && offset > 0; i++) {
403
+      temp = this.lYearDays(i)
404
+      offset -= temp
405
+    }
406
+    if (offset < 0) {
407
+      offset += temp; i--
408
+    }
409
+
410
+    // 是否今天
411
+    var isTodayObj = new Date()
412
+    var isToday = false
413
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
414
+      isToday = true
415
+    }
416
+    // 星期几
417
+    var nWeek = objDate.getDay()
418
+    var cWeek = this.nStr1[nWeek]
419
+    // 数字表示周几顺应天朝周一开始的惯例
420
+    if (nWeek == 0) {
421
+      nWeek = 7
422
+    }
423
+    // 农历年
424
+    var year = i
425
+    var leap = this.leapMonth(i) // 闰哪个月
426
+    var isLeap = false
427
+
428
+    // 效验闰月
429
+    for (i = 1; i < 13 && offset > 0; i++) {
430
+      // 闰月
431
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
432
+        --i
433
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
434
+      } else {
435
+        temp = this.monthDays(year, i)// 计算农历普通月天数
436
+      }
437
+      // 解除闰月
438
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
439
+      offset -= temp
440
+    }
441
+    // 闰月导致数组下标重叠取反
442
+    if (offset == 0 && leap > 0 && i == leap + 1) {
443
+      if (isLeap) {
444
+        isLeap = false
445
+      } else {
446
+        isLeap = true; --i
447
+      }
448
+    }
449
+    if (offset < 0) {
450
+      offset += temp; --i
451
+    }
452
+    // 农历月
453
+    var month = i
454
+    // 农历日
455
+    var day = offset + 1
456
+    // 天干地支处理
457
+    var sm = m - 1
458
+    var gzY = this.toGanZhiYear(year)
459
+
460
+    // 当月的两个节气
461
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
462
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
463
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
464
+
465
+    // 依据12节气修正干支月
466
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
467
+    if (d >= firstNode) {
468
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
469
+    }
470
+
471
+    // 传入的日期的节气与否
472
+    var isTerm = false
473
+    var Term = null
474
+    if (firstNode == d) {
475
+      isTerm = true
476
+      Term = this.solarTerm[m * 2 - 2]
477
+    }
478
+    if (secondNode == d) {
479
+      isTerm = true
480
+      Term = this.solarTerm[m * 2 - 1]
481
+    }
482
+    // 日柱 当月一日与 1900/1/1 相差天数
483
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
484
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
485
+    // 该日期所属的星座
486
+    var astro = this.toAstro(m, d)
487
+
488
+    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 }
489
+  },
490
+
491
+  /**
492
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
493
+      * @param y  lunar year
494
+      * @param m  lunar month
495
+      * @param d  lunar day
496
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
497
+      * @return JSON object
498
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
499
+      */
500
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
501
+    var isLeapMonth = !!isLeapMonth
502
+    var leapOffset = 0
503
+    var leapMonth = this.leapMonth(y)
504
+    var leapDay = this.leapDays(y)
505
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
506
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
507
+    var day = this.monthDays(y, m)
508
+    var _day = day
509
+    // bugFix 2016-9-25
510
+    // if month is leap, _day use leapDays method
511
+    if (isLeapMonth) {
512
+      _day = this.leapDays(y, m)
513
+    }
514
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
515
+
516
+    // 计算农历的时间差
517
+    var offset = 0
518
+    for (var i = 1900; i < y; i++) {
519
+      offset += this.lYearDays(i)
520
+    }
521
+    var leap = 0; var isAdd = false
522
+    for (var i = 1; i < m; i++) {
523
+      leap = this.leapMonth(y)
524
+      if (!isAdd) { // 处理闰月
525
+        if (leap <= i && leap > 0) {
526
+          offset += this.leapDays(y); isAdd = true
527
+        }
528
+      }
529
+      offset += this.monthDays(y, i)
530
+    }
531
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
532
+    if (isLeapMonth) { offset += day }
533
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
534
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
535
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
536
+    var cY = calObj.getUTCFullYear()
537
+    var cM = calObj.getUTCMonth() + 1
538
+    var cD = calObj.getUTCDate()
539
+
540
+    return this.solar2lunar(cY, cM, cD)
541
+  }
542
+}
543
+
544
+export default calendar

+ 12 - 0
src/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
src/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
src/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
src/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
+}

+ 195 - 0
src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue

@@ -0,0 +1,195 @@
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" :class="weeks.extraInfo.color"></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
+				'blue':weeks.extraInfo.color == 'blue',
41
+				'uni-calendar-item--extra':weeks.extraInfo.info,
42
+				'uni-calendar-item--isDay-text':weeks.isDay,
43
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
44
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
45
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
46
+				'uni-calendar-item--multiple': weeks.multiple,
47
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
48
+				'uni-calendar-item--disable':weeks.disable,
49
+				}">{{weeks.extraInfo.info}}</text>
50
+		</view>
51
+	</view>
52
+</template>
53
+
54
+<script>
55
+	import { initVueI18n } from '@dcloudio/uni-i18n'
56
+	import i18nMessages from './i18n/index.js'
57
+	const {	t	} = initVueI18n(i18nMessages)
58
+
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" scoped>
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-primary: #2979ff !default;
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
+	.uni-calendar-item__weeks-box-circle.blue{
149
+		background-color: $uni-primary;
150
+	}
151
+
152
+	.uni-calendar-item--disable {
153
+		background-color: rgba(249, 249, 249, $uni-opacity-disabled);
154
+		color: $uni-text-color-disable;
155
+	}
156
+
157
+	.uni-calendar-item--isDay-text {
158
+		color: $uni-primary;
159
+	}
160
+
161
+	.uni-calendar-item--isDay {
162
+		background-color: $uni-primary;
163
+		opacity: 0.8;
164
+		color: #fff;
165
+	}
166
+
167
+	.uni-calendar-item--extra {
168
+		color: $uni-color-error;
169
+		opacity: 0.8;
170
+	}
171
+	.uni-calendar-item--extra.blue{
172
+		color: $uni-primary;
173
+	}
174
+	.uni-calendar-item--checked {
175
+		background-color: $uni-primary;
176
+		color: #fff;
177
+		opacity: 0.8;
178
+	}
179
+	.uni-calendar-item--checked.blue{
180
+		color: #fff;
181
+	}
182
+	.uni-calendar-item--multiple {
183
+		background-color: $uni-primary;
184
+		color: #fff;
185
+		opacity: 0.8;
186
+	}
187
+	.uni-calendar-item--before-checked {
188
+		background-color: #ff5a5f;
189
+		color: #fff;
190
+	}
191
+	.uni-calendar-item--after-checked {
192
+		background-color: #ff5a5f;
193
+		color: #fff;
194
+	}
195
+</style>

+ 567 - 0
src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue

@@ -0,0 +1,567 @@
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
+
67
+	import { initVueI18n } from '@dcloudio/uni-i18n'
68
+	import i18nMessages from './i18n/index.js'
69
+	const {	t	} = initVueI18n(i18nMessages)
70
+
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
+				this.cale.setDate(this.nowDate.fullDate)
189
+				this.weeks = this.cale.weeks
190
+			},
191
+			endDate(val){
192
+				this.cale.resetEndDate(val)
193
+				this.cale.setDate(this.nowDate.fullDate)
194
+				this.weeks = this.cale.weeks
195
+			},
196
+			selected(newVal) {
197
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
198
+				this.weeks = this.cale.weeks
199
+			}
200
+		},
201
+		created() {
202
+			this.cale = new Calendar({
203
+				selected: this.selected,
204
+				startDate: this.startDate,
205
+				endDate: this.endDate,
206
+				range: this.range,
207
+			})
208
+			this.init(this.date)
209
+		},
210
+		methods: {
211
+			// 取消穿透
212
+			clean() {},
213
+			bindDateChange(e) {
214
+				const value = e.detail.value + '-1'
215
+				this.setDate(value)
216
+
217
+				const { year,month } = this.cale.getDate(value)
218
+        this.$emit('monthSwitch', {
219
+            year,
220
+            month
221
+        })
222
+			},
223
+			/**
224
+			 * 初始化日期显示
225
+			 * @param {Object} date
226
+			 */
227
+			init(date) {
228
+				this.cale.setDate(date)
229
+				this.weeks = this.cale.weeks
230
+				this.nowDate = this.calendar = this.cale.getInfo(date)
231
+			},
232
+			/**
233
+			 * 打开日历弹窗
234
+			 */
235
+			open() {
236
+				// 弹窗模式并且清理数据
237
+				if (this.clearDate && !this.insert) {
238
+					this.cale.cleanMultipleStatus()
239
+					// this.cale.setDate(this.date)
240
+					this.init(this.date)
241
+				}
242
+				this.show = true
243
+				this.$nextTick(() => {
244
+					setTimeout(() => {
245
+						this.aniMaskShow = true
246
+					}, 50)
247
+				})
248
+			},
249
+			/**
250
+			 * 关闭日历弹窗
251
+			 */
252
+			close() {
253
+				this.aniMaskShow = false
254
+				this.$nextTick(() => {
255
+					setTimeout(() => {
256
+						this.show = false
257
+						this.$emit('close')
258
+					}, 300)
259
+				})
260
+			},
261
+			/**
262
+			 * 确认按钮
263
+			 */
264
+			confirm() {
265
+				this.setEmit('confirm')
266
+				this.close()
267
+			},
268
+			/**
269
+			 * 变化触发
270
+			 */
271
+			change() {
272
+				if (!this.insert) return
273
+				this.setEmit('change')
274
+			},
275
+			/**
276
+			 * 选择月份触发
277
+			 */
278
+			monthSwitch() {
279
+				let {
280
+					year,
281
+					month
282
+				} = this.nowDate
283
+				this.$emit('monthSwitch', {
284
+					year,
285
+					month: Number(month)
286
+				})
287
+			},
288
+			/**
289
+			 * 派发事件
290
+			 * @param {Object} name
291
+			 */
292
+			setEmit(name) {
293
+				let {
294
+					year,
295
+					month,
296
+					date,
297
+					fullDate,
298
+					lunar,
299
+					extraInfo
300
+				} = this.calendar
301
+				this.$emit(name, {
302
+					range: this.cale.multipleStatus,
303
+					year,
304
+					month,
305
+					date,
306
+					fulldate: fullDate,
307
+					lunar,
308
+					extraInfo: extraInfo || {}
309
+				})
310
+			},
311
+			/**
312
+			 * 选择天触发
313
+			 * @param {Object} weeks
314
+			 */
315
+			choiceDate(weeks) {
316
+				if (weeks.disable) return
317
+				this.calendar = weeks
318
+				// 设置多选
319
+				this.cale.setMultiple(this.calendar.fullDate)
320
+				this.weeks = this.cale.weeks
321
+				this.change()
322
+			},
323
+			/**
324
+			 * 回到今天
325
+			 */
326
+			backToday() {
327
+				const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
328
+				const date = this.cale.getDate(new Date())
329
+        const todayYearMonth = `${date.year}-${date.month}`
330
+
331
+				this.init(date.fullDate)
332
+
333
+        if(nowYearMonth !== todayYearMonth) {
334
+          this.monthSwitch()
335
+        }
336
+
337
+				this.change()
338
+			},
339
+			/**
340
+			 * 上个月
341
+			 */
342
+			pre() {
343
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
344
+				this.setDate(preDate)
345
+				this.monthSwitch()
346
+
347
+			},
348
+			/**
349
+			 * 下个月
350
+			 */
351
+			next() {
352
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
353
+				this.setDate(nextDate)
354
+				this.monthSwitch()
355
+			},
356
+			/**
357
+			 * 设置日期
358
+			 * @param {Object} date
359
+			 */
360
+			setDate(date) {
361
+				this.cale.setDate(date)
362
+				this.weeks = this.cale.weeks
363
+				this.nowDate = this.cale.getInfo(date)
364
+			}
365
+		}
366
+	}
367
+</script>
368
+
369
+<style lang="scss" scoped>
370
+	$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
371
+	$uni-border-color: #EDEDED;
372
+	$uni-text-color: #333;
373
+	$uni-bg-color-hover:#f1f1f1;
374
+	$uni-font-size-base:14px;
375
+	$uni-text-color-placeholder: #808080;
376
+	$uni-color-subtitle: #555555;
377
+	$uni-text-color-grey:#999;
378
+	.uni-calendar {
379
+		/* #ifndef APP-NVUE */
380
+		display: flex;
381
+		/* #endif */
382
+		flex-direction: column;
383
+	}
384
+
385
+	.uni-calendar__mask {
386
+		position: fixed;
387
+		bottom: 0;
388
+		top: 0;
389
+		left: 0;
390
+		right: 0;
391
+		background-color: $uni-bg-color-mask;
392
+		transition-property: opacity;
393
+		transition-duration: 0.3s;
394
+		opacity: 0;
395
+		/* #ifndef APP-NVUE */
396
+		z-index: 99;
397
+		/* #endif */
398
+	}
399
+
400
+	.uni-calendar--mask-show {
401
+		opacity: 1
402
+	}
403
+
404
+	.uni-calendar--fixed {
405
+		position: fixed;
406
+		/* #ifdef APP-NVUE */
407
+		bottom: 0;
408
+		/* #endif */
409
+		left: 0;
410
+		right: 0;
411
+		transition-property: transform;
412
+		transition-duration: 0.3s;
413
+		transform: translateY(460px);
414
+		/* #ifndef APP-NVUE */
415
+		bottom: calc(var(--window-bottom));
416
+		z-index: 99;
417
+		/* #endif */
418
+	}
419
+
420
+	.uni-calendar--ani-show {
421
+		transform: translateY(0);
422
+	}
423
+
424
+	.uni-calendar__content {
425
+		background-color: #fff;
426
+	}
427
+
428
+	.uni-calendar__header {
429
+		position: relative;
430
+		/* #ifndef APP-NVUE */
431
+		display: flex;
432
+		/* #endif */
433
+		flex-direction: row;
434
+		justify-content: center;
435
+		align-items: center;
436
+		height: 50px;
437
+		border-bottom-color: $uni-border-color;
438
+		border-bottom-style: solid;
439
+		border-bottom-width: 1px;
440
+	}
441
+
442
+	.uni-calendar--fixed-top {
443
+		/* #ifndef APP-NVUE */
444
+		display: flex;
445
+		/* #endif */
446
+		flex-direction: row;
447
+		justify-content: space-between;
448
+		border-top-color: $uni-border-color;
449
+		border-top-style: solid;
450
+		border-top-width: 1px;
451
+	}
452
+
453
+	.uni-calendar--fixed-width {
454
+		width: 50px;
455
+	}
456
+
457
+	.uni-calendar__backtoday {
458
+		position: absolute;
459
+		right: 0;
460
+		top: 25rpx;
461
+		padding: 0 5px;
462
+		padding-left: 10px;
463
+		height: 25px;
464
+		line-height: 25px;
465
+		font-size: 12px;
466
+		border-top-left-radius: 25px;
467
+		border-bottom-left-radius: 25px;
468
+		color: $uni-text-color;
469
+		background-color: $uni-bg-color-hover;
470
+	}
471
+
472
+	.uni-calendar__header-text {
473
+		text-align: center;
474
+		width: 100px;
475
+		font-size: $uni-font-size-base;
476
+		color: $uni-text-color;
477
+	}
478
+
479
+	.uni-calendar__header-btn-box {
480
+		/* #ifndef APP-NVUE */
481
+		display: flex;
482
+		/* #endif */
483
+		flex-direction: row;
484
+		align-items: center;
485
+		justify-content: center;
486
+		width: 50px;
487
+		height: 50px;
488
+	}
489
+
490
+	.uni-calendar__header-btn {
491
+		width: 10px;
492
+		height: 10px;
493
+		border-left-color: $uni-text-color-placeholder;
494
+		border-left-style: solid;
495
+		border-left-width: 2px;
496
+		border-top-color: $uni-color-subtitle;
497
+		border-top-style: solid;
498
+		border-top-width: 2px;
499
+	}
500
+
501
+	.uni-calendar--left {
502
+		transform: rotate(-45deg);
503
+	}
504
+
505
+	.uni-calendar--right {
506
+		transform: rotate(135deg);
507
+	}
508
+
509
+
510
+	.uni-calendar__weeks {
511
+		position: relative;
512
+		/* #ifndef APP-NVUE */
513
+		display: flex;
514
+		/* #endif */
515
+		flex-direction: row;
516
+	}
517
+
518
+	.uni-calendar__weeks-item {
519
+		flex: 1;
520
+	}
521
+
522
+	.uni-calendar__weeks-day {
523
+		flex: 1;
524
+		/* #ifndef APP-NVUE */
525
+		display: flex;
526
+		/* #endif */
527
+		flex-direction: column;
528
+		justify-content: center;
529
+		align-items: center;
530
+		height: 45px;
531
+		border-bottom-color: #F5F5F5;
532
+		border-bottom-style: solid;
533
+		border-bottom-width: 1px;
534
+	}
535
+
536
+	.uni-calendar__weeks-day-text {
537
+		font-size: 14px;
538
+	}
539
+
540
+	.uni-calendar__box {
541
+		position: relative;
542
+	}
543
+
544
+	.uni-calendar__box-bg {
545
+		/* #ifndef APP-NVUE */
546
+		display: flex;
547
+		/* #endif */
548
+		justify-content: center;
549
+		align-items: center;
550
+		position: absolute;
551
+		top: 0;
552
+		left: 0;
553
+		right: 0;
554
+		bottom: 0;
555
+	}
556
+
557
+	.uni-calendar__box-bg-text {
558
+		font-size: 200px;
559
+		font-weight: bold;
560
+		color: $uni-text-color-grey;
561
+		opacity: 0.1;
562
+		text-align: center;
563
+		/* #ifndef APP-NVUE */
564
+		line-height: 1;
565
+		/* #endif */
566
+	}
567
+</style>

+ 360 - 0
src/uni_modules/uni-calendar/components/uni-calendar/util.js

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

+ 105 - 0
src/uni_modules/uni-calendar/package.json

@@ -0,0 +1,105 @@
1
+{
2
+  "id": "uni-calendar",
3
+  "displayName": "uni-calendar 日历",
4
+  "version": "1.4.12",
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
+    "uni-app": "^3.1.0",
18
+    "uni-app-x": "^3.1.0"
19
+  },
20
+  "directories": {
21
+    "example": "../../temps/example_temps"
22
+  },
23
+  "dcloudext": {
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
+    "type": "component-vue",
42
+    "darkmode": "-",
43
+    "i18n": "-",
44
+    "widescreen": "-"
45
+  },
46
+  "uni_modules": {
47
+    "dependencies": [],
48
+    "encrypt": [],
49
+    "platforms": {
50
+      "cloud": {
51
+        "tcb": "√",
52
+        "aliyun": "√",
53
+        "alipay": "x"
54
+      },
55
+      "client": {
56
+        "uni-app": {
57
+          "vue": {
58
+            "vue2": "-",
59
+            "vue3": "-"
60
+          },
61
+          "web": {
62
+            "safari": "-",
63
+            "chrome": "-"
64
+          },
65
+          "app": {
66
+            "vue": "-",
67
+            "nvue": "-",
68
+            "android": "-",
69
+            "ios": "-",
70
+            "harmony": "-"
71
+          },
72
+          "mp": {
73
+            "weixin": "-",
74
+            "alipay": "-",
75
+            "toutiao": "-",
76
+            "baidu": "-",
77
+            "kuaishou": "-",
78
+            "jd": "-",
79
+            "harmony": "-",
80
+            "qq": "-",
81
+            "lark": "-"
82
+          },
83
+          "quickapp": {
84
+            "huawei": "-",
85
+            "union": "-"
86
+          }
87
+        },
88
+        "uni-app-x": {
89
+          "web": {
90
+            "safari": "-",
91
+            "chrome": "-"
92
+          },
93
+          "app": {
94
+            "android": "-",
95
+            "ios": "-",
96
+            "harmony": "-"
97
+          },
98
+          "mp": {
99
+            "weixin": "-"
100
+          }
101
+        }
102
+      }
103
+    }
104
+  }
105
+}

+ 103 - 0
src/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
src/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
src/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>

+ 106 - 0
src/uni_modules/uni-card/package.json

@@ -0,0 +1,106 @@
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
+    "uni-app": "^3.1.0",
17
+    "uni-app-x": "^3.1.0"
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "-",
42
+    "i18n": "-",
43
+    "widescreen": "-"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-icons",
48
+      "uni-scss"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "√",
54
+        "aliyun": "√"
55
+      },
56
+      "client": {
57
+        "uni-app": {
58
+          "vue": {
59
+            "vue2": "-",
60
+            "vue3": "-"
61
+          },
62
+          "web": {
63
+            "safari": "-",
64
+            "chrome": "-"
65
+          },
66
+          "app": {
67
+            "vue": "-",
68
+            "nvue": "-",
69
+            "android": "-",
70
+            "ios": "-",
71
+            "harmony": "-"
72
+          },
73
+          "mp": {
74
+            "weixin": "-",
75
+            "alipay": "-",
76
+            "toutiao": "-",
77
+            "baidu": "-",
78
+            "kuaishou": "-",
79
+            "jd": "-",
80
+            "harmony": "-",
81
+            "qq": "-",
82
+            "lark": "-"
83
+          },
84
+          "quickapp": {
85
+            "huawei": "-",
86
+            "union": "-"
87
+          }
88
+        },
89
+        "uni-app-x": {
90
+          "web": {
91
+            "safari": "-",
92
+            "chrome": "-"
93
+          },
94
+          "app": {
95
+            "android": "-",
96
+            "ios": "-",
97
+            "harmony": "-"
98
+          },
99
+          "mp": {
100
+            "weixin": "-"
101
+          }
102
+        }
103
+      }
104
+    }
105
+  }
106
+}

+ 12 - 0
src/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
+

+ 44 - 0
src/uni_modules/uni-collapse/changelog.md

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

+ 402 - 0
src/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 {String} border = ['auto'|'show'|'none'] 是否显示分隔线
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>

+ 145 - 0
src/uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue

@@ -0,0 +1,145 @@
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
+				const str = typeof val === 'string'
70
+				const arr = Array.isArray(val)
71
+				this.childrens.forEach((vm) => {
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
+						const isOpen = val.findIndex(v => v === vm.nameSync) !== -1
83
+						if (this.accordion && isOpen) {
84
+							console.warn('accordion 属性为 true ,v-model 类型应该为 string')
85
+							return
86
+						}
87
+						vm.isOpen = isOpen
88
+					}
89
+				})
90
+				this.emit(val)
91
+				this.$emit('change', val)
92
+			},
93
+			setAccordion(self) {
94
+				if (!this.accordion) return
95
+				this.childrens.forEach((vm, index) => {
96
+					if (self !== vm) {
97
+						vm.isOpen = false
98
+					}
99
+				})
100
+			},
101
+			resize() {
102
+				this.childrens.forEach((vm, index) => {
103
+					// #ifndef APP-NVUE
104
+					vm.getCollapseHeight()
105
+					// #endif
106
+					// #ifdef APP-NVUE
107
+					vm.getNvueHwight()
108
+					// #endif
109
+				})
110
+			},
111
+			onChange(isOpen, self) {
112
+				let activeItem = []
113
+
114
+				if (this.accordion) {
115
+					activeItem = isOpen ? self.nameSync : ''
116
+				} else {
117
+					this.childrens.forEach((vm, index) => {
118
+						if (vm.isOpen) {
119
+							activeItem.push(vm.nameSync)
120
+						}
121
+					})
122
+				}
123
+				this.$emit('change', activeItem)
124
+				this.emit(activeItem)
125
+			},
126
+			emit(val){
127
+				this.$emit('input', val)
128
+				this.$emit('update:modelValue', val)
129
+			}
130
+		}
131
+	}
132
+</script>
133
+<style lang="scss" >
134
+	.uni-collapse {
135
+		/* #ifndef APP-NVUE */
136
+		width: 100%;
137
+		display: flex;
138
+		/* #endif */
139
+		/* #ifdef APP-NVUE */
140
+		flex: 1;
141
+		/* #endif */
142
+		flex-direction: column;
143
+		background-color: #fff;
144
+	}
145
+</style>

+ 106 - 0
src/uni_modules/uni-collapse/package.json

@@ -0,0 +1,106 @@
1
+{
2
+  "id": "uni-collapse",
3
+  "displayName": "uni-collapse 折叠面板",
4
+  "version": "1.4.6",
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
+    "uni-app": "^4.07",
16
+    "uni-app-x": ""
17
+  },
18
+  "directories": {
19
+    "example": "../../temps/example_temps"
20
+  },
21
+  "dcloudext": {
22
+    "sale": {
23
+      "regular": {
24
+        "price": "0.00"
25
+      },
26
+      "sourcecode": {
27
+        "price": "0.00"
28
+      }
29
+    },
30
+    "contact": {
31
+      "qq": ""
32
+    },
33
+    "declaration": {
34
+      "ads": "无",
35
+      "data": "无",
36
+      "permissions": "无"
37
+    },
38
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
39
+    "type": "component-vue",
40
+    "darkmode": "x",
41
+    "i18n": "x",
42
+    "widescreen": "x"
43
+  },
44
+  "uni_modules": {
45
+    "dependencies": [
46
+      "uni-scss",
47
+      "uni-icons"
48
+    ],
49
+    "encrypt": [],
50
+    "platforms": {
51
+      "cloud": {
52
+        "tcb": "x",
53
+        "aliyun": "x",
54
+        "alipay": "x"
55
+      },
56
+      "client": {
57
+        "uni-app": {
58
+          "vue": {
59
+            "vue2": "√",
60
+            "vue3": "√"
61
+          },
62
+          "web": {
63
+            "safari": "√",
64
+            "chrome": "√"
65
+          },
66
+          "app": {
67
+            "vue": "√",
68
+            "nvue": "√",
69
+            "android": "√",
70
+            "ios": "√",
71
+            "harmony": "√"
72
+          },
73
+          "mp": {
74
+            "weixin": "√",
75
+            "alipay": "√",
76
+            "toutiao": "√",
77
+            "baidu": "√",
78
+            "kuaishou": "-",
79
+            "jd": "-",
80
+            "harmony": "-",
81
+            "qq": "√",
82
+            "lark": "-"
83
+          },
84
+          "quickapp": {
85
+            "huawei": "√",
86
+            "union": "√"
87
+          }
88
+        },
89
+        "uni-app-x": {
90
+          "web": {
91
+            "safari": "-",
92
+            "chrome": "-"
93
+          },
94
+          "app": {
95
+            "android": "-",
96
+            "ios": "-",
97
+            "harmony": "-"
98
+          },
99
+          "mp": {
100
+            "weixin": "-"
101
+          }
102
+        }
103
+      }
104
+    }
105
+  }
106
+}

+ 12 - 0
src/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 

+ 17 - 0
src/uni_modules/uni-combox/changelog.md

@@ -0,0 +1,17 @@
1
+## 1.0.2(2024-09-21)
2
+- 新增 clearAble属性
3
+## 1.0.1(2021-11-23)
4
+- 优化 label、label-width 属性
5
+## 1.0.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-combox](https://uniapp.dcloud.io/component/uniui/uni-combox)
8
+## 0.1.0(2021-07-30)
9
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
10
+## 0.0.6(2021-05-12)
11
+- 新增 组件示例地址
12
+## 0.0.5(2021-04-21)
13
+- 优化 添加依赖 uni-icons, 导入后自动下载依赖
14
+## 0.0.4(2021-02-05)
15
+- 优化 组件引用关系,通过uni_modules引用组件
16
+## 0.0.3(2021-02-04)
17
+- 调整为uni_modules目录规范

+ 301 - 0
src/uni_modules/uni-combox/components/uni-combox/uni-combox.vue

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

+ 107 - 0
src/uni_modules/uni-combox/package.json

@@ -0,0 +1,107 @@
1
+{
2
+  "id": "uni-combox",
3
+  "displayName": "uni-combox 组合框",
4
+  "version": "1.0.2",
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
+    "uni-app": "^3.1.0",
17
+    "uni-app-x": "^3.1.0"
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "-",
42
+    "i18n": "-",
43
+    "widescreen": "-"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-scss",
48
+      "uni-icons"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "√",
54
+        "aliyun": "√",
55
+        "alipay": "x"
56
+      },
57
+      "client": {
58
+        "uni-app": {
59
+          "vue": {
60
+            "vue2": "-",
61
+            "vue3": "-"
62
+          },
63
+          "web": {
64
+            "safari": "-",
65
+            "chrome": "-"
66
+          },
67
+          "app": {
68
+            "vue": "-",
69
+            "nvue": "-",
70
+            "android": "-",
71
+            "ios": "-",
72
+            "harmony": "-"
73
+          },
74
+          "mp": {
75
+            "weixin": "-",
76
+            "alipay": "-",
77
+            "toutiao": "-",
78
+            "baidu": "-",
79
+            "kuaishou": "-",
80
+            "jd": "-",
81
+            "harmony": "-",
82
+            "qq": "-",
83
+            "lark": "-"
84
+          },
85
+          "quickapp": {
86
+            "huawei": "-",
87
+            "union": "-"
88
+          }
89
+        },
90
+        "uni-app-x": {
91
+          "web": {
92
+            "safari": "-",
93
+            "chrome": "-"
94
+          },
95
+          "app": {
96
+            "android": "-",
97
+            "ios": "-",
98
+            "harmony": "-"
99
+          },
100
+          "mp": {
101
+            "weixin": "-"
102
+          }
103
+        }
104
+      }
105
+    }
106
+  }
107
+}

+ 11 - 0
src/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 

+ 30 - 0
src/uni_modules/uni-countdown/changelog.md

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

+ 6 - 0
src/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
src/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
src/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
src/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
+}

+ 278 - 0
src/uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue

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

+ 105 - 0
src/uni_modules/uni-countdown/package.json

@@ -0,0 +1,105 @@
1
+{
2
+  "id": "uni-countdown",
3
+  "displayName": "uni-countdown 倒计时",
4
+  "version": "1.2.5",
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
+    "uni-app": "^3.1.0",
16
+    "uni-app-x": "^3.1.0"
17
+  },
18
+  "directories": {
19
+    "example": "../../temps/example_temps"
20
+  },
21
+  "dcloudext": {
22
+    "sale": {
23
+      "regular": {
24
+        "price": "0.00"
25
+      },
26
+      "sourcecode": {
27
+        "price": "0.00"
28
+      }
29
+    },
30
+    "contact": {
31
+      "qq": ""
32
+    },
33
+    "declaration": {
34
+      "ads": "无",
35
+      "data": "无",
36
+      "permissions": "无"
37
+    },
38
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
39
+    "type": "component-vue",
40
+    "darkmode": "-",
41
+    "i18n": "-",
42
+    "widescreen": "-"
43
+  },
44
+  "uni_modules": {
45
+    "dependencies": [
46
+      "uni-scss"
47
+    ],
48
+    "encrypt": [],
49
+    "platforms": {
50
+      "cloud": {
51
+        "tcb": "√",
52
+        "aliyun": "√",
53
+        "alipay": "x"
54
+      },
55
+      "client": {
56
+        "uni-app": {
57
+          "vue": {
58
+            "vue2": "-",
59
+            "vue3": "-"
60
+          },
61
+          "web": {
62
+            "safari": "-",
63
+            "chrome": "-"
64
+          },
65
+          "app": {
66
+            "vue": "-",
67
+            "nvue": "-",
68
+            "android": "-",
69
+            "ios": "-",
70
+            "harmony": "-"
71
+          },
72
+          "mp": {
73
+            "weixin": "-",
74
+            "alipay": "-",
75
+            "toutiao": "-",
76
+            "baidu": "-",
77
+            "kuaishou": "-",
78
+            "jd": "-",
79
+            "harmony": "-",
80
+            "qq": "-",
81
+            "lark": "-"
82
+          },
83
+          "quickapp": {
84
+            "huawei": "-",
85
+            "union": "-"
86
+          }
87
+        },
88
+        "uni-app-x": {
89
+          "web": {
90
+            "safari": "-",
91
+            "chrome": "-"
92
+          },
93
+          "app": {
94
+            "android": "-",
95
+            "ios": "-",
96
+            "harmony": "-"
97
+          },
98
+          "mp": {
99
+            "weixin": "-"
100
+          }
101
+        }
102
+      }
103
+    }
104
+  }
105
+}

+ 10 - 0
src/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 

+ 51 - 0
src/uni_modules/uni-data-checkbox/changelog.md

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

+ 316 - 0
src/uni_modules/uni-data-checkbox/components/uni-data-checkbox/clientdb.js

@@ -0,0 +1,316 @@
1
+
2
+const events = {
3
+	load: 'load',
4
+	error: 'error'
5
+}
6
+const pageMode = {
7
+	add: 'add',
8
+	replace: 'replace'
9
+}
10
+
11
+const attrs = [
12
+	'pageCurrent',
13
+	'pageSize',
14
+	'collection',
15
+	'action',
16
+	'field',
17
+	'getcount',
18
+	'orderby',
19
+	'where'
20
+]
21
+
22
+export default {
23
+	data() {
24
+		return {
25
+			loading: false,
26
+			listData: this.getone ? {} : [],
27
+			paginationInternal: {
28
+				current: this.pageCurrent,
29
+				size: this.pageSize,
30
+				count: 0
31
+			},
32
+			errorMessage: ''
33
+		}
34
+	},
35
+	created() {
36
+		let db = null;
37
+		let dbCmd = null;
38
+
39
+		if(this.collection){
40
+			this.db = uniCloud.database();
41
+			this.dbCmd = this.db.command;
42
+		}
43
+
44
+		this._isEnded = false
45
+
46
+		this.$watch(() => {
47
+			let al = []
48
+			attrs.forEach(key => {
49
+				al.push(this[key])
50
+			})
51
+			return al
52
+		}, (newValue, oldValue) => {
53
+			this.paginationInternal.pageSize = this.pageSize
54
+
55
+			let needReset = false
56
+			for (let i = 2; i < newValue.length; i++) {
57
+				if (newValue[i] != oldValue[i]) {
58
+					needReset = true
59
+					break
60
+				}
61
+			}
62
+			if (needReset) {
63
+				this.clear()
64
+				this.reset()
65
+			}
66
+			if (newValue[0] != oldValue[0]) {
67
+				this.paginationInternal.current = this.pageCurrent
68
+			}
69
+
70
+			this._execLoadData()
71
+		})
72
+
73
+		// #ifdef H5
74
+		if (process.env.NODE_ENV === 'development') {
75
+			this._debugDataList = []
76
+			if (!window.unidev) {
77
+				window.unidev = {
78
+					clientDB: {
79
+						data: []
80
+					}
81
+				}
82
+			}
83
+			unidev.clientDB.data.push(this._debugDataList)
84
+		}
85
+		// #endif
86
+
87
+		// #ifdef MP-TOUTIAO
88
+		let changeName
89
+		let events = this.$scope.dataset.eventOpts
90
+		for (let i = 0; i < events.length; i++) {
91
+			let event = events[i]
92
+			if (event[0].includes('^load')) {
93
+				changeName = event[1][0][0]
94
+			}
95
+		}
96
+		if (changeName) {
97
+			let parent = this.$parent
98
+			let maxDepth = 16
99
+			this._changeDataFunction = null
100
+			while (parent && maxDepth > 0) {
101
+				let fun = parent[changeName]
102
+				if (fun && typeof fun === 'function') {
103
+					this._changeDataFunction = fun
104
+					maxDepth = 0
105
+					break
106
+				}
107
+				parent = parent.$parent
108
+				maxDepth--;
109
+			}
110
+		}
111
+		// #endif
112
+
113
+		// if (!this.manual) {
114
+		// 	this.loadData()
115
+		// }
116
+	},
117
+	// #ifdef H5
118
+	beforeDestroy() {
119
+		if (process.env.NODE_ENV === 'development' && window.unidev) {
120
+			let cd = this._debugDataList
121
+			let dl = unidev.clientDB.data
122
+			for (let i = dl.length - 1; i >= 0; i--) {
123
+				if (dl[i] === cd) {
124
+					dl.splice(i, 1)
125
+					break
126
+				}
127
+			}
128
+		}
129
+	},
130
+	// #endif
131
+	methods: {
132
+		loadData(args1, args2) {
133
+			let callback = null
134
+			if (typeof args1 === 'object') {
135
+				if (args1.clear) {
136
+					this.clear()
137
+					this.reset()
138
+				}
139
+				if (args1.current !== undefined) {
140
+					this.paginationInternal.current = args1.current
141
+				}
142
+				if (typeof args2 === 'function') {
143
+					callback = args2
144
+				}
145
+			} else if (typeof args1 === 'function') {
146
+				callback = args1
147
+			}
148
+
149
+			this._execLoadData(callback)
150
+		},
151
+		loadMore() {
152
+			if (this._isEnded) {
153
+				return
154
+			}
155
+			this._execLoadData()
156
+		},
157
+		refresh() {
158
+			this.clear()
159
+			this._execLoadData()
160
+		},
161
+		clear() {
162
+			this._isEnded = false
163
+			this.listData = []
164
+		},
165
+		reset() {
166
+			this.paginationInternal.current = 1
167
+		},
168
+		remove(id, {
169
+			action,
170
+			callback,
171
+			confirmTitle,
172
+			confirmContent
173
+		} = {}) {
174
+			if (!id || !id.length) {
175
+				return
176
+			}
177
+			uni.showModal({
178
+				title: confirmTitle || '提示',
179
+				content: confirmContent || '是否删除该数据',
180
+				showCancel: true,
181
+				success: (res) => {
182
+					if (!res.confirm) {
183
+						return
184
+					}
185
+					this._execRemove(id, action, callback)
186
+				}
187
+			})
188
+		},
189
+		_execLoadData(callback) {
190
+			if (this.loading) {
191
+				return
192
+			}
193
+			this.loading = true
194
+			this.errorMessage = ''
195
+
196
+			this._getExec().then((res) => {
197
+				this.loading = false
198
+				const {
199
+					data,
200
+					count
201
+				} = res.result
202
+				this._isEnded = data.length < this.pageSize
203
+
204
+				callback && callback(data, this._isEnded)
205
+				this._dispatchEvent(events.load, data)
206
+
207
+				if (this.getone) {
208
+					this.listData = data.length ? data[0] : undefined
209
+				} else if (this.pageData === pageMode.add) {
210
+					this.listData.push(...data)
211
+					if (this.listData.length) {
212
+						this.paginationInternal.current++
213
+					}
214
+				} else if (this.pageData === pageMode.replace) {
215
+					this.listData = data
216
+					this.paginationInternal.count = count
217
+				}
218
+
219
+				// #ifdef H5
220
+				if (process.env.NODE_ENV === 'development') {
221
+					this._debugDataList.length = 0
222
+					this._debugDataList.push(...JSON.parse(JSON.stringify(this.listData)))
223
+				}
224
+				// #endif
225
+			}).catch((err) => {
226
+				this.loading = false
227
+				this.errorMessage = err
228
+				callback && callback()
229
+				this.$emit(events.error, err)
230
+			})
231
+		},
232
+		_getExec() {
233
+			let exec = this.db
234
+			if (this.action) {
235
+				exec = exec.action(this.action)
236
+			}
237
+
238
+			exec = exec.collection(this.collection)
239
+
240
+			if (!(!this.where || !Object.keys(this.where).length)) {
241
+				exec = exec.where(this.where)
242
+			}
243
+			if (this.field) {
244
+				exec = exec.field(this.field)
245
+			}
246
+			if (this.orderby) {
247
+				exec = exec.orderBy(this.orderby)
248
+			}
249
+
250
+			const {
251
+				current,
252
+				size
253
+			} = this.paginationInternal
254
+			exec = exec.skip(size * (current - 1)).limit(size).get({
255
+				getCount: this.getcount
256
+			})
257
+
258
+			return exec
259
+		},
260
+		_execRemove(id, action, callback) {
261
+			if (!this.collection || !id) {
262
+				return
263
+			}
264
+
265
+			const ids = Array.isArray(id) ? id : [id]
266
+			if (!ids.length) {
267
+				return
268
+			}
269
+
270
+			uni.showLoading({
271
+				mask: true
272
+			})
273
+
274
+			let exec = this.db
275
+			if (action) {
276
+				exec = exec.action(action)
277
+			}
278
+
279
+			exec.collection(this.collection).where({
280
+				_id: dbCmd.in(ids)
281
+			}).remove().then((res) => {
282
+				callback && callback(res.result)
283
+				if (this.pageData === pageMode.replace) {
284
+					this.refresh()
285
+				} else {
286
+					this.removeData(ids)
287
+				}
288
+			}).catch((err) => {
289
+				uni.showModal({
290
+					content: err.message,
291
+					showCancel: false
292
+				})
293
+			}).finally(() => {
294
+				uni.hideLoading()
295
+			})
296
+		},
297
+		removeData(ids) {
298
+			let il = ids.slice(0)
299
+			let dl = this.listData
300
+			for (let i = dl.length - 1; i >= 0; i--) {
301
+				let index = il.indexOf(dl[i]._id)
302
+				if (index >= 0) {
303
+					dl.splice(i, 1)
304
+					il.splice(index, 1)
305
+				}
306
+			}
307
+		},
308
+		_dispatchEvent(type, data) {
309
+			if (this._changeDataFunction) {
310
+				this._changeDataFunction(data, this._isEnded)
311
+			} else {
312
+				this.$emit(type, data, this._isEnded)
313
+			}
314
+		}
315
+	}
316
+}

+ 853 - 0
src/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue

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

+ 107 - 0
src/uni_modules/uni-data-checkbox/package.json

@@ -0,0 +1,107 @@
1
+{
2
+  "id": "uni-data-checkbox",
3
+  "displayName": "uni-data-checkbox 数据选择器",
4
+  "version": "1.0.6",
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
+    "uni-app": "^3.1.0",
17
+    "uni-app-x": "^3.1.0"
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "-",
42
+    "i18n": "-",
43
+    "widescreen": "-"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-load-more",
48
+      "uni-scss"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "√",
54
+        "aliyun": "√",
55
+        "alipay": "x"
56
+      },
57
+      "client": {
58
+        "uni-app": {
59
+          "vue": {
60
+            "vue2": "-",
61
+            "vue3": "-"
62
+          },
63
+          "web": {
64
+            "safari": "-",
65
+            "chrome": "-"
66
+          },
67
+          "app": {
68
+            "vue": "-",
69
+            "nvue": "-",
70
+            "android": "-",
71
+            "ios": "-",
72
+            "harmony": "-"
73
+          },
74
+          "mp": {
75
+            "weixin": "-",
76
+            "alipay": "-",
77
+            "toutiao": "-",
78
+            "baidu": "-",
79
+            "kuaishou": "-",
80
+            "jd": "-",
81
+            "harmony": "-",
82
+            "qq": "-",
83
+            "lark": "-"
84
+          },
85
+          "quickapp": {
86
+            "huawei": "-",
87
+            "union": "-"
88
+          }
89
+        },
90
+        "uni-app-x": {
91
+          "web": {
92
+            "safari": "-",
93
+            "chrome": "-"
94
+          },
95
+          "app": {
96
+            "android": "-",
97
+            "ios": "-",
98
+            "harmony": "-"
99
+          },
100
+          "mp": {
101
+            "weixin": "-"
102
+          }
103
+        }
104
+      }
105
+    }
106
+  }
107
+}

+ 18 - 0
src/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 

+ 79 - 0
src/uni_modules/uni-data-picker/changelog.md

@@ -0,0 +1,79 @@
1
+## 2.0.2(2025-04-14)
2
+- 修复 在readonly属性为true时选项匹配错误的问题
3
+## 2.0.0(2023-12-14)
4
+- 新增 支持 uni-app-x
5
+## 1.1.2(2023-04-11)
6
+- 修复 更改 modelValue 报错的 bug
7
+- 修复 v-for 未使用 key 值控制台 warning
8
+## 1.1.1(2023-02-21)
9
+- 修复代码合并时引发 value 属性为空时不渲染数据的问题
10
+## 1.1.0(2023-02-15)
11
+- 修复 localdata 不支持动态更新的bug
12
+## 1.0.9(2023-02-15)
13
+- 修复 localdata 不支持动态更新的bug
14
+## 1.0.8(2022-09-16)
15
+- 可以使用 uni-scss 控制主题色
16
+## 1.0.7(2022-07-06)
17
+- 优化 pc端图标位置不正确的问题
18
+## 1.0.6(2022-07-05)
19
+- 优化 显示样式
20
+## 1.0.5(2022-07-04)
21
+- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
22
+## 1.0.4(2022-04-19)
23
+- 修复 字节小程序 本地数据无法选择下一级的Bug
24
+## 1.0.3(2022-02-25)
25
+- 修复 nvue 不支持的 v-show 的 bug
26
+## 1.0.2(2022-02-25)
27
+- 修复 条件编译 nvue 不支持的 css 样式
28
+## 1.0.1(2021-11-23)
29
+- 修复 由上个版本引发的map、v-model等属性不生效的bug
30
+## 1.0.0(2021-11-19)
31
+- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
32
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker)
33
+## 0.4.9(2021-10-28)
34
+- 修复 VUE2 v-model 概率无效的 bug
35
+## 0.4.8(2021-10-27)
36
+- 修复 v-model 概率无效的 bug
37
+## 0.4.7(2021-10-25)
38
+- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+
39
+- 修复 树型 uniCloud 数据类型为 int 时报错的 bug
40
+## 0.4.6(2021-10-19)
41
+- 修复 非 VUE3 v-model 为 0 时无法选中的 bug
42
+## 0.4.5(2021-09-26)
43
+- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效
44
+- 修复 readonly 为 true 时报错的 bug
45
+## 0.4.4(2021-09-26)
46
+- 修复 上一版本造成的 map 属性失效的 bug
47
+- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略
48
+## 0.4.3(2021-09-24)
49
+- 修复 某些情况下级联未触发的 bug
50
+## 0.4.2(2021-09-23)
51
+- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用
52
+- 新增 选项内容过长自动添加省略号
53
+## 0.4.1(2021-09-15)
54
+- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段
55
+## 0.4.0(2021-07-13)
56
+- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
57
+## 0.3.5(2021-06-04)
58
+- 修复 无法加载云端数据的问题
59
+## 0.3.4(2021-05-28)
60
+- 修复 v-model 无效问题
61
+- 修复 loaddata 为空数据组时加载时间过长问题
62
+- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点
63
+## 0.3.3(2021-05-12)
64
+- 新增 组件示例地址
65
+## 0.3.2(2021-04-22)
66
+- 修复 非树形数据有 where 属性查询报错的问题
67
+## 0.3.1(2021-04-15)
68
+- 修复 本地数据概率无法回显时问题
69
+## 0.3.0(2021-04-07)
70
+- 新增 支持云端非树形表结构数据
71
+- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题
72
+## 0.2.0(2021-03-15)
73
+- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题
74
+## 0.1.9(2021-03-09)
75
+- 修复 微信小程序某些情况下无法选择的问题
76
+## 0.1.8(2021-02-05)
77
+- 优化 部分样式在 nvue 上的兼容表现
78
+## 0.1.7(2021-02-05)
79
+- 调整为 uni_modules 目录规范

+ 45 - 0
src/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

+ 380 - 0
src/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue

@@ -0,0 +1,380 @@
1
+<template>
2
+  <view class="uni-data-tree">
3
+    <view class="uni-data-tree-input" @click="handleInput">
4
+      <slot :data="selectedPaths" :error="error">
5
+        <view class="input-value" :class="{'input-value-border': border}">
6
+          <text v-if="error!=null" class="error-text">{{error!.errMsg}}</text>
7
+          <scroll-view v-if="selectedPaths.length" class="selected-path" scroll-x="true">
8
+            <view class="selected-list">
9
+              <template v-for="(item, index) in selectedPaths">
10
+                <text class="text-color">{{item[mappingTextName]}}</text>
11
+                <text v-if="index<selectedPaths.length-1" class="input-split-line">{{split}}</text>
12
+              </template>
13
+            </view>
14
+          </scroll-view>
15
+          <text v-else-if="error==null&&!loading" class="placeholder">{{placeholder}}</text>
16
+          <view v-if="!readonly" class="arrow-area">
17
+            <view class="input-arrow"></view>
18
+          </view>
19
+        </view>
20
+      </slot>
21
+      <view v-if="loading && !isOpened" class="selected-loading">
22
+        <slot name="picker-loading" :loading="loading"></slot>
23
+      </view>
24
+    </view>
25
+    <view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
26
+    <view class="uni-data-tree-dialog" v-if="isOpened">
27
+      <view class="uni-popper__arrow"></view>
28
+      <view class="dialog-caption">
29
+        <view class="dialog-title-view">
30
+          <text class="dialog-title">{{popupTitle}}</text>
31
+        </view>
32
+        <view class="dialog-close" @click="handleClose">
33
+          <view class="dialog-close-plus" data-id="close"></view>
34
+          <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
35
+        </view>
36
+      </view>
37
+      <view ref="pickerView" class="uni-data-pickerview">
38
+        <view v-if="error!=null" class="error">
39
+          <text class="error-text">{{error!.errMsg}}</text>
40
+        </view>
41
+        <scroll-view v-if="!isCloudDataList" :scroll-x="true">
42
+          <view class="selected-node-list">
43
+            <template v-for="(item, index) in selectedNodes">
44
+              <text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}"
45
+                @click="onTabSelect(index)">
46
+                {{item[mappingTextName]}}
47
+              </text>
48
+            </template>
49
+          </view>
50
+        </scroll-view>
51
+        <list-view class="list-view" :scroll-y="true">
52
+          <list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)">
53
+            <text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text>
54
+            <text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text>
55
+          </list-item>
56
+        </list-view>
57
+        <view class="loading-cover" v-if="loading">
58
+          <slot name="pickerview-loading" :loading="loading"></slot>
59
+        </view>
60
+      </view>
61
+    </view>
62
+  </view>
63
+</template>
64
+
65
+<script>
66
+  import { dataPicker } from "../uni-data-pickerview/uni-data-picker.uts"
67
+
68
+  /**
69
+   * DataPicker 级联选择
70
+   * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。
71
+   * @tutorial https://ext.dcloud.net.cn/plugin?id=3796
72
+   * @property {String} popup-title 弹出窗口标题
73
+   * @property {Array} localdata 本地数据,参考
74
+   * @property {Boolean} border = [true|false] 是否有边框
75
+   * @property {Boolean} readonly = [true|false] 是否仅读
76
+   * @property {Boolean} preload = [true|false] 是否预加载数据
77
+   * @value true 开启预加载数据,点击弹出窗口后显示已加载数据
78
+   * @value false 关闭预加载数据,点击弹出窗口后开始加载数据
79
+   * @property {Boolean} step-searh = [true|false] 是否分布查询
80
+   * @value true 启用分布查询,仅查询当前选中节点
81
+   * @value false 关闭分布查询,一次查询出所有数据
82
+   * @property {String|DBFieldString} self-field 分布查询当前字段名称
83
+   * @property {String|DBFieldString} parent-field 分布查询父字段名称
84
+   * @property {String|DBCollectionString} collection 表名
85
+   * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
86
+   * @property {String} orderby 排序字段及正序倒叙设置
87
+   * @property {String|JQLString} where 查询条件
88
+   * @event {Function} popupshow 弹出的选择窗口打开时触发此事件
89
+   * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件
90
+   */
91
+  export default {
92
+    name: 'UniDataPicker',
93
+    emits: ['popupopened', 'popupclosed', 'nodeclick', 'change', 'input', 'update:modelValue', 'inputclick'],
94
+    mixins: [dataPicker],
95
+    props: {
96
+      popupTitle: {
97
+        type: String,
98
+        default: '请选择'
99
+      },
100
+      placeholder: {
101
+        type: String,
102
+        default: '请选择'
103
+      },
104
+      heightMobile: {
105
+        type: String,
106
+        default: ''
107
+      },
108
+      readonly: {
109
+        type: Boolean,
110
+        default: false
111
+      },
112
+      clearIcon: {
113
+        type: Boolean,
114
+        default: true
115
+      },
116
+      border: {
117
+        type: Boolean,
118
+        default: true
119
+      },
120
+      split: {
121
+        type: String,
122
+        default: '/'
123
+      },
124
+      ellipsis: {
125
+        type: Boolean,
126
+        default: true
127
+      }
128
+    },
129
+    data() {
130
+      return {
131
+        isOpened: false
132
+      }
133
+    },
134
+    computed: {
135
+      isShowClearIcon() : boolean {
136
+        if (this.readonly) {
137
+          return false
138
+        }
139
+
140
+        if (this.clearIcon && this.selectedPaths.length > 0) {
141
+          return true
142
+        }
143
+
144
+        return false
145
+      }
146
+    },
147
+    created() {
148
+      this.load()
149
+    },
150
+    methods: {
151
+      clear() {
152
+      },
153
+      load() {
154
+        if (this.isLocalData) {
155
+          this.loadLocalData()
156
+        } else if (this.isCloudDataList || this.isCloudDataTree) {
157
+          this.loadCloudDataPath()
158
+        }
159
+      },
160
+      show() {
161
+        this.isOpened = true
162
+        this.$emit('popupopened')
163
+        if (!this.hasCloudTreeData) {
164
+          this.loadData()
165
+        }
166
+      },
167
+      hide() {
168
+        this.isOpened = false
169
+        this.$emit('popupclosed')
170
+      },
171
+      handleInput() {
172
+        if (this.readonly) {
173
+          this.$emit('inputclick')
174
+        } else {
175
+          this.show()
176
+        }
177
+      },
178
+      handleClose() {
179
+        this.hide()
180
+      },
181
+      onFinish() {
182
+        this.selectedPaths = this.getChangeNodes()
183
+        this.$emit('change', this.selectedPaths)
184
+        this.hide()
185
+      }
186
+    }
187
+  }
188
+</script>
189
+
190
+<style>
191
+  @import url("../uni-data-pickerview/uni-data-pickerview.css");
192
+
193
+  .uni-data-tree {
194
+    position: relative;
195
+  }
196
+
197
+  .uni-data-tree-input {
198
+    position: relative;
199
+  }
200
+
201
+  .selected-loading {
202
+    display: flex;
203
+    justify-content: center;
204
+    position: absolute;
205
+    left: 0;
206
+    top: 0;
207
+    right: 0;
208
+    bottom: 0;
209
+  }
210
+
211
+  .error-text {
212
+    flex: 1;
213
+    font-size: 12px;
214
+    color: #DD524D;
215
+  }
216
+
217
+  .input-value {
218
+    flex-direction: row;
219
+    align-items: center;
220
+    flex-wrap: nowrap;
221
+    padding: 5px 5px;
222
+    padding-right: 5px;
223
+    overflow: hidden;
224
+    min-height: 28px;
225
+  }
226
+
227
+  .input-value-border {
228
+    border: 1px solid #e5e5e5;
229
+    border-radius: 5px;
230
+  }
231
+
232
+  .selected-path {
233
+    flex: 1;
234
+    flex-direction: row;
235
+    overflow: hidden;
236
+  }
237
+
238
+  .load-more {
239
+    width: 40px;
240
+  }
241
+
242
+  .selected-list {
243
+    flex-direction: row;
244
+    flex-wrap: nowrap;
245
+  }
246
+
247
+  .selected-item {
248
+    flex-direction: row;
249
+    flex-wrap: nowrap;
250
+  }
251
+
252
+  .text-color {
253
+    font-size: 14px;
254
+    color: #333;
255
+  }
256
+
257
+  .placeholder {
258
+    color: grey;
259
+    font-size: 14px;
260
+  }
261
+
262
+  .input-split-line {
263
+    opacity: .5;
264
+    margin-left: 1px;
265
+    margin-right: 1px;
266
+  }
267
+
268
+  .arrow-area {
269
+    position: relative;
270
+    padding: 0 12px;
271
+    margin-left: auto;
272
+    justify-content: center;
273
+    transform: rotate(-45deg);
274
+    transform-origin: center;
275
+  }
276
+
277
+  .input-arrow {
278
+    width: 8px;
279
+    height: 8px;
280
+    border-left: 2px solid #999;
281
+    border-bottom: 2px solid #999;
282
+  }
283
+
284
+  .uni-data-tree-cover {
285
+    position: fixed;
286
+    left: 0;
287
+    top: 0;
288
+    right: 0;
289
+    bottom: 0;
290
+    background-color: rgba(0, 0, 0, .4);
291
+    flex-direction: column;
292
+    z-index: 100;
293
+  }
294
+
295
+  .uni-data-tree-dialog {
296
+    position: fixed;
297
+    left: 0;
298
+    top: 20%;
299
+    right: 0;
300
+    bottom: 0;
301
+    background-color: #FFFFFF;
302
+    border-top-left-radius: 10px;
303
+    border-top-right-radius: 10px;
304
+    flex-direction: column;
305
+    z-index: 102;
306
+    overflow: hidden;
307
+  }
308
+
309
+  .dialog-caption {
310
+    position: relative;
311
+    flex-direction: row;
312
+  }
313
+
314
+  .dialog-title-view {
315
+    flex: 1;
316
+  }
317
+
318
+  .dialog-title {
319
+    align-self: center;
320
+    padding: 0 10px;
321
+    line-height: 44px;
322
+  }
323
+
324
+  .dialog-close {
325
+    position: absolute;
326
+    top: 0;
327
+    right: 0;
328
+    bottom: 0;
329
+    flex-direction: row;
330
+    align-items: center;
331
+    padding: 0 15px;
332
+  }
333
+
334
+  .dialog-close-plus {
335
+    width: 16px;
336
+    height: 2px;
337
+    background-color: #666;
338
+    border-radius: 2px;
339
+    transform: rotate(45deg);
340
+  }
341
+
342
+  .dialog-close-rotate {
343
+    position: absolute;
344
+    transform: rotate(-45deg);
345
+  }
346
+
347
+  .uni-data-pickerview {
348
+    flex: 1;
349
+  }
350
+
351
+  .icon-clear {
352
+    display: flex;
353
+    align-items: center;
354
+  }
355
+
356
+  /* #ifdef H5 */
357
+  @media all and (min-width: 768px) {
358
+    .uni-data-tree-cover {
359
+      background-color: transparent;
360
+    }
361
+
362
+    .uni-data-tree-dialog {
363
+      position: absolute;
364
+      top: 55px;
365
+      height: auto;
366
+      min-height: 400px;
367
+      max-height: 50vh;
368
+      background-color: #fff;
369
+      border: 1px solid #EBEEF5;
370
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
371
+      border-radius: 4px;
372
+      overflow: unset;
373
+    }
374
+
375
+    .dialog-caption {
376
+      display: none;
377
+    }
378
+  }
379
+  /* #endif */
380
+</style>

+ 575 - 0
src/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue

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

File diff suppressed because it is too large
+ 1 - 0
src/uni_modules/uni-data-picker/components/uni-data-pickerview/loading.uts


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

@@ -0,0 +1,622 @@
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: 500
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
+    isCloudData() {
129
+      return this.collection.length > 0;
130
+    },
131
+    isCloudDataList() {
132
+      return (this.isCloudData && (!this.parentField && !this.selfField));
133
+    },
134
+    isCloudDataTree() {
135
+      return (this.isCloudData && this.parentField && this.selfField);
136
+    },
137
+    dataValue() {
138
+      let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null ||
139
+        this.modelValue !== undefined);
140
+      return isModelValue ? this.modelValue : this.value;
141
+    },
142
+    hasValue() {
143
+      if (typeof this.dataValue === 'number') {
144
+        return true
145
+      }
146
+      return (this.dataValue != null) && (this.dataValue.length > 0)
147
+    }
148
+  },
149
+  created() {
150
+    this.$watch(() => {
151
+      var al = [];
152
+      ['pageCurrent',
153
+        'pageSize',
154
+        'spaceInfo',
155
+        'value',
156
+        'modelValue',
157
+        'localdata',
158
+        'collection',
159
+        'action',
160
+        'field',
161
+        'orderby',
162
+        'where',
163
+        'getont',
164
+        'getcount',
165
+        'gettree'
166
+      ].forEach(key => {
167
+        al.push(this[key])
168
+      });
169
+      return al
170
+    }, (newValue, oldValue) => {
171
+      let needReset = false
172
+      for (let i = 2; i < newValue.length; i++) {
173
+        if (newValue[i] != oldValue[i]) {
174
+          needReset = true
175
+          break
176
+        }
177
+      }
178
+      if (newValue[0] != oldValue[0]) {
179
+        this.page.current = this.pageCurrent
180
+      }
181
+      this.page.size = this.pageSize
182
+
183
+      this.onPropsChange()
184
+    })
185
+    this._treeData = []
186
+  },
187
+  methods: {
188
+    onPropsChange() {
189
+      this._treeData = [];
190
+    },
191
+
192
+    // 填充 pickview 数据
193
+    async loadData() {
194
+      if (this.isLocalData) {
195
+        this.loadLocalData();
196
+      } else if (this.isCloudDataList) {
197
+        this.loadCloudDataList();
198
+      } else if (this.isCloudDataTree) {
199
+        this.loadCloudDataTree();
200
+      }
201
+    },
202
+
203
+    // 加载本地数据
204
+    async loadLocalData() {
205
+      this._treeData = [];
206
+      this._extractTree(this.localdata, this._treeData);
207
+
208
+      let inputValue = this.dataValue;
209
+      if (inputValue === undefined) {
210
+        return;
211
+      }
212
+
213
+      if (Array.isArray(inputValue)) {
214
+        inputValue = inputValue[inputValue.length - 1];
215
+        if (typeof inputValue === 'object' && inputValue[this.map.value]) {
216
+          inputValue = inputValue[this.map.value];
217
+        }
218
+      }
219
+
220
+      this.selected = this._findNodePath(inputValue, this.localdata);
221
+    },
222
+
223
+    // 加载 Cloud 数据 (单列)
224
+    async loadCloudDataList() {
225
+      if (this.loading) {
226
+        return;
227
+      }
228
+      this.loading = true;
229
+
230
+      try {
231
+        let response = await this.getCommand();
232
+        let responseData = response.result.data;
233
+
234
+        this._treeData = responseData;
235
+
236
+        this._updateBindData();
237
+        this._updateSelected();
238
+
239
+        this.onDataChange();
240
+      } catch (e) {
241
+        this.errorMessage = e;
242
+      } finally {
243
+        this.loading = false;
244
+      }
245
+    },
246
+
247
+    // 加载 Cloud 数据 (树形)
248
+    async loadCloudDataTree() {
249
+      if (this.loading) {
250
+        return;
251
+      }
252
+      this.loading = true;
253
+
254
+      try {
255
+        let commandOptions = {
256
+          field: this._cloudDataPostField(),
257
+          where: this._cloudDataTreeWhere()
258
+        };
259
+        if (this.gettree) {
260
+          commandOptions.startwith = `${this.selfField}=='${this.dataValue}'`;
261
+        }
262
+
263
+        let response = await this.getCommand(commandOptions);
264
+        let responseData = response.result.data;
265
+
266
+        this._treeData = responseData;
267
+        this._updateBindData();
268
+        this._updateSelected();
269
+
270
+        this.onDataChange();
271
+      } catch (e) {
272
+        this.errorMessage = e;
273
+      } finally {
274
+        this.loading = false;
275
+      }
276
+    },
277
+
278
+    // 加载 Cloud 数据 (节点)
279
+    async loadCloudDataNode(callback) {
280
+      if (this.loading) {
281
+        return;
282
+      }
283
+      this.loading = true;
284
+
285
+      try {
286
+        let commandOptions = {
287
+          field: this._cloudDataPostField(),
288
+          where: this._cloudDataNodeWhere()
289
+        };
290
+
291
+        let response = await this.getCommand(commandOptions);
292
+        let responseData = response.result.data;
293
+
294
+        callback(responseData);
295
+      } catch (e) {
296
+        this.errorMessage = e;
297
+      } finally {
298
+        this.loading = false;
299
+      }
300
+    },
301
+
302
+    // 回显 Cloud 数据
303
+    getCloudDataValue() {
304
+      if (this.isCloudDataList) {
305
+        return this.getCloudDataListValue();
306
+      }
307
+
308
+      if (this.isCloudDataTree) {
309
+        return this.getCloudDataTreeValue();
310
+      }
311
+    },
312
+
313
+    // 回显 Cloud 数据 (单列)
314
+    getCloudDataListValue() {
315
+      // 根据 field's as value标识匹配 where 条件
316
+      let where = [];
317
+      let whereField = this._getForeignKeyByField();
318
+      if (whereField) {
319
+        where.push(`${whereField} == '${this.dataValue}'`)
320
+      }
321
+
322
+      where = where.join(' || ');
323
+
324
+      if (this.where) {
325
+        where = `(${this.where}) && (${where})`
326
+      }
327
+
328
+      return this.getCommand({
329
+        field: this._cloudDataPostField(),
330
+        where
331
+      }).then((res) => {
332
+        this.selected = res.result.data;
333
+        return res.result.data;
334
+      });
335
+    },
336
+
337
+    // 回显 Cloud 数据 (树形)
338
+    getCloudDataTreeValue() {
339
+      return this.getCommand({
340
+        field: this._cloudDataPostField(),
341
+        getTreePath: {
342
+          startWith: `${this.selfField}=='${this.dataValue}'`
343
+        }
344
+      }).then((res) => {
345
+        let treePath = [];
346
+        this._extractTreePath(res.result.data, treePath);
347
+        this.selected = treePath;
348
+        return treePath;
349
+      });
350
+    },
351
+
352
+    getCommand(options = {}) {
353
+      /* eslint-disable no-undef */
354
+      let db = uniCloud.database(this.spaceInfo)
355
+
356
+      const action = options.action || this.action
357
+      if (action) {
358
+        db = db.action(action)
359
+      }
360
+
361
+      const collection = options.collection || this.collection
362
+      db = db.collection(collection)
363
+
364
+      const where = options.where || this.where
365
+      if (!(!where || !Object.keys(where).length)) {
366
+        db = db.where(where)
367
+      }
368
+
369
+      const field = options.field || this.field
370
+      if (field) {
371
+        db = db.field(field)
372
+      }
373
+
374
+      const orderby = options.orderby || this.orderby
375
+      if (orderby) {
376
+        db = db.orderBy(orderby)
377
+      }
378
+
379
+      const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current
380
+      const size = options.pageSize !== undefined ? options.pageSize : this.page.size
381
+      const getCount = options.getcount !== undefined ? options.getcount : this.getcount
382
+      const getTree = options.gettree !== undefined ? options.gettree : this.gettree
383
+
384
+      const getOptions = {
385
+        getCount,
386
+        getTree
387
+      }
388
+      if (options.getTreePath) {
389
+        getOptions.getTreePath = options.getTreePath
390
+      }
391
+
392
+      db = db.skip(size * (current - 1)).limit(size).get(getOptions)
393
+
394
+      return db
395
+    },
396
+
397
+    _cloudDataPostField() {
398
+      let fields = [this.field];
399
+      if (this.parentField) {
400
+        fields.push(`${this.parentField} as parent_value`);
401
+      }
402
+      return fields.join(',');
403
+    },
404
+
405
+    _cloudDataTreeWhere() {
406
+      let result = []
407
+      let selected = this.selected
408
+      let parentField = this.parentField
409
+      if (parentField) {
410
+        result.push(`${parentField} == null || ${parentField} == ""`)
411
+      }
412
+      if (selected.length) {
413
+        for (var i = 0; i < selected.length - 1; i++) {
414
+          result.push(`${parentField} == '${selected[i].value}'`)
415
+        }
416
+      }
417
+
418
+      let where = []
419
+      if (this.where) {
420
+        where.push(`(${this.where})`)
421
+      }
422
+
423
+      if (result.length) {
424
+        where.push(`(${result.join(' || ')})`)
425
+      }
426
+
427
+      return where.join(' && ')
428
+    },
429
+
430
+    _cloudDataNodeWhere() {
431
+      let where = []
432
+      let selected = this.selected;
433
+      if (selected.length) {
434
+        where.push(`${this.parentField} == '${selected[selected.length - 1].value}'`);
435
+      }
436
+
437
+      where = where.join(' || ');
438
+
439
+      if (this.where) {
440
+        return `(${this.where}) && (${where})`
441
+      }
442
+
443
+      return where
444
+    },
445
+
446
+    _getWhereByForeignKey() {
447
+      let result = []
448
+      let whereField = this._getForeignKeyByField();
449
+      if (whereField) {
450
+        result.push(`${whereField} == '${this.dataValue}'`)
451
+      }
452
+
453
+      if (this.where) {
454
+        return `(${this.where}) && (${result.join(' || ')})`
455
+      }
456
+
457
+      return result.join(' || ')
458
+    },
459
+
460
+    _getForeignKeyByField() {
461
+      let fields = this.field.split(',');
462
+      let whereField = null;
463
+      for (let i = 0; i < fields.length; i++) {
464
+        const items = fields[i].split('as');
465
+        if (items.length < 2) {
466
+          continue;
467
+        }
468
+        if (items[1].trim() === 'value') {
469
+          whereField = items[0].trim();
470
+          break;
471
+        }
472
+      }
473
+      return whereField;
474
+    },
475
+
476
+    _updateBindData(node) {
477
+      const {
478
+        dataList,
479
+        hasNodes
480
+      } = this._filterData(this._treeData, this.selected)
481
+
482
+      let isleaf = this._stepSearh === false && !hasNodes
483
+
484
+      if (node) {
485
+        node.isleaf = isleaf
486
+      }
487
+
488
+      this.dataList = dataList
489
+      this.selectedIndex = dataList.length - 1
490
+
491
+      if (!isleaf && this.selected.length < dataList.length) {
492
+        this.selected.push({
493
+          value: null,
494
+          text: "请选择"
495
+        })
496
+      }
497
+
498
+      return {
499
+        isleaf,
500
+        hasNodes
501
+      }
502
+    },
503
+
504
+    _updateSelected() {
505
+      let dl = this.dataList
506
+      let sl = this.selected
507
+      let textField = this.map.text
508
+      let valueField = this.map.value
509
+      for (let i = 0; i < sl.length; i++) {
510
+        let value = sl[i].value
511
+        let dl2 = dl[i]
512
+        for (let j = 0; j < dl2.length; j++) {
513
+          let item2 = dl2[j]
514
+          if (item2[valueField] === value) {
515
+            sl[i].text = item2[textField]
516
+            break
517
+          }
518
+        }
519
+      }
520
+    },
521
+
522
+    _filterData(data, paths) {
523
+      let dataList = []
524
+      let hasNodes = true
525
+
526
+      dataList.push(data.filter((item) => {
527
+        return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '')
528
+      }))
529
+      for (let i = 0; i < paths.length; i++) {
530
+        let value = paths[i].value
531
+        let nodes = data.filter((item) => {
532
+          return item.parent_value === value
533
+        })
534
+
535
+        if (nodes.length) {
536
+          dataList.push(nodes)
537
+        } else {
538
+          hasNodes = false
539
+        }
540
+      }
541
+
542
+      return {
543
+        dataList,
544
+        hasNodes
545
+      }
546
+    },
547
+
548
+    _extractTree(nodes, result, parent_value) {
549
+      let list = result || []
550
+      let valueField = this.map.value
551
+      for (let i = 0; i < nodes.length; i++) {
552
+        let node = nodes[i]
553
+
554
+        let child = {}
555
+        for (let key in node) {
556
+          if (key !== 'children') {
557
+            child[key] = node[key]
558
+          }
559
+        }
560
+        if (parent_value !== null && parent_value !== undefined && parent_value !== '') {
561
+          child.parent_value = parent_value
562
+        }
563
+        result.push(child)
564
+
565
+        let children = node.children
566
+        if (children) {
567
+          this._extractTree(children, result, node[valueField])
568
+        }
569
+      }
570
+    },
571
+
572
+    _extractTreePath(nodes, result) {
573
+      let list = result || []
574
+      for (let i = 0; i < nodes.length; i++) {
575
+        let node = nodes[i]
576
+
577
+        let child = {}
578
+        for (let key in node) {
579
+          if (key !== 'children') {
580
+            child[key] = node[key]
581
+          }
582
+        }
583
+        result.push(child)
584
+
585
+        let children = node.children
586
+        if (children) {
587
+          this._extractTreePath(children, result)
588
+        }
589
+      }
590
+    },
591
+
592
+    _findNodePath(key, nodes, path = []) {
593
+      let textField = this.map.text
594
+      let valueField = this.map.value
595
+      for (let i = 0; i < nodes.length; i++) {
596
+        let node = nodes[i]
597
+        let children = node.children
598
+        let text = node[textField]
599
+        let value = node[valueField]
600
+
601
+        path.push({
602
+          value,
603
+          text
604
+        })
605
+
606
+        if (value === key) {
607
+          return path
608
+        }
609
+
610
+        if (children) {
611
+          const p = this._findNodePath(key, children, path)
612
+          if (p.length) {
613
+            return p
614
+          }
615
+        }
616
+
617
+        path.pop()
618
+      }
619
+      return []
620
+    }
621
+  }
622
+}

+ 693 - 0
src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts

@@ -0,0 +1,693 @@
1
+export type PaginationType = {
2
+  current : number,
3
+  size : number,
4
+  count : number
5
+}
6
+
7
+export type LoadMoreType = {
8
+  contentdown : string,
9
+  contentrefresh : string,
10
+  contentnomore : string
11
+}
12
+
13
+export type SelectedItemType = {
14
+  name : string,
15
+  value : string,
16
+}
17
+
18
+export type GetCommandOptions = {
19
+  collection ?: UTSJSONObject,
20
+  field ?: string,
21
+  orderby ?: string,
22
+  where ?: any,
23
+  pageData ?: string,
24
+  pageCurrent ?: number,
25
+  pageSize ?: number,
26
+  getCount ?: boolean,
27
+  getTree ?: any,
28
+  getTreePath ?: UTSJSONObject,
29
+  startwith ?: string,
30
+  limitlevel ?: number,
31
+  groupby ?: string,
32
+  groupField ?: string,
33
+  distinct ?: boolean,
34
+  pageIndistinct ?: boolean,
35
+  foreignKey ?: string,
36
+  loadtime ?: string,
37
+  manual ?: boolean
38
+}
39
+
40
+const DefaultSelectedNode = {
41
+  text: '请选择',
42
+  value: ''
43
+}
44
+
45
+export const dataPicker = defineMixin({
46
+  props: {
47
+    localdata: {
48
+      type: Array as PropType<Array<UTSJSONObject>>,
49
+      default: [] as Array<UTSJSONObject>
50
+    },
51
+    collection: {
52
+      type: Object,
53
+      default: ''
54
+    },
55
+    field: {
56
+      type: String,
57
+      default: ''
58
+    },
59
+    orderby: {
60
+      type: String,
61
+      default: ''
62
+    },
63
+    where: {
64
+      type: Object,
65
+      default: ''
66
+    },
67
+    pageData: {
68
+      type: String,
69
+      default: 'add'
70
+    },
71
+    pageCurrent: {
72
+      type: Number,
73
+      default: 1
74
+    },
75
+    pageSize: {
76
+      type: Number,
77
+      default: 20
78
+    },
79
+    getcount: {
80
+      type: Boolean,
81
+      default: false
82
+    },
83
+    gettree: {
84
+      type: Object,
85
+      default: ''
86
+    },
87
+    gettreepath: {
88
+      type: Object,
89
+      default: ''
90
+    },
91
+    startwith: {
92
+      type: String,
93
+      default: ''
94
+    },
95
+    limitlevel: {
96
+      type: Number,
97
+      default: 10
98
+    },
99
+    groupby: {
100
+      type: String,
101
+      default: ''
102
+    },
103
+    groupField: {
104
+      type: String,
105
+      default: ''
106
+    },
107
+    distinct: {
108
+      type: Boolean,
109
+      default: false
110
+    },
111
+    pageIndistinct: {
112
+      type: Boolean,
113
+      default: false
114
+    },
115
+    foreignKey: {
116
+      type: String,
117
+      default: ''
118
+    },
119
+    loadtime: {
120
+      type: String,
121
+      default: 'auto'
122
+    },
123
+    manual: {
124
+      type: Boolean,
125
+      default: false
126
+    },
127
+    preload: {
128
+      type: Boolean,
129
+      default: false
130
+    },
131
+    stepSearh: {
132
+      type: Boolean,
133
+      default: true
134
+    },
135
+    selfField: {
136
+      type: String,
137
+      default: ''
138
+    },
139
+    parentField: {
140
+      type: String,
141
+      default: ''
142
+    },
143
+    multiple: {
144
+      type: Boolean,
145
+      default: false
146
+    },
147
+    value: {
148
+      type: Object,
149
+      default: ''
150
+    },
151
+    modelValue: {
152
+      type: Object,
153
+      default: ''
154
+    },
155
+    defaultProps: {
156
+      type: Object as PropType<UTSJSONObject>,
157
+    }
158
+  },
159
+  data() {
160
+    return {
161
+      loading: false,
162
+      error: null as UniCloudError | null,
163
+      treeData: [] as Array<UTSJSONObject>,
164
+      selectedIndex: 0,
165
+      selectedNodes: [] as Array<UTSJSONObject>,
166
+      selectedPages: [] as Array<UTSJSONObject>[],
167
+      selectedValue: '',
168
+      selectedPaths: [] as Array<UTSJSONObject>,
169
+      pagination: {
170
+        current: 1,
171
+        size: 20,
172
+        count: 0
173
+      } as PaginationType
174
+    }
175
+  },
176
+  computed: {
177
+    mappingTextName() : string {
178
+      // TODO
179
+      return (this.defaultProps != null) ? this.defaultProps!.getString('text', 'text') : 'text'
180
+    },
181
+    mappingValueName() : string {
182
+      // TODO
183
+      return (this.defaultProps != null) ? this.defaultProps!.getString('value', 'value') : 'value'
184
+    },
185
+    currentDataList() : Array<UTSJSONObject> {
186
+      if (this.selectedIndex > this.selectedPages.length - 1) {
187
+        return [] as Array<UTSJSONObject>
188
+      }
189
+      return this.selectedPages[this.selectedIndex]
190
+    },
191
+    isLocalData() : boolean {
192
+      return this.localdata.length > 0
193
+    },
194
+    isCloudData() : boolean {
195
+      return this._checkIsNotNull(this.collection)
196
+    },
197
+    isCloudDataList() : boolean {
198
+      return (this.isCloudData && (this.parentField.length == 0 && this.selfField.length == 0))
199
+    },
200
+    isCloudDataTree() : boolean {
201
+      return (this.isCloudData && this.parentField.length > 0 && this.selfField.length > 0)
202
+    },
203
+    dataValue() : any {
204
+      return this.hasModelValue ? this.modelValue : this.value
205
+    },
206
+    hasCloudTreeData() : boolean {
207
+      return this.treeData.length > 0
208
+    },
209
+    hasModelValue() : boolean {
210
+      if (typeof this.modelValue == 'string') {
211
+        const valueString = this.modelValue as string
212
+        return (valueString.length > 0)
213
+      } else if (Array.isArray(this.modelValue)) {
214
+        const valueArray = this.modelValue as Array<string>
215
+        return (valueArray.length > 0)
216
+      }
217
+      return false
218
+    },
219
+    hasCloudDataValue() : boolean {
220
+      if (typeof this.dataValue == 'string') {
221
+        const valueString = this.dataValue as string
222
+        return (valueString.length > 0)
223
+      }
224
+      return false
225
+    }
226
+  },
227
+  created() {
228
+    this.pagination.current = this.pageCurrent
229
+    this.pagination.size = this.pageSize
230
+
231
+    this.$watch(
232
+      () : any => [
233
+        this.pageCurrent,
234
+        this.pageSize,
235
+        this.localdata,
236
+        this.value,
237
+        this.collection,
238
+        this.field,
239
+        this.getcount,
240
+        this.orderby,
241
+        this.where,
242
+        this.groupby,
243
+        this.groupField,
244
+        this.distinct
245
+      ],
246
+      (newValue : Array<any>, oldValue : Array<any>) => {
247
+        this.pagination.size = this.pageSize
248
+        if (newValue[0] !== oldValue[0]) {
249
+          this.pagination.current = this.pageCurrent
250
+        }
251
+
252
+        this.onPropsChange()
253
+      }
254
+    )
255
+  },
256
+  methods: {
257
+    onPropsChange() {
258
+      this.selectedIndex = 0
259
+      this.treeData.length = 0
260
+      this.selectedNodes.length = 0
261
+      this.selectedPages.length = 0
262
+      this.selectedPaths.length = 0
263
+
264
+      // 加载数据
265
+      this.$nextTick(() => {
266
+        this.loadData()
267
+      })
268
+    },
269
+
270
+    onTabSelect(index : number) {
271
+      this.selectedIndex = index
272
+    },
273
+
274
+    onNodeClick(nodeData : UTSJSONObject) {
275
+      if (nodeData.getBoolean('disable', false)) {
276
+        return
277
+      }
278
+
279
+      const isLeaf = this._checkIsLeafNode(nodeData)
280
+
281
+      this._trimSelectedNodes(nodeData)
282
+
283
+      this.$emit('nodeclick', nodeData)
284
+
285
+      if (this.isLocalData) {
286
+        if (isLeaf || !this._checkHasChildren(nodeData)) {
287
+          this.onFinish()
288
+        }
289
+      } else if (this.isCloudDataList) {
290
+        this.onFinish()
291
+      } else if (this.isCloudDataTree) {
292
+        if (isLeaf) {
293
+          this.onFinish()
294
+        } else if (!this._checkHasChildren(nodeData)) {
295
+          // 尝试请求一次,如果没有返回数据标记为叶子节点
296
+          this.loadCloudDataNode(nodeData)
297
+        }
298
+      }
299
+    },
300
+
301
+    getChangeNodes(): Array<UTSJSONObject> {
302
+      const nodes: Array<UTSJSONObject> = []
303
+      this.selectedNodes.forEach((node : UTSJSONObject) => {
304
+        const newNode: UTSJSONObject = {}
305
+        newNode[this.mappingTextName] = node.getString(this.mappingTextName)
306
+        newNode[this.mappingValueName] = node.getString(this.mappingValueName)
307
+        nodes.push(newNode)
308
+      })
309
+      return nodes
310
+    },
311
+
312
+    onFinish() { },
313
+
314
+    // 加载数据(自动判定环境)
315
+    loadData() {
316
+      if (this.isLocalData) {
317
+        this.loadLocalData()
318
+      } else if (this.isCloudDataList) {
319
+        this.loadCloudDataList()
320
+      } else if (this.isCloudDataTree) {
321
+        this.loadCloudDataTree()
322
+      }
323
+    },
324
+
325
+    // 加载本地数据
326
+    loadLocalData() {
327
+      this.treeData = this.localdata
328
+      if (Array.isArray(this.dataValue)) {
329
+        const value = this.dataValue as Array<UTSJSONObject>
330
+        this.selectedPaths = value.slice(0)
331
+        this._pushSelectedTreeNodes(value, this.localdata)
332
+      } else {
333
+        this._pushSelectedNodes(this.localdata)
334
+      }
335
+    },
336
+
337
+    // 加载 Cloud 数据 (单列)
338
+    loadCloudDataList() {
339
+      this._loadCloudData(null, (data : Array<UTSJSONObject>) => {
340
+        this.treeData = data
341
+        this._pushSelectedNodes(data)
342
+      })
343
+    },
344
+
345
+    // 加载 Cloud 数据 (树形)
346
+    loadCloudDataTree() {
347
+      let commandOptions = {
348
+        field: this._cloudDataPostField(),
349
+        where: this._cloudDataTreeWhere(),
350
+        getTree: true
351
+      } as GetCommandOptions
352
+      if (this._checkIsNotNull(this.gettree)) {
353
+        commandOptions.startwith = `${this.selfField}=='${this.dataValue as string}'`
354
+      }
355
+      this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => {
356
+        this.treeData = data
357
+        if (this.selectedPaths.length > 0) {
358
+          this._pushSelectedTreeNodes(this.selectedPaths, data)
359
+        } else {
360
+          this._pushSelectedNodes(data)
361
+        }
362
+      })
363
+    },
364
+
365
+    // 加载 Cloud 数据 (节点)
366
+    loadCloudDataNode(nodeData : UTSJSONObject) {
367
+      const commandOptions = {
368
+        field: this._cloudDataPostField(),
369
+        where: this._cloudDataNodeWhere()
370
+      } as GetCommandOptions
371
+      this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => {
372
+        nodeData['children'] = data
373
+        if (data.length == 0) {
374
+          nodeData['isleaf'] = true
375
+          this.onFinish()
376
+        } else {
377
+          this._pushSelectedNodes(data)
378
+        }
379
+      })
380
+    },
381
+
382
+    // 回显 Cloud Tree Path
383
+    loadCloudDataPath() {
384
+      if (!this.hasCloudDataValue) {
385
+        return
386
+      }
387
+
388
+      const command : GetCommandOptions = {}
389
+
390
+      // 单列
391
+      if (this.isCloudDataList) {
392
+        // 根据 field's as value标识匹配 where 条件
393
+        let where : Array<string> = [];
394
+        let whereField = this._getForeignKeyByField();
395
+        if (whereField.length > 0) {
396
+          where.push(`${whereField} == '${this.dataValue as string}'`)
397
+        }
398
+
399
+        let whereString = where.join(' || ')
400
+        if (this._checkIsNotNull(this.where)) {
401
+          whereString = `(${this.where}) && (${whereString})`
402
+        }
403
+
404
+        command.field = this._cloudDataPostField()
405
+        command.where = whereString
406
+      }
407
+
408
+      // 树形
409
+      if (this.isCloudDataTree) {
410
+        command.field = this._cloudDataPostField()
411
+        command.getTreePath = {
412
+          startWith: `${this.selfField}=='${this.dataValue as string}'`
413
+        }
414
+      }
415
+
416
+      this._loadCloudData(command, (data : Array<UTSJSONObject>) => {
417
+        this._extractTreePath(data, this.selectedPaths)
418
+      })
419
+    },
420
+
421
+    _loadCloudData(options ?: GetCommandOptions, callback ?: ((data : Array<UTSJSONObject>) => void)) {
422
+      if (this.loading) {
423
+        return
424
+      }
425
+      this.loading = true
426
+
427
+      this.error = null
428
+
429
+      this._getCommand(options).then((response : UniCloudDBGetResult) => {
430
+        callback?.(response.data)
431
+      }).catch((err : any | null) => {
432
+        this.error = err as UniCloudError
433
+      }).finally(() => {
434
+        this.loading = false
435
+      })
436
+    },
437
+
438
+    _cloudDataPostField() : string {
439
+      let fields = [this.field];
440
+      if (this.parentField.length > 0) {
441
+        fields.push(`${this.parentField} as parent_value`)
442
+      }
443
+      return fields.join(',')
444
+    },
445
+
446
+    _cloudDataTreeWhere() : string {
447
+      let result : Array<string> = []
448
+      let selectedNodes = this.selectedNodes.length > 0 ? this.selectedNodes : this.selectedPaths
449
+      let parentField = this.parentField
450
+      if (parentField.length > 0) {
451
+        result.push(`${parentField} == null || ${parentField} == ""`)
452
+      }
453
+      if (selectedNodes.length > 0) {
454
+        for (var i = 0; i < selectedNodes.length - 1; i++) {
455
+          const parentFieldValue = selectedNodes[i].getString('value', '')
456
+          result.push(`${parentField} == '${parentFieldValue}'`)
457
+        }
458
+      }
459
+
460
+      let where : Array<string> = []
461
+      if (this._checkIsNotNull(this.where)) {
462
+        where.push(`(${this.where as string})`)
463
+      }
464
+
465
+      if (result.length > 0) {
466
+        where.push(`(${result.join(' || ')})`)
467
+      }
468
+
469
+      return where.join(' && ')
470
+    },
471
+
472
+    _cloudDataNodeWhere() : string {
473
+      const where : Array<string> = []
474
+      if (this.selectedNodes.length > 0) {
475
+        const value = this.selectedNodes[this.selectedNodes.length - 1].getString('value', '')
476
+        where.push(`${this.parentField} == '${value}'`)
477
+      }
478
+
479
+      let whereString = where.join(' || ')
480
+      if (this._checkIsNotNull(this.where)) {
481
+        return `(${this.where as string}) && (${whereString})`
482
+      }
483
+
484
+      return whereString
485
+    },
486
+
487
+    _getWhereByForeignKey() : string {
488
+      let result : Array<string> = []
489
+      let whereField = this._getForeignKeyByField();
490
+      if (whereField.length > 0) {
491
+        result.push(`${whereField} == '${this.dataValue as string}'`)
492
+      }
493
+
494
+      if (this._checkIsNotNull(this.where)) {
495
+        return `(${this.where}) && (${result.join(' || ')})`
496
+      }
497
+
498
+      return result.join(' || ')
499
+    },
500
+
501
+    _getForeignKeyByField() : string {
502
+      const fields = this.field.split(',')
503
+      let whereField = ''
504
+      for (let i = 0; i < fields.length; i++) {
505
+        const items = fields[i].split('as')
506
+        if (items.length < 2) {
507
+          continue
508
+        }
509
+        if (items[1].trim() === 'value') {
510
+          whereField = items[0].trim()
511
+          break
512
+        }
513
+      }
514
+      return whereField
515
+    },
516
+
517
+    _getCommand(options ?: GetCommandOptions) : Promise<UniCloudDBGetResult> {
518
+      let db = uniCloud.databaseForJQL()
519
+
520
+      let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array<any>)) : db.collection(this.collection)
521
+
522
+      let filter : UniCloudDBFilter | null = null
523
+      if (this.foreignKey.length > 0) {
524
+        filter = collection.foreignKey(this.foreignKey)
525
+      }
526
+
527
+      const where : any = options?.where ?? this.where
528
+      if (typeof where == 'string') {
529
+        const whereString = where as string
530
+        if (whereString.length > 0) {
531
+          filter = (filter != null) ? filter.where(where) : collection.where(where)
532
+        }
533
+      } else {
534
+        filter = (filter != null) ? filter.where(where) : collection.where(where)
535
+      }
536
+
537
+      let query : UniCloudDBQuery | null = null
538
+      if (this.field.length > 0) {
539
+        query = (filter != null) ? filter.field(this.field) : collection.field(this.field)
540
+      }
541
+      if (this.groupby.length > 0) {
542
+        if (query != null) {
543
+          query = query.groupBy(this.groupby)
544
+        } else if (filter != null) {
545
+          query = filter.groupBy(this.groupby)
546
+        }
547
+      }
548
+      if (this.groupField.length > 0) {
549
+        if (query != null) {
550
+          query = query.groupField(this.groupField)
551
+        } else if (filter != null) {
552
+          query = filter.groupField(this.groupField)
553
+        }
554
+      }
555
+      if (this.distinct == true) {
556
+        if (query != null) {
557
+          query = query.distinct(this.field)
558
+        } else if (filter != null) {
559
+          query = filter.distinct(this.field)
560
+        }
561
+      }
562
+      if (this.orderby.length > 0) {
563
+        if (query != null) {
564
+          query = query.orderBy(this.orderby)
565
+        } else if (filter != null) {
566
+          query = filter.orderBy(this.orderby)
567
+        }
568
+      }
569
+
570
+      const size = this.pagination.size
571
+      const current = this.pagination.current
572
+      if (query != null) {
573
+        query = query.skip(size * (current - 1)).limit(size)
574
+      } else if (filter != null) {
575
+        query = filter.skip(size * (current - 1)).limit(size)
576
+      } else {
577
+        query = collection.skip(size * (current - 1)).limit(size)
578
+      }
579
+
580
+      const getOptions = {}
581
+      const treeOptions = {
582
+        limitLevel: this.limitlevel,
583
+        startWith: this.startwith
584
+      }
585
+      if (this.getcount == true) {
586
+        getOptions['getCount'] = this.getcount
587
+      }
588
+
589
+      const getTree : any = options?.getTree ?? this.gettree
590
+      if (typeof getTree == 'string') {
591
+        const getTreeString = getTree as string
592
+        if (getTreeString.length > 0) {
593
+          getOptions['getTree'] = treeOptions
594
+        }
595
+      } else if (typeof getTree == 'object') {
596
+        getOptions['getTree'] = treeOptions
597
+      } else {
598
+        getOptions['getTree'] = getTree
599
+      }
600
+
601
+      const getTreePath = options?.getTreePath ?? this.gettreepath
602
+      if (typeof getTreePath == 'string') {
603
+        const getTreePathString = getTreePath as string
604
+        if (getTreePathString.length > 0) {
605
+          getOptions['getTreePath'] = getTreePath
606
+        }
607
+      } else {
608
+        getOptions['getTreePath'] = getTreePath
609
+      }
610
+
611
+      return query.get(getOptions)
612
+    },
613
+
614
+    _checkIsNotNull(value : any) : boolean {
615
+      if (typeof value == 'string') {
616
+        const valueString = value as string
617
+        return (valueString.length > 0)
618
+      } else if (value instanceof UTSJSONObject) {
619
+        return true
620
+      }
621
+      return false
622
+    },
623
+
624
+    _checkIsLeafNode(nodeData : UTSJSONObject) : boolean {
625
+      if (this.selectedIndex >= this.limitlevel) {
626
+        return true
627
+      }
628
+
629
+      if (nodeData.getBoolean('isleaf', false)) {
630
+        return true
631
+      }
632
+
633
+      return false
634
+    },
635
+
636
+    _checkHasChildren(nodeData : UTSJSONObject) : boolean {
637
+      const children = nodeData.getArray('children') ?? ([] as Array<any>)
638
+      return children.length > 0
639
+    },
640
+
641
+    _pushSelectedNodes(nodes : Array<UTSJSONObject>) {
642
+      this.selectedNodes.push(DefaultSelectedNode)
643
+      this.selectedPages.push(nodes)
644
+      this.selectedIndex = this.selectedPages.length - 1
645
+    },
646
+
647
+    _trimSelectedNodes(nodeData : UTSJSONObject) {
648
+      this.selectedNodes.splice(this.selectedIndex)
649
+      this.selectedNodes.push(nodeData)
650
+
651
+      if (this.selectedPages.length > 0) {
652
+        this.selectedPages.splice(this.selectedIndex + 1)
653
+      }
654
+
655
+      const children = nodeData.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>)
656
+      if (children.length > 0) {
657
+        this.selectedNodes.push(DefaultSelectedNode)
658
+        this.selectedPages.push(children)
659
+      }
660
+
661
+      this.selectedIndex = this.selectedPages.length - 1
662
+    },
663
+
664
+    _pushSelectedTreeNodes(paths : Array<UTSJSONObject>, nodes : Array<UTSJSONObject>) {
665
+      let children : Array<UTSJSONObject> = nodes
666
+      paths.forEach((node : UTSJSONObject) => {
667
+        const findNode = children.find((item : UTSJSONObject) : boolean => {
668
+          return (item.getString(this.mappingValueName) == node.getString(this.mappingValueName))
669
+        })
670
+        if (findNode != null) {
671
+          this.selectedPages.push(children)
672
+          this.selectedNodes.push(node)
673
+          children = findNode.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>)
674
+        }
675
+      })
676
+      this.selectedIndex = this.selectedPages.length - 1
677
+    },
678
+
679
+    _extractTreePath(nodes : Array<UTSJSONObject>, result : Array<UTSJSONObject>) {
680
+      if (nodes.length == 0) {
681
+        return
682
+      }
683
+
684
+      const node = nodes[0]
685
+      result.push(node)
686
+
687
+      const children = node.getArray<UTSJSONObject>('children')
688
+      if (Array.isArray(children) && children!.length > 0) {
689
+        this._extractTreePath(children, result)
690
+      }
691
+    }
692
+  }
693
+})

+ 76 - 0
src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css

@@ -0,0 +1,76 @@
1
+.uni-data-pickerview {
2
+  position: relative;
3
+  flex-direction: column;
4
+  overflow: hidden;
5
+}
6
+
7
+.loading-cover {
8
+  position: absolute;
9
+  left: 0;
10
+  top: 0;
11
+  right: 0;
12
+  bottom: 0;
13
+  align-items: center;
14
+  justify-content: center;
15
+  background-color: rgba(150, 150, 150, .1);
16
+}
17
+
18
+.error {
19
+  background-color: #fff;
20
+  padding: 15px;
21
+}
22
+
23
+.error-text {
24
+  color: #DD524D;
25
+}
26
+
27
+.selected-node-list {
28
+  flex-direction: row;
29
+  flex-wrap: nowrap;
30
+}
31
+
32
+.selected-node-item {
33
+  margin-left: 10px;
34
+  margin-right: 10px;
35
+  padding: 8px 10px 8px 10px;
36
+  border-bottom: 2px solid transparent;
37
+}
38
+
39
+.selected-node-item-active {
40
+  color: #007aff;
41
+  border-bottom-color: #007aff;
42
+}
43
+
44
+.list-view {
45
+  flex: 1;
46
+}
47
+
48
+.list-item {
49
+  flex-direction: row;
50
+  justify-content: space-between;
51
+  padding: 12px 15px;
52
+  border-bottom: 1px solid #f0f0f0;
53
+}
54
+
55
+.item-text {
56
+  color: #333333;
57
+}
58
+
59
+.item-text-disabled {
60
+  opacity: .5;
61
+}
62
+
63
+.item-text-overflow {
64
+  overflow: hidden;
65
+}
66
+
67
+.check {
68
+  margin-right: 5px;
69
+  border: 2px solid #007aff;
70
+  border-left: 0;
71
+  border-top: 0;
72
+  height: 12px;
73
+  width: 6px;
74
+  transform-origin: center;
75
+  transform: rotate(45deg);
76
+}

+ 69 - 0
src/uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue

@@ -0,0 +1,69 @@
1
+<template>
2
+  <view class="uni-data-pickerview">
3
+    <view v-if="error!=null" class="error">
4
+      <text class="error-text">{{error!.errMsg}}</text>
5
+    </view>
6
+    <scroll-view v-if="!isCloudDataList" :scroll-x="true">
7
+      <view class="selected-node-list">
8
+        <template v-for="(item, index) in selectedNodes">
9
+          <text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}"
10
+            @click="onTabSelect(index)">
11
+            {{item[mappingTextName]}}
12
+          </text>
13
+        </template>
14
+      </view>
15
+    </scroll-view>
16
+    <list-view class="list-view" :scroll-y="true">
17
+      <list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)">
18
+        <text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text>
19
+        <text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text>
20
+      </list-item>
21
+    </list-view>
22
+    <view class="loading-cover" v-if="loading">
23
+      <slot name="pickerview-loading" :loading="loading"></slot>
24
+    </view>
25
+  </view>
26
+</template>
27
+
28
+<script>
29
+  import { dataPicker } from "./uni-data-picker.uts"
30
+
31
+  /**
32
+   * DataPickerview
33
+   * @description uni-data-pickerview
34
+   * @tutorial https://ext.dcloud.net.cn/plugin?id=3796
35
+   * @property {Array} localdata 本地数据,参考
36
+   * @property {Boolean} step-searh = [true|false] 是否分布查询
37
+   * @value true 启用分布查询,仅查询当前选中节点
38
+   * @value false 关闭分布查询,一次查询出所有数据
39
+   * @property {String|DBFieldString} self-field 分布查询当前字段名称
40
+   * @property {String|DBFieldString} parent-field 分布查询父字段名称
41
+   * @property {String|DBCollectionString} collection 表名
42
+   * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
43
+   * @property {String} orderby 排序字段及正序倒叙设置
44
+   * @property {String|JQLString} where 查询条件
45
+   */
46
+  export default {
47
+    name: 'UniDataPickerView',
48
+    emits: ['nodeclick', 'change', 'update:modelValue'],
49
+    mixins: [dataPicker],
50
+    props: {
51
+      ellipsis: {
52
+        type: Boolean,
53
+        default: true
54
+      }
55
+    },
56
+    created() {
57
+      this.loadData()
58
+    },
59
+    methods: {
60
+      onFinish() {
61
+        this.$emit('change', this.getChangeNodes())
62
+      }
63
+    }
64
+  }
65
+</script>
66
+
67
+<style>
68
+  @import url("uni-data-pickerview.css");
69
+</style>

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

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

+ 109 - 0
src/uni_modules/uni-data-picker/package.json

@@ -0,0 +1,109 @@
1
+{
2
+  "id": "uni-data-picker",
3
+  "displayName": "uni-data-picker 数据驱动的picker选择器",
4
+  "version": "2.0.2",
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
+    "uni-app": "^3.1.0",
18
+    "uni-app-x": "^3.1.0"
19
+  },
20
+  "directories": {
21
+    "example": "../../temps/example_temps"
22
+  },
23
+  "dcloudext": {
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
+    "type": "component-vue",
42
+    "darkmode": "-",
43
+    "i18n": "-",
44
+    "widescreen": "-"
45
+  },
46
+  "uni_modules": {
47
+    "dependencies": [
48
+      "uni-load-more",
49
+      "uni-icons",
50
+      "uni-scss"
51
+    ],
52
+    "encrypt": [],
53
+    "platforms": {
54
+      "cloud": {
55
+        "tcb": "√",
56
+        "aliyun": "√",
57
+        "alipay": "x"
58
+      },
59
+      "client": {
60
+        "uni-app": {
61
+          "vue": {
62
+            "vue2": "-",
63
+            "vue3": "-"
64
+          },
65
+          "web": {
66
+            "safari": "-",
67
+            "chrome": "-"
68
+          },
69
+          "app": {
70
+            "vue": "-",
71
+            "nvue": "-",
72
+            "android": "-",
73
+            "ios": "-",
74
+            "harmony": "-"
75
+          },
76
+          "mp": {
77
+            "weixin": "-",
78
+            "alipay": "-",
79
+            "toutiao": "-",
80
+            "baidu": "-",
81
+            "kuaishou": "-",
82
+            "jd": "-",
83
+            "harmony": "-",
84
+            "qq": "-",
85
+            "lark": "-"
86
+          },
87
+          "quickapp": {
88
+            "huawei": "-",
89
+            "union": "-"
90
+          }
91
+        },
92
+        "uni-app-x": {
93
+          "web": {
94
+            "safari": "-",
95
+            "chrome": "-"
96
+          },
97
+          "app": {
98
+            "android": "-",
99
+            "ios": "-",
100
+            "harmony": "-"
101
+          },
102
+          "mp": {
103
+            "weixin": "-"
104
+          }
105
+        }
106
+      }
107
+    }
108
+  }
109
+}

+ 22 - 0
src/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

+ 51 - 0
src/uni_modules/uni-data-select/changelog.md

@@ -0,0 +1,51 @@
1
+## 1.1.0(2025-08-19)
2
+- 新增 插槽 selected empty option
3
+- 新增 mutiple 属性,支持多选功能
4
+- 新增 wrap 属性,支持选中的文字超过一行显示
5
+- 新增 align 属性,支持修改选中的文字显示的位置
6
+- 新增 hideRight 属性,支持隐藏右侧所有按钮
7
+- 新增 mode 属性,支持修改边框样式
8
+- 新增 事件 open close clear
9
+## 1.0.10(2025-04-14)
10
+- 修复 清除按钮不展示问题
11
+## 1.0.9(2025-03-26)
12
+- 优化 默认背景为白色与整体组件保持风格统一
13
+## 1.0.8(2024-03-28)
14
+- 修复 在vue2下:style动态绑定导致编译失败的bug
15
+## 1.0.7(2024-01-20)
16
+- 修复 长文本回显超过容器的bug,超过容器部分显示省略号
17
+## 1.0.6(2023-04-12)
18
+- 修复 微信小程序点击时会改变背景颜色的 bug
19
+## 1.0.5(2023-02-03)
20
+- 修复 禁用时会显示清空按钮
21
+## 1.0.4(2023-02-02)
22
+- 优化 查询条件短期内多次变更只查询最后一次变更后的结果
23
+- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue
24
+## 1.0.3(2023-01-16)
25
+- 修复 不关联服务空间报错的问题
26
+## 1.0.2(2023-01-14)
27
+- 新增  属性 `format` 可用于格式化显示选项内容
28
+## 1.0.1(2022-12-06)
29
+- 修复  当where变化时,数据不会自动更新的问题
30
+## 0.1.9(2022-09-05)
31
+- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框
32
+## 0.1.8(2022-08-29)
33
+- 修复 点击的位置不准确
34
+## 0.1.7(2022-08-12)
35
+- 新增 支持 disabled 属性
36
+## 0.1.6(2022-07-06)
37
+- 修复 pc端宽度异常的bug
38
+## 0.1.5
39
+- 修复 pc端宽度异常的bug
40
+## 0.1.4(2022-07-05)
41
+- 优化 显示样式
42
+## 0.1.3(2022-06-02)
43
+- 修复 localdata 赋值不生效的 bug
44
+- 新增 支持  uni.scss 修改颜色
45
+- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用)
46
+## 0.1.2(2022-05-08)
47
+- 修复 当 value 为 0 时选择不生效的 bug
48
+## 0.1.1(2022-05-07)
49
+- 新增 记住上次的选项(仅 collection 存在时有效)
50
+## 0.1.0(2022-04-22)
51
+- 初始化

+ 838 - 0
src/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue

@@ -0,0 +1,838 @@
1
+<template>
2
+	<view class="uni-stat__select">
3
+		<span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span>
4
+		<view class="uni-stat-box" :class="{'uni-stat__actived': current}">
5
+			<view class="uni-select" :class="{'uni-select--disabled':disabled, 'uni-select--wrap': shouldWrap , 'border-default': mode == 'default','border-bottom': mode == 'underline'}">
6
+				<view class="uni-select__input-box" @click="toggleSelector" :class="{'uni-select__input-box--wrap': shouldWrap}">
7
+          <view v-if="slotSelected" class="slot-content padding-top-bottom" :class="{'uni-select__input-text--wrap': shouldWrap}">
8
+            <slot name="selected" :selectedItems="getSelectedItems()"></slot>
9
+          </view>
10
+          <template v-else>
11
+            <view v-if="textShow" class="uni-select__input-text" :class="{'uni-select__input-text--wrap': shouldWrap}">
12
+              <view class="padding-top-bottom" :class="'align-'+align">{{textShow}}</view>
13
+            </view>
14
+            <view v-else class="uni-select__input-text uni-select__input-placeholder" :class="'align-'+align">{{typePlaceholder}}</view>
15
+          </template>
16
+					<view key="clear-button" v-if="!hideRight && shouldShowClear && clear && !disabled" @click.stop="clearVal">
17
+						<uni-icons type="clear" color="#c0c4cc" size="24" />
18
+					</view>
19
+					<view key="arrow-button" v-else-if="!hideRight">
20
+						<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
21
+					</view>
22
+				</view>
23
+				<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
24
+					<view class="uni-select__selector" :style="getOffsetByPlacement" v-if="showSelector">
25
+						<view :class="placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'"></view>
26
+						<scroll-view scroll-y="true" class="uni-select__selector-scroll">
27
+							<template v-if="slotEmpty && mixinDatacomResData.length === 0">
28
+								<view class="uni-select__selector-empty">
29
+									<slot name="empty" :empty="emptyTips"></slot>
30
+								</view>
31
+							</template>
32
+							<template v-else>
33
+								<view v-if="mixinDatacomResData.length === 0" class="uni-select__selector-empty">
34
+									<text>{{emptyTips}}</text>
35
+								</view>
36
+							</template>
37
+							<template v-if="slotOption">
38
+								<view v-for="(itemData,index) in mixinDatacomResData" :key="index" @click="change(itemData)">
39
+									<slot name="option" :item="itemData" :itemSelected="multiple? getCurrentValues().includes(itemData.value):getCurrentValues() == itemData.value"></slot>
40
+								</view>
41
+							</template>
42
+							<template v-else>
43
+								<view v-if="!multiple && mixinDatacomResData.length > 0" class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
44
+									@click="change(item)">
45
+									<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text>
46
+								</view>
47
+								<view v-if="multiple && mixinDatacomResData.length > 0" >
48
+									<checkbox-group @change="checkBoxChange">
49
+										<label class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index" >
50
+											<checkbox :value="index+''" :checked="getCurrentValues().includes(item.value)" :disabled="item.disable"></checkbox>
51
+											<view :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</view>
52
+										</label>
53
+									</checkbox-group>
54
+								</view>
55
+							</template>
56
+						</scroll-view>
57
+					</view>
58
+			</view>
59
+		</view>
60
+	</view>
61
+</template>
62
+
63
+<script>
64
+	/**
65
+	 * DataChecklist 数据选择器
66
+	 * @description 通过数据渲染的下拉框组件
67
+	 * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
68
+	 * @property {String|Array} value 默认值,多选时为数组
69
+	 * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
70
+	 * @property {Boolean} clear 是否可以清空已选项
71
+	 * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
72
+	 * @property {String} label 左侧标题
73
+	 * @property {String} placeholder 输入框的提示文字
74
+	 * @property {Boolean} disabled 是否禁用
75
+	 * @property {Boolean} multiple 是否多选模式
76
+	 * @property {Boolean} wrap 是否允许选中文本换行显示
77
+	 * @property {String} placement 弹出位置
78
+	 * 	@value top   		顶部弹出
79
+	 * 	@value bottom		底部弹出(default)
80
+	 * @property {String} align 选择文字的位置
81
+	 *  @value left 显示左侧
82
+	 *  @value center 显示中间
83
+	 *  @value right 显示 右侧
84
+	 * @property {Boolean} hideRight 是否隐藏右侧按钮
85
+	 * @property {String} mode 边框样式
86
+	 *  @value default 四周边框
87
+	 *  @value underline 下边框
88
+	 *  @value none 无边框
89
+	 * @event {Function} change  选中发生变化触发
90
+	 * @event {Function} open  选择框开启时触发
91
+	 * @event {Function} close  选择框关闭时触发
92
+	 * @event {Function} clear  点击清除按钮之后触发
93
+	 */
94
+
95
+	export default {
96
+		name: "uni-data-select",
97
+		mixins: [uniCloud.mixinDatacom || {}],
98
+		emits: [
99
+			'open',
100
+			'close',
101
+			'update:modelValue',
102
+			'input',
103
+			'clear',
104
+			'change'
105
+		],
106
+		model: {
107
+			prop: 'modelValue',
108
+			event: 'update:modelValue'
109
+		},
110
+		options: {
111
+			// #ifdef MP-TOUTIAO
112
+			virtualHost: false,
113
+			// #endif
114
+			// #ifndef MP-TOUTIAO
115
+			virtualHost: true
116
+			// #endif
117
+		},
118
+		props: {
119
+			localdata: {
120
+				type: Array,
121
+				default () {
122
+					return []
123
+				}
124
+			},
125
+			value: {
126
+				type: [String, Number, Array],
127
+				default: ''
128
+			},
129
+			modelValue: {
130
+				type: [String, Number, Array],
131
+				default: ''
132
+			},
133
+			label: {
134
+				type: String,
135
+				default: ''
136
+			},
137
+			placeholder: {
138
+				type: String,
139
+				default: '请选择'
140
+			},
141
+			emptyTips: {
142
+				type: String,
143
+				default: '无选项'
144
+			},
145
+			clear: {
146
+				type: Boolean,
147
+				default: true
148
+			},
149
+			defItem: {
150
+				type: Number,
151
+				default: 0
152
+			},
153
+			disabled: {
154
+				type: Boolean,
155
+				default: false
156
+			},
157
+			// 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
158
+			format: {
159
+				type: String,
160
+				default: ''
161
+			},
162
+			placement: {
163
+				type: String,
164
+				default: 'bottom'
165
+			},
166
+      multiple: {
167
+				type: Boolean,
168
+				default: false
169
+			},
170
+			wrap: {
171
+				type: Boolean,
172
+				default: false
173
+			},
174
+			align:{
175
+				type: String,
176
+				default: "left"
177
+			},
178
+			hideRight: {
179
+				type: Boolean,
180
+				default: false
181
+			},
182
+      mode:{
183
+        type: String,
184
+        default: 'default'
185
+      }
186
+		},
187
+		data() {
188
+			return {
189
+				showSelector: false,
190
+				current: '',
191
+				mixinDatacomResData: [],
192
+				apps: [],
193
+				channels: [],
194
+				cacheKey: "uni-data-select-lastSelectedValue",
195
+			};
196
+		},
197
+		created() {
198
+			this.debounceGet = this.debounce(() => {
199
+				this.query();
200
+			}, 300);
201
+			if (this.collection && !this.localdata.length) {
202
+				this.debounceGet();
203
+			}
204
+		},
205
+		computed: {
206
+			typePlaceholder() {
207
+				const text = {
208
+					'opendb-stat-app-versions': '版本',
209
+					'opendb-app-channels': '渠道',
210
+					'opendb-app-list': '应用'
211
+				}
212
+				const common = this.placeholder
213
+				const placeholder = text[this.collection]
214
+				return placeholder ?
215
+					common + placeholder :
216
+					common
217
+			},
218
+			valueCom() {
219
+        if (this.value === '') return this.modelValue
220
+				if (this.modelValue === '') return this.value
221
+				return this.value
222
+			},
223
+			textShow() {
224
+				// 长文本显示
225
+				if (this.multiple) {
226
+					const currentValues = this.getCurrentValues();
227
+					if (Array.isArray(currentValues) && currentValues.length > 0) {
228
+						const selectedItems = this.mixinDatacomResData.filter(item => currentValues.includes(item.value));
229
+						return selectedItems.map(item => this.formatItemName(item)).join(', ');
230
+					} else {
231
+						return ''; // 空数组时返回空字符串,显示占位符
232
+					}
233
+				} else {
234
+					return this.current;
235
+				}
236
+			},
237
+			shouldShowClear() {
238
+				if (this.multiple) {
239
+					const currentValues = this.getCurrentValues();
240
+					return Array.isArray(currentValues) && currentValues.length > 0;
241
+				} else {
242
+					return !!this.current;
243
+				}
244
+			},
245
+			shouldWrap() {
246
+				// 只有在多选模式、开启换行、且有内容时才应用换行样式
247
+				return this.multiple && this.wrap && !!this.textShow;
248
+			},
249
+			getOffsetByPlacement() {
250
+				switch (this.placement) {
251
+					case 'top':
252
+						return "bottom:calc(100% + 12px);";
253
+					case 'bottom':
254
+						return "top:calc(100% + 12px);";
255
+				}
256
+			},
257
+      slotSelected(){
258
+        // #ifdef VUE2
259
+        return this.$scopedSlots ? this.$scopedSlots.selected : false
260
+        // #endif
261
+        // #ifdef VUE3
262
+        return this.$slots ? this.$slots.selected : false
263
+        // #endif
264
+      },
265
+      slotEmpty(){
266
+        // #ifdef VUE2
267
+        return this.$scopedSlots ? this.$scopedSlots.empty : false
268
+        // #endif
269
+        // #ifdef VUE3
270
+        return this.$slots ? this.$slots.empty : false
271
+        // #endif
272
+      },
273
+			slotOption(){
274
+				// #ifdef VUE2
275
+				return this.$scopedSlots ? this.$scopedSlots.option : false
276
+				// #endif
277
+				// #ifdef VUE3
278
+				return this.$slots ? this.$slots.option : false
279
+				// #endif
280
+			}
281
+		},
282
+		watch: {
283
+			showSelector:{
284
+				handler(val,old){
285
+					val ? this.$emit('open') : this.$emit('close')
286
+				}
287
+			},
288
+			localdata: {
289
+				immediate: true,
290
+				handler(val, old) {
291
+					if (Array.isArray(val) && old !== val) {
292
+						this.mixinDatacomResData = val
293
+					}
294
+				}
295
+			},
296
+			valueCom(val, old) {
297
+				this.initDefVal()
298
+			},
299
+			mixinDatacomResData: {
300
+				immediate: true,
301
+				handler(val) {
302
+					if (val.length) {
303
+						this.initDefVal()
304
+					}
305
+				}
306
+			},
307
+		},
308
+		methods: {
309
+			getSelectedItems() {
310
+				const currentValues = this.getCurrentValues();
311
+				let _minxData = this.mixinDatacomResData
312
+				// #ifdef MP-WEIXIN || MP-TOUTIAO
313
+				_minxData = JSON.parse(JSON.stringify(this.mixinDatacomResData))
314
+				// #endif
315
+				if (this.multiple) {
316
+					return _minxData.filter(item => currentValues.includes(item.value)) || [];
317
+				} else {
318
+					return _minxData.filter(item => item.value === currentValues) || [];
319
+				}
320
+			},
321
+			debounce(fn, time = 100) {
322
+				let timer = null
323
+				return function(...args) {
324
+					if (timer) clearTimeout(timer)
325
+					timer = setTimeout(() => {
326
+						fn.apply(this, args)
327
+					}, time)
328
+				}
329
+			},
330
+			// 检查项目是否已选中
331
+			isSelected(item) {
332
+				if (this.multiple) {
333
+					const currentValues = this.getCurrentValues();
334
+					return Array.isArray(currentValues) && currentValues.includes(item.value);
335
+				} else {
336
+					return this.getCurrentValues() === item.value;
337
+				}
338
+			},
339
+			// 获取当前选中的值
340
+			getCurrentValues() {
341
+				if (this.multiple) {
342
+					console.log(Array.isArray(this.valueCom) ? this.valueCom : (this.valueCom ? [this.valueCom] : []),111111111)
343
+					return Array.isArray(this.valueCom) ? this.valueCom : (this.valueCom ? [this.valueCom] : []);
344
+				} else {
345
+					return this.valueCom;
346
+				}
347
+			},
348
+			// 执行数据库查询
349
+			query() {
350
+				this.mixinDatacomEasyGet();
351
+			},
352
+			// 监听查询条件变更事件
353
+			onMixinDatacomPropsChange() {
354
+				if (this.collection) {
355
+					this.debounceGet();
356
+				}
357
+			},
358
+			initDefVal() {
359
+				let defValue = this.multiple ? [] : ''
360
+				if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) {
361
+					defValue = this.valueCom
362
+				} else {
363
+					let strogeValue
364
+					if (this.collection) {
365
+						strogeValue = this.getCache()
366
+					}
367
+					if (strogeValue || strogeValue === 0) {
368
+						defValue = strogeValue
369
+					} else {
370
+						let defItem = this.multiple ? [] : ''
371
+						if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) {
372
+							defItem = this.multiple ? [this.mixinDatacomResData[this.defItem - 1].value] : this.mixinDatacomResData[this.defItem - 1].value
373
+						}
374
+						defValue = defItem
375
+					}
376
+					if (defValue || defValue === 0 || (this.multiple && Array.isArray(defValue) && defValue.length > 0)) {
377
+						this.emit(defValue)
378
+					}
379
+				}
380
+
381
+				if (this.multiple) {
382
+					const selectedValues = Array.isArray(defValue) ? defValue : (defValue ? [defValue] : []);
383
+					const selectedItems = this.mixinDatacomResData.filter(item => selectedValues.includes(item.value));
384
+					this.current = selectedItems.map(item => this.formatItemName(item));
385
+				} else {
386
+					const def = this.mixinDatacomResData.find(item => item.value === defValue)
387
+					this.current = def ? this.formatItemName(def) : ''
388
+				}
389
+			},
390
+
391
+			/**
392
+			 * @param {[String, Number, Array]} value
393
+			 * 判断用户给的 value 是否同时为禁用状态
394
+			 */
395
+			isDisabled(value) {
396
+				if (Array.isArray(value)) {
397
+					// 对于数组,如果任意一个值被禁用,则认为整体被禁用
398
+					return value.some(val => {
399
+						return this.mixinDatacomResData.some(item => item.value === val && item.disable);
400
+					});
401
+				} else {
402
+					let isDisabled = false;
403
+					this.mixinDatacomResData.forEach(item => {
404
+						if (item.value === value) {
405
+							isDisabled = item.disable
406
+						}
407
+					})
408
+					return isDisabled;
409
+				}
410
+			},
411
+			clearVal() {
412
+				const emptyValue = this.multiple ? [] : '';
413
+				this.emit(emptyValue)
414
+				this.current = this.multiple ? [] : ''
415
+				if (this.collection) {
416
+					this.removeCache()
417
+				}
418
+				this.$emit('clear')
419
+			},
420
+			checkBoxChange(res){
421
+				let range = res.detail.value
422
+
423
+				let currentValues = range && range.length > 0? range.map((item)=>{
424
+					const index = parseInt(item, 10);
425
+
426
+					if (isNaN(index)) {
427
+						console.error(`无效索引: ${item}`);
428
+					}
429
+
430
+					if (index < 0 || index >= this.mixinDatacomResData.length) {
431
+						console.error(`索引越界: ${index}`);
432
+					}
433
+
434
+					return this.mixinDatacomResData[index].value;
435
+				}) : []
436
+				const selectedItems = this.mixinDatacomResData.filter(dataItem => currentValues.includes(dataItem.value));
437
+				this.current = selectedItems.map(dataItem => this.formatItemName(dataItem));
438
+
439
+				this.emit(currentValues);
440
+			},
441
+			change(item) {
442
+				if (!item.disable) {
443
+					if (this.multiple) {
444
+						// 多选模式
445
+						let currentValues = this.getCurrentValues();
446
+						if (!Array.isArray(currentValues)) {
447
+							currentValues = currentValues ? [currentValues] : [];
448
+						}
449
+
450
+						const itemValue = item.value;
451
+						const index = currentValues.indexOf(itemValue);
452
+
453
+						if (index > -1) {
454
+							currentValues.splice(index, 1);
455
+						} else {
456
+							currentValues.push(itemValue);
457
+						}
458
+
459
+						const selectedItems = this.mixinDatacomResData.filter(dataItem => currentValues.includes(dataItem.value));
460
+						this.current = selectedItems.map(dataItem => this.formatItemName(dataItem));
461
+
462
+						this.emit(currentValues);
463
+					} else {
464
+						// 单选模式
465
+						this.showSelector = false
466
+						this.current = this.formatItemName(item)
467
+						this.emit(item.value)
468
+					}
469
+				}
470
+			},
471
+			emit(val) {
472
+				this.$emit('input', val)
473
+				this.$emit('update:modelValue', val)
474
+				this.$emit('change', val)
475
+				if (this.collection) {
476
+					this.setCache(val);
477
+				}
478
+			},
479
+			toggleSelector() {
480
+				if (this.disabled) {
481
+					return
482
+				}
483
+
484
+				this.showSelector = !this.showSelector
485
+			},
486
+			formatItemName(item) {
487
+				let {
488
+					text,
489
+					value,
490
+					channel_code
491
+				} = item
492
+				channel_code = channel_code ? `(${channel_code})` : ''
493
+
494
+				if (this.format) {
495
+					// 格式化输出
496
+					let str = "";
497
+					str = this.format;
498
+					for (let key in item) {
499
+						str = str.replace(new RegExp(`{${key}}`, "g"), item[key]);
500
+					}
501
+					return str;
502
+				} else {
503
+					return this.collection.indexOf('app-list') > 0 ?
504
+						`${text}(${value})` :
505
+						(
506
+							text ?
507
+							text :
508
+							`未命名${channel_code}`
509
+						)
510
+				}
511
+			},
512
+			// 获取当前加载的数据
513
+			getLoadData() {
514
+				return this.mixinDatacomResData;
515
+			},
516
+			// 获取当前缓存key
517
+			getCurrentCacheKey() {
518
+				return this.collection;
519
+			},
520
+			// 获取缓存
521
+			getCache(name = this.getCurrentCacheKey()) {
522
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
523
+				return cacheData[name];
524
+			},
525
+			// 设置缓存
526
+			setCache(value, name = this.getCurrentCacheKey()) {
527
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
528
+				cacheData[name] = value;
529
+				uni.setStorageSync(this.cacheKey, cacheData);
530
+			},
531
+			// 删除缓存
532
+			removeCache(name = this.getCurrentCacheKey()) {
533
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
534
+				delete cacheData[name];
535
+				uni.setStorageSync(this.cacheKey, cacheData);
536
+			},
537
+		}
538
+	}
539
+</script>
540
+
541
+<style lang="scss">
542
+	$uni-base-color: #6a6a6a !default;
543
+	$uni-main-color: #333 !default;
544
+	$uni-secondary-color: #909399 !default;
545
+	$uni-border-3: #e5e5e5;
546
+  $uni-primary: #2979ff !default;
547
+	$uni-success: #4cd964 !default;
548
+	$uni-warning: #f0ad4e !default;
549
+	$uni-error: #dd524d !default;
550
+	$uni-info: #909399 !default;
551
+
552
+	/* #ifndef APP-NVUE */
553
+	@media screen and (max-width: 500px) {
554
+		.hide-on-phone {
555
+			display: none;
556
+		}
557
+	}
558
+
559
+	/* #endif */
560
+	.uni-stat__select {
561
+		display: flex;
562
+		align-items: center;
563
+		// padding: 15px;
564
+		/* #ifdef H5 */
565
+		cursor: pointer;
566
+		/* #endif */
567
+		width: 100%;
568
+		flex: 1;
569
+		box-sizing: border-box;
570
+	}
571
+
572
+	.uni-stat-box {
573
+		background-color: #fff;
574
+		width: 100%;
575
+		flex: 1;
576
+	}
577
+
578
+	.uni-stat__actived {
579
+		width: 100%;
580
+		flex: 1;
581
+		// outline: 1px solid #2979ff;
582
+	}
583
+
584
+	.uni-label-text {
585
+		font-size: 14px;
586
+		font-weight: bold;
587
+		color: $uni-base-color;
588
+		margin: auto 0;
589
+		margin-right: 5px;
590
+	}
591
+
592
+  .border-bottom {
593
+    border-bottom: solid 1px $uni-border-3;
594
+  }
595
+
596
+  .border-default {
597
+    border: 1px solid $uni-border-3;
598
+  }
599
+
600
+	.uni-select {
601
+		font-size: 14px;
602
+		box-sizing: border-box;
603
+		border-radius: 4px;
604
+		padding: 0 5px;
605
+		padding-left: 10px;
606
+		position: relative;
607
+		/* #ifndef APP-NVUE */
608
+		display: flex;
609
+		user-select: none;
610
+		/* #endif */
611
+		flex-direction: row;
612
+		align-items: center;
613
+		width: 100%;
614
+		flex: 1;
615
+		min-height: 35px;
616
+
617
+		&--disabled {
618
+			background-color: #f5f7fa;
619
+			cursor: not-allowed;
620
+		}
621
+
622
+		&--wrap {
623
+			height: auto;
624
+			min-height: 35px;
625
+			// align-items: flex-start;
626
+		}
627
+	}
628
+
629
+	.uni-select__label {
630
+		font-size: 16px;
631
+		// line-height: 22px;
632
+		height: 35px;
633
+		padding-right: 10px;
634
+		color: $uni-secondary-color;
635
+	}
636
+
637
+	.uni-select__input-box {
638
+		// height: 35px;
639
+		width: 0px;
640
+		position: relative;
641
+		/* #ifndef APP-NVUE */
642
+		display: flex;
643
+		/* #endif */
644
+		flex: 1;
645
+		flex-direction: row;
646
+		align-items: center;
647
+
648
+		&--wrap {
649
+			.uni-select__input-text {
650
+				margin-right: 8px;
651
+			}
652
+		}
653
+
654
+    .padding-top-bottom {
655
+      padding-top: 5px;
656
+      padding-bottom: 5px;
657
+    }
658
+
659
+    .slot-content {
660
+      width: 100%;
661
+      display: flex;
662
+      flex-direction: row;
663
+			flex-wrap: wrap;
664
+    }
665
+	}
666
+
667
+	.uni-select__input {
668
+		flex: 1;
669
+		font-size: 14px;
670
+		height: 22px;
671
+		line-height: 22px;
672
+	}
673
+
674
+	.uni-select__input-plac {
675
+		font-size: 14px;
676
+		color: $uni-secondary-color;
677
+	}
678
+
679
+	.uni-select__selector {
680
+		/* #ifndef APP-NVUE */
681
+		box-sizing: border-box;
682
+		/* #endif */
683
+		position: absolute;
684
+		left: 0;
685
+		width: 100%;
686
+		background-color: #FFFFFF;
687
+		border: 1px solid #EBEEF5;
688
+		border-radius: 6px;
689
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
690
+		z-index: 3;
691
+		padding: 4px 0;
692
+	}
693
+
694
+	.uni-select__selector-scroll {
695
+		/* #ifndef APP-NVUE */
696
+		max-height: 200px;
697
+		box-sizing: border-box;
698
+		/* #endif */
699
+	}
700
+
701
+	/* #ifdef H5 */
702
+	@media (min-width: 768px) {
703
+		.uni-select__selector-scroll {
704
+			max-height: 600px;
705
+		}
706
+	}
707
+
708
+	/* #endif */
709
+
710
+	.uni-select__selector-empty,
711
+	.uni-select__selector-item {
712
+		/* #ifndef APP-NVUE */
713
+		display: flex;
714
+		cursor: pointer;
715
+		/* #endif */
716
+		flex-direction: row;
717
+		align-items: center;
718
+		line-height: 35px;
719
+		font-size: 14px;
720
+		/* border-bottom: solid 1px $uni-border-3; */
721
+		padding: 0px 10px;
722
+	}
723
+
724
+
725
+
726
+	.uni-select__selector-item-check {
727
+		margin-left: auto;
728
+	}
729
+
730
+	.uni-select__selector-empty:last-child,
731
+	.uni-select__selector-item:last-child {
732
+		/* #ifndef APP-NVUE */
733
+		border-bottom: none;
734
+		/* #endif */
735
+	}
736
+
737
+	.uni-select__selector__disabled {
738
+		opacity: 0.4;
739
+		cursor: default;
740
+	}
741
+
742
+	/* picker 弹出层通用的指示小三角 */
743
+	.uni-popper__arrow_bottom,
744
+	.uni-popper__arrow_bottom::after,
745
+	.uni-popper__arrow_top,
746
+	.uni-popper__arrow_top::after {
747
+		position: absolute;
748
+		display: block;
749
+		width: 0;
750
+		height: 0;
751
+		border-color: transparent;
752
+		border-style: solid;
753
+		border-width: 6px;
754
+	}
755
+
756
+	.uni-popper__arrow_bottom {
757
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
758
+		top: -6px;
759
+		left: 10%;
760
+		margin-right: 3px;
761
+		border-top-width: 0;
762
+		border-bottom-color: #EBEEF5;
763
+	}
764
+
765
+	.uni-popper__arrow_bottom::after {
766
+		content: " ";
767
+		top: 1px;
768
+		margin-left: -6px;
769
+		border-top-width: 0;
770
+		border-bottom-color: #fff;
771
+	}
772
+
773
+	.uni-popper__arrow_top {
774
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
775
+		bottom: -6px;
776
+		left: 10%;
777
+		margin-right: 3px;
778
+		border-bottom-width: 0;
779
+		border-top-color: #EBEEF5;
780
+	}
781
+
782
+	.uni-popper__arrow_top::after {
783
+		content: " ";
784
+		bottom: 1px;
785
+		margin-left: -6px;
786
+		border-bottom-width: 0;
787
+		border-top-color: #fff;
788
+	}
789
+
790
+
791
+	.uni-select__input-text {
792
+		// width: 280px;
793
+		width: 100%;
794
+		color: $uni-main-color;
795
+		white-space: nowrap;
796
+		text-overflow: ellipsis;
797
+		-o-text-overflow: ellipsis;
798
+		overflow: hidden;
799
+
800
+		&--wrap {
801
+			white-space: normal;
802
+			text-overflow: initial;
803
+			-o-text-overflow: initial;
804
+			overflow: visible;
805
+			word-wrap: break-word;
806
+			word-break: break-all;
807
+			// line-height: 1.5;
808
+		}
809
+	}
810
+
811
+	.uni-select__input-placeholder {
812
+		color: $uni-base-color;
813
+		font-size: 12px;
814
+    margin: 1px 0;
815
+	}
816
+
817
+	.uni-select--mask {
818
+		position: fixed;
819
+		top: 0;
820
+		bottom: 0;
821
+		right: 0;
822
+		left: 0;
823
+		z-index: 2;
824
+	}
825
+
826
+  .align-left {
827
+    text-align: left;
828
+  }
829
+
830
+  .align-center {
831
+    text-align: center;
832
+  }
833
+
834
+  .align-right {
835
+    text-align: right;
836
+  }
837
+
838
+</style>

+ 106 - 0
src/uni_modules/uni-data-select/package.json

@@ -0,0 +1,106 @@
1
+{
2
+  "id": "uni-data-select",
3
+  "displayName": "uni-data-select 下拉框选择器",
4
+  "version": "1.1.0",
5
+  "description": "通过数据驱动的下拉框选择器",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "select",
9
+    "uni-data-select",
10
+    "下拉框",
11
+    "下拉选"
12
+],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": "^3.1.1",
16
+    "uni-app": "^4.45",
17
+    "uni-app-x": ""
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "x",
42
+    "i18n": "x",
43
+    "widescreen": "x"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-load-more"
48
+    ],
49
+    "encrypt": [],
50
+    "platforms": {
51
+      "cloud": {
52
+        "tcb": "√",
53
+        "aliyun": "√",
54
+        "alipay": "√"
55
+      },
56
+      "client": {
57
+        "uni-app": {
58
+          "vue": {
59
+            "vue2": "√",
60
+            "vue3": "√"
61
+          },
62
+          "web": {
63
+            "safari": "√",
64
+            "chrome": "√"
65
+          },
66
+          "app": {
67
+            "vue": "√",
68
+            "nvue": "-",
69
+            "android": "√",
70
+            "ios": "√",
71
+            "harmony": "√"
72
+          },
73
+          "mp": {
74
+            "weixin": "√",
75
+            "alipay": "√",
76
+            "toutiao": "√",
77
+            "baidu": "-",
78
+            "kuaishou": "-",
79
+            "jd": "-",
80
+            "harmony": "-",
81
+            "qq": "-",
82
+            "lark": "-"
83
+          },
84
+          "quickapp": {
85
+            "huawei": "-",
86
+            "union": "-"
87
+          }
88
+        },
89
+        "uni-app-x": {
90
+          "web": {
91
+            "safari": "-",
92
+            "chrome": "-"
93
+          },
94
+          "app": {
95
+            "android": "-",
96
+            "ios": "-",
97
+            "harmony": "-"
98
+          },
99
+          "mp": {
100
+            "weixin": "-"
101
+          }
102
+        }
103
+      }
104
+    }
105
+  }
106
+}

+ 8 - 0
src/uni_modules/uni-data-select/readme.md

@@ -0,0 +1,8 @@
1
+## DataSelect 下拉框选择器
2
+> **组件名:uni-data-select**
3
+> 代码块: `uDataSelect`
4
+
5
+当选项过多时,使用下拉菜单展示并选择内容
6
+
7
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select)
8
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 10 - 0
src/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
src/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
src/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>

+ 106 - 0
src/uni_modules/uni-dateformat/package.json

@@ -0,0 +1,106 @@
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
+    "uni-app": "^3.1.0",
18
+    "uni-app-x": "^3.1.0"
19
+  },
20
+  "directories": {
21
+    "example": "../../temps/example_temps"
22
+  },
23
+  "dcloudext": {
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
+    "type": "component-vue",
42
+    "darkmode": "-",
43
+    "i18n": "-",
44
+    "widescreen": "-"
45
+  },
46
+  "uni_modules": {
47
+    "dependencies": [
48
+      "uni-scss"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "√",
54
+        "aliyun": "√"
55
+      },
56
+      "client": {
57
+        "uni-app": {
58
+          "vue": {
59
+            "vue2": "-",
60
+            "vue3": "-"
61
+          },
62
+          "web": {
63
+            "safari": "-",
64
+            "chrome": "-"
65
+          },
66
+          "app": {
67
+            "vue": "-",
68
+            "nvue": "-",
69
+            "android": "-",
70
+            "ios": "-",
71
+            "harmony": "-"
72
+          },
73
+          "mp": {
74
+            "weixin": "-",
75
+            "alipay": "-",
76
+            "toutiao": "-",
77
+            "baidu": "-",
78
+            "kuaishou": "-",
79
+            "jd": "-",
80
+            "harmony": "-",
81
+            "qq": "-",
82
+            "lark": "-"
83
+          },
84
+          "quickapp": {
85
+            "huawei": "-",
86
+            "union": "-"
87
+          }
88
+        },
89
+        "uni-app-x": {
90
+          "web": {
91
+            "safari": "-",
92
+            "chrome": "-"
93
+          },
94
+          "app": {
95
+            "android": "-",
96
+            "ios": "-",
97
+            "harmony": "-"
98
+          },
99
+          "mp": {
100
+            "weixin": "-"
101
+          }
102
+        }
103
+      }
104
+    }
105
+  }
106
+}

+ 11 - 0
src/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 

+ 173 - 0
src/uni_modules/uni-datetime-picker/changelog.md

@@ -0,0 +1,173 @@
1
+## 2.2.42(2025-08-20)
2
+- 修复 datetime-picker 小程序样式警告
3
+## 2.2.41(2025-08-20)
4
+- 修复 uni-datetime-picker组件时间对比报错问题
5
+## 2.2.40(2025-04-14)
6
+- 修复 绑定字符串值的时,日历面板选中状态未重置到默认值的问题
7
+## 2.2.39(2025-04-14)
8
+- 修复 在 iOS 微信小程序上type='daterange'时,传入'YYYY-MM-DD'格式不生效的问题
9
+
10
+## 2.2.38(2024-10-15)
11
+- 修复 微信小程序中的getSystemInfo警告
12
+## 2.2.35(2024-09-21)
13
+- 修复 没有选中日期时点击确定直接报错的Bug [详情](https://ask.dcloud.net.cn/question/198168)
14
+## 2.2.34(2024-04-24)
15
+- 新增 日期点击事件,在点击日期时会触发该事件。
16
+## 2.2.33(2024-04-15)
17
+- 修复 抖音小程序事件传递失效bug
18
+## 2.2.32(2024-02-20)
19
+- 修复 日历的close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844)
20
+## 2.2.31(2024-02-20)
21
+- 修复 h5平台 右边日历的月份默认+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841)
22
+## 2.2.30(2024-01-31)
23
+- 修复 隐藏“秒”时,在IOS15及以下版本时出现 结束时间在开始时间之前 的bug [详情](https://github.com/dcloudio/uni-ui/issues/788)
24
+## 2.2.29(2024-01-20)
25
+- 新增 show事件,弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694)
26
+## 2.2.28(2024-01-18)
27
+- 去除 noChange事件,当进行日期范围选择时,若只选了一天,则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815)
28
+## 2.2.27(2024-01-10)
29
+- 优化 增加noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815)
30
+## 2.2.26(2024-01-08)
31
+- 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834)
32
+## 2.2.25(2023-10-18)
33
+- 修复 PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
34
+## 2.2.24(2023-06-02)
35
+- 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
36
+- 优化 当前月可以选择上月、下月的日期的Bug
37
+## 2.2.23(2023-05-02)
38
+- 修复 部分情况修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
39
+- 修复 部分平台及设备第一次点击无法显示弹框的Bug
40
+- 修复 ios 日期格式未补零显示及使用异常的Bug [详情](https://ask.dcloud.net.cn/question/162979)
41
+## 2.2.22(2023-03-30)
42
+- 修复 日历 picker 修改年月后,自动选中当月1日的Bug [详情](https://ask.dcloud.net.cn/question/165937)
43
+- 修复 小程序端 低版本 ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979)
44
+## 2.2.21(2023-02-20)
45
+- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
46
+## 2.2.20(2023-02-17)
47
+- 优化 值为空依然选中当天问题
48
+- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间
49
+- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间
50
+- 优化 字节小程序日期时间范围选择,底部日期换行的Bug
51
+## 2.2.19(2023-02-09)
52
+- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
53
+## 2.2.18(2023-02-08)
54
+- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
55
+- 优化 PC端输入日期格式错误时返回当前日期时间
56
+- 优化 PC端输入日期时间超出 start、end 限制的Bug
57
+- 优化 移动端日期时间范围用法时间展示不完整问题
58
+## 2.2.17(2023-02-04)
59
+- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
60
+- 修复 vue3 time-picker 无法显示绑定时分秒的Bug
61
+## 2.2.16(2023-02-02)
62
+- 修复 字节小程序报错的Bug
63
+## 2.2.15(2023-02-02)
64
+- 修复 某些情况切换月份错误的Bug
65
+## 2.2.14(2023-01-30)
66
+- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
67
+## 2.2.13(2023-01-10)
68
+- 修复 多次加载组件造成内存占用的Bug
69
+## 2.2.12(2022-12-01)
70
+- 修复 vue3 下 i18n 国际化初始值不正确的Bug
71
+## 2.2.11(2022-09-19)
72
+- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
73
+## 2.2.10(2022-09-19)
74
+- 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
75
+## 2.2.9(2022-09-16)
76
+- 可以使用 uni-scss 控制主题色
77
+## 2.2.8(2022-09-08)
78
+- 修复 close事件无效的Bug
79
+## 2.2.7(2022-09-05)
80
+- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824)
81
+## 2.2.6(2022-06-30)
82
+- 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致
83
+## 2.2.5(2022-06-24)
84
+- 修复 日历顶部年月及底部确认未国际化的Bug
85
+## 2.2.4(2022-03-31)
86
+- 修复 Vue3 下动态赋值,单选类型未响应的Bug
87
+## 2.2.3(2022-03-28)
88
+- 修复 Vue3 下动态赋值未响应的Bug
89
+## 2.2.2(2021-12-10)
90
+- 修复 clear-icon 属性在小程序平台不生效的Bug
91
+## 2.2.1(2021-12-10)
92
+- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug
93
+## 2.2.0(2021-11-19)
94
+- 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource)
95
+- 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
96
+## 2.1.5(2021-11-09)
97
+- 新增 提供组件设计资源,组件样式调整
98
+## 2.1.4(2021-09-10)
99
+- 修复 hide-second 在移动端的Bug
100
+- 修复 单选赋默认值时,赋值日期未高亮的Bug
101
+- 修复 赋默认值时,移动端未正确显示时间的Bug
102
+## 2.1.3(2021-09-09)
103
+- 新增 hide-second 属性,支持只使用时分,隐藏秒
104
+## 2.1.2(2021-09-03)
105
+- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
106
+- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
107
+- 优化 调整字号大小,美化日历界面
108
+- 修复 因国际化导致的 placeholder 失效的Bug
109
+## 2.1.1(2021-08-24)
110
+- 新增 支持国际化
111
+- 优化 范围选择器在 pc 端过宽的问题
112
+## 2.1.0(2021-08-09)
113
+- 新增 适配 vue3
114
+## 2.0.19(2021-08-09)
115
+- 新增 支持作为 uni-forms 子组件相关功能
116
+- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug
117
+## 2.0.18(2021-08-05)
118
+- 修复 type 属性动态赋值无效的Bug
119
+- 修复 ‘确认’按钮被 tabbar 遮盖 bug
120
+- 修复 组件未赋值时范围选左、右日历相同的Bug
121
+## 2.0.17(2021-08-04)
122
+- 修复 范围选未正确显示当前值的Bug
123
+- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug
124
+## 2.0.16(2021-07-21)
125
+- 新增 return-type 属性支持返回 date 日期对象
126
+## 2.0.15(2021-07-14)
127
+- 修复 单选日期类型,初始赋值后不在当前日历的Bug
128
+- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
129
+- 优化 移动端移除显示框的清空按钮,无实际用途
130
+## 2.0.14(2021-07-14)
131
+- 修复 组件赋值为空,界面未更新的Bug
132
+- 修复 start 和 end 不能动态赋值的Bug
133
+- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug
134
+## 2.0.13(2021-07-08)
135
+- 修复 范围选择不能动态赋值的Bug
136
+## 2.0.12(2021-07-08)
137
+- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
138
+## 2.0.11(2021-07-08)
139
+- 优化 弹出层在超出视窗边缘定位不准确的问题
140
+## 2.0.10(2021-07-08)
141
+- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug
142
+- 优化 弹出层在超出视窗边缘被遮盖的问题
143
+## 2.0.9(2021-07-07)
144
+- 新增 maskClick 事件
145
+- 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px
146
+- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
147
+## 2.0.8(2021-07-07)
148
+- 新增 日期时间显示框支持插槽
149
+## 2.0.7(2021-07-01)
150
+- 优化 添加 uni-icons 依赖
151
+## 2.0.6(2021-05-22)
152
+- 修复 图标在小程序上不显示的Bug
153
+- 优化 重命名引用组件,避免潜在组件命名冲突
154
+## 2.0.5(2021-05-20)
155
+- 优化 代码目录扁平化
156
+## 2.0.4(2021-05-12)
157
+- 新增 组件示例地址
158
+## 2.0.3(2021-05-10)
159
+- 修复 ios 下不识别 '-' 日期格式的Bug
160
+- 优化 pc 下弹出层添加边框和阴影
161
+## 2.0.2(2021-05-08)
162
+- 修复 在 admin 中获取弹出层定位错误的bug
163
+## 2.0.1(2021-05-08)
164
+- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
165
+## 2.0.0(2021-04-30)
166
+- 支持日历形式的日期+时间的范围选择
167
+ > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
168
+## 1.0.6(2021-03-18)
169
+- 新增 hide-second 属性,时间支持仅选择时、分
170
+- 修复 选择跟显示的日期不一样的Bug
171
+- 修复 chang事件触发2次的Bug
172
+- 修复 分、秒 end 范围错误的Bug
173
+- 优化 更好的 nvue 适配

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

@@ -0,0 +1,177 @@
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--today': weeks.isToday}"></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
+			checkHover: {
45
+				type: Boolean,
46
+				default: false
47
+			}
48
+		},
49
+		methods: {
50
+			choiceDate(weeks) {
51
+				this.$emit('change', weeks)
52
+			},
53
+			handleMousemove(weeks) {
54
+				this.$emit('handleMouse', weeks)
55
+			}
56
+		}
57
+	}
58
+</script>
59
+
60
+<style lang="scss" >
61
+	$uni-primary: #007aff !default;
62
+
63
+	.uni-calendar-item__weeks-box {
64
+		flex: 1;
65
+		/* #ifndef APP-NVUE */
66
+		display: flex;
67
+		/* #endif */
68
+		flex-direction: column;
69
+		justify-content: center;
70
+		align-items: center;
71
+		margin: 1px 0;
72
+		position: relative;
73
+	}
74
+
75
+	.uni-calendar-item__weeks-box-text {
76
+		font-size: 14px;
77
+		// font-family: Lato-Bold, Lato;
78
+		font-weight: bold;
79
+		color: darken($color: $uni-primary, $amount: 40%);
80
+	}
81
+
82
+	.uni-calendar-item__weeks-box-item {
83
+		position: relative;
84
+		/* #ifndef APP-NVUE */
85
+		display: flex;
86
+		/* #endif */
87
+		flex-direction: column;
88
+		justify-content: center;
89
+		align-items: center;
90
+		width: 40px;
91
+		height: 40px;
92
+		/* #ifdef H5 */
93
+		cursor: pointer;
94
+		/* #endif */
95
+	}
96
+
97
+
98
+	.uni-calendar-item__weeks-box-circle {
99
+		position: absolute;
100
+		top: 5px;
101
+		right: 5px;
102
+		width: 8px;
103
+		height: 8px;
104
+		border-radius: 8px;
105
+		background-color: #dd524d;
106
+
107
+	}
108
+
109
+	.uni-calendar-item__weeks-box .uni-calendar-item--disable {
110
+		cursor: default;
111
+	}
112
+
113
+	.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
114
+		color: #D1D1D1;
115
+	}
116
+
117
+	.uni-calendar-item--today {
118
+		position: absolute;
119
+		top: 10px;
120
+		right: 17%;
121
+		background-color: #dd524d;
122
+		width:6px;
123
+		height: 6px;
124
+		border-radius: 50%;
125
+	}
126
+
127
+	.uni-calendar-item--extra {
128
+		color: #dd524d;
129
+		opacity: 0.8;
130
+	}
131
+
132
+	.uni-calendar-item__weeks-box .uni-calendar-item--checked {
133
+		background-color: $uni-primary;
134
+		border-radius: 50%;
135
+		box-sizing: border-box;
136
+		border: 3px solid #fff;
137
+	}
138
+
139
+	.uni-calendar-item--checked .uni-calendar-item--checked-text {
140
+		color: #fff;
141
+	}
142
+
143
+	.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
144
+		color: #333;
145
+	}
146
+
147
+	.uni-calendar-item--multiple {
148
+		background-color:  #F6F7FC;
149
+		// color: #fff;
150
+	}
151
+
152
+	.uni-calendar-item--multiple .uni-calendar-item--before-checked,
153
+	.uni-calendar-item--multiple .uni-calendar-item--after-checked {
154
+		background-color: $uni-primary;
155
+		border-radius: 50%;
156
+		box-sizing: border-box;
157
+		border: 3px solid #F6F7FC;
158
+	}
159
+
160
+	.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
161
+	.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
162
+		color: #fff;
163
+	}
164
+
165
+	.uni-calendar-item--before-checked-x {
166
+		border-top-left-radius: 50px;
167
+		border-bottom-left-radius: 50px;
168
+		box-sizing: border-box;
169
+		background-color: #F6F7FC;
170
+	}
171
+
172
+	.uni-calendar-item--after-checked-x {
173
+		border-top-right-radius: 50px;
174
+		border-bottom-right-radius: 50px;
175
+		background-color: #F6F7FC;
176
+	}
177
+</style>

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

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

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

@@ -0,0 +1,22 @@
1
+{
2
+	"uni-datetime-picker.selectDate": "select date",
3
+	"uni-datetime-picker.selectTime": "select time",
4
+	"uni-datetime-picker.selectDateTime": "select date and time",
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-datetime-picker.year": "-",
13
+	"uni-datetime-picker.month": "",
14
+	"uni-calender.MON": "MON",
15
+	"uni-calender.TUE": "TUE",
16
+	"uni-calender.WED": "WED",
17
+	"uni-calender.THU": "THU",
18
+	"uni-calender.FRI": "FRI",
19
+	"uni-calender.SAT": "SAT",
20
+	"uni-calender.SUN": "SUN",
21
+	"uni-calender.confirm": "confirm"
22
+}

+ 8 - 0
src/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
+}

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

@@ -0,0 +1,22 @@
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-datetime-picker.year": "年",
13
+	"uni-datetime-picker.month": "月",
14
+	"uni-calender.SUN": "日",
15
+	"uni-calender.MON": "一",
16
+	"uni-calender.TUE": "二",
17
+	"uni-calender.WED": "三",
18
+	"uni-calender.THU": "四",
19
+	"uni-calender.FRI": "五",
20
+	"uni-calender.SAT": "六",
21
+	"uni-calender.confirm": "确认"
22
+}

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

@@ -0,0 +1,22 @@
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-datetime-picker.year": "年",
13
+  "uni-datetime-picker.month": "月",
14
+  "uni-calender.SUN": "日",
15
+  "uni-calender.MON": "一",
16
+  "uni-calender.TUE": "二",
17
+  "uni-calender.WED": "三",
18
+  "uni-calender.THU": "四",
19
+  "uni-calender.FRI": "五",
20
+  "uni-calender.SAT": "六",
21
+  "uni-calender.confirm": "確認"
22
+}

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

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

File diff suppressed because it is too large
+ 1074 - 0
src/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue


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

@@ -0,0 +1,421 @@
1
+class Calendar {
2
+	constructor({
3
+		selected,
4
+		startDate,
5
+		endDate,
6
+		range,
7
+	} = {}) {
8
+		// 当前日期
9
+		this.date = this.getDateObj(new Date()) // 当前初入日期
10
+		// 打点信息
11
+		this.selected = selected || [];
12
+		// 起始时间
13
+		this.startDate = startDate
14
+		// 终止时间
15
+		this.endDate = endDate
16
+		// 是否范围选择
17
+		this.range = range
18
+		// 多选状态
19
+		this.cleanMultipleStatus()
20
+		// 每周日期
21
+		this.weeks = {}
22
+		this.lastHover = false
23
+	}
24
+	/**
25
+	 * 设置日期
26
+	 * @param {Object} date
27
+	 */
28
+	setDate(date) {
29
+		const selectDate = this.getDateObj(date)
30
+		this.getWeeks(selectDate.fullDate)
31
+	}
32
+
33
+	/**
34
+	 * 清理多选状态
35
+	 */
36
+	cleanMultipleStatus() {
37
+		this.multipleStatus = {
38
+			before: '',
39
+			after: '',
40
+			data: []
41
+		}
42
+	}
43
+
44
+	setStartDate(startDate) {
45
+		this.startDate = startDate
46
+	}
47
+
48
+	setEndDate(endDate) {
49
+		this.endDate = endDate
50
+	}
51
+
52
+	getPreMonthObj(date) {
53
+		date = fixIosDateFormat(date)
54
+		date = new Date(date)
55
+
56
+		const oldMonth = date.getMonth()
57
+		date.setMonth(oldMonth - 1)
58
+		const newMonth = date.getMonth()
59
+		if (oldMonth !== 0 && newMonth - oldMonth === 0) {
60
+			date.setMonth(newMonth - 1)
61
+		}
62
+		return this.getDateObj(date)
63
+	}
64
+	getNextMonthObj(date) {
65
+		date = fixIosDateFormat(date)
66
+		date = new Date(date)
67
+
68
+		const oldMonth = date.getMonth()
69
+		date.setMonth(oldMonth + 1)
70
+		const newMonth = date.getMonth()
71
+		if (newMonth - oldMonth > 1) {
72
+			date.setMonth(newMonth - 1)
73
+		}
74
+		return this.getDateObj(date)
75
+	}
76
+
77
+	/**
78
+	 * 获取指定格式Date对象
79
+	 */
80
+	getDateObj(date) {
81
+		date = fixIosDateFormat(date)
82
+		date = new Date(date)
83
+
84
+		return {
85
+			fullDate: getDate(date),
86
+			year: date.getFullYear(),
87
+			month: addZero(date.getMonth() + 1),
88
+			date: addZero(date.getDate()),
89
+			day: date.getDay()
90
+		}
91
+	}
92
+
93
+	/**
94
+	 * 获取上一个月日期集合
95
+	 */
96
+	getPreMonthDays(amount, dateObj) {
97
+		const result = []
98
+		for (let i = amount - 1; i >= 0; i--) {
99
+			const month = dateObj.month - 1
100
+			result.push({
101
+				date: new Date(dateObj.year, month, -i).getDate(),
102
+				month,
103
+				disable: true
104
+			})
105
+		}
106
+		return result
107
+	}
108
+	/**
109
+	 * 获取本月日期集合
110
+	 */
111
+	getCurrentMonthDays(amount, dateObj) {
112
+		const result = []
113
+		const fullDate = this.date.fullDate
114
+		for (let i = 1; i <= amount; i++) {
115
+			const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
116
+			const isToday = fullDate === currentDate
117
+			// 获取打点信息
118
+			const info = this.selected && this.selected.find((item) => {
119
+				if (this.dateEqual(currentDate, item.date)) {
120
+					return item
121
+				}
122
+			})
123
+
124
+			// 日期禁用
125
+			let disableBefore = true
126
+			let disableAfter = true
127
+			if (this.startDate) {
128
+				disableBefore = dateCompare(this.startDate, currentDate)
129
+			}
130
+
131
+			if (this.endDate) {
132
+				disableAfter = dateCompare(currentDate, this.endDate)
133
+			}
134
+
135
+			let multiples = this.multipleStatus.data
136
+			let multiplesStatus = -1
137
+			if (this.range && multiples) {
138
+				multiplesStatus = multiples.findIndex((item) => {
139
+					return this.dateEqual(item, currentDate)
140
+				})
141
+			}
142
+			const checked = multiplesStatus !== -1
143
+
144
+			result.push({
145
+				fullDate: currentDate,
146
+				year: dateObj.year,
147
+				date: i,
148
+				multiple: this.range ? checked : false,
149
+				beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
150
+				afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
151
+				month: dateObj.month,
152
+				disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(
153
+					currentDate, this.endDate)),
154
+				isToday,
155
+				userChecked: false,
156
+				extraInfo: info
157
+			})
158
+		}
159
+		return result
160
+	}
161
+	/**
162
+	 * 获取下一个月日期集合
163
+	 */
164
+	_getNextMonthDays(amount, dateObj) {
165
+		const result = []
166
+		const month = dateObj.month + 1
167
+		for (let i = 1; i <= amount; i++) {
168
+			result.push({
169
+				date: i,
170
+				month,
171
+				disable: true
172
+			})
173
+		}
174
+		return result
175
+	}
176
+
177
+	/**
178
+	 * 获取当前日期详情
179
+	 * @param {Object} date
180
+	 */
181
+	getInfo(date) {
182
+		if (!date) {
183
+			date = new Date()
184
+		}
185
+		const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
186
+		return res ? res : this.getDateObj(date)
187
+	}
188
+
189
+	/**
190
+	 * 比较时间是否相等
191
+	 */
192
+	dateEqual(before, after) {
193
+		before = new Date(fixIosDateFormat(before))
194
+		after = new Date(fixIosDateFormat(after))
195
+		return before.valueOf() === after.valueOf()
196
+	}
197
+
198
+	/**
199
+	 *  比较真实起始日期
200
+	 */
201
+
202
+	isLogicBefore(currentDate, before, after) {
203
+		let logicBefore = before
204
+		if (before && after) {
205
+			logicBefore = dateCompare(before, after) ? before : after
206
+		}
207
+		return this.dateEqual(logicBefore, currentDate)
208
+	}
209
+
210
+	isLogicAfter(currentDate, before, after) {
211
+		let logicAfter = after
212
+		if (before && after) {
213
+			logicAfter = dateCompare(before, after) ? after : before
214
+		}
215
+		return this.dateEqual(logicAfter, currentDate)
216
+	}
217
+
218
+	/**
219
+	 * 获取日期范围内所有日期
220
+	 * @param {Object} begin
221
+	 * @param {Object} end
222
+	 */
223
+	geDateAll(begin, end) {
224
+		var arr = []
225
+		var ab = begin.split('-')
226
+		var ae = end.split('-')
227
+		var db = new Date()
228
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
229
+		var de = new Date()
230
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
231
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
232
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
233
+		for (var k = unixDb; k <= unixDe;) {
234
+			k = k + 24 * 60 * 60 * 1000
235
+			arr.push(this.getDateObj(new Date(parseInt(k))).fullDate)
236
+		}
237
+		return arr
238
+	}
239
+
240
+	/**
241
+	 *  获取多选状态
242
+	 */
243
+	setMultiple(fullDate) {
244
+		if (!this.range) return
245
+
246
+		let {
247
+			before,
248
+			after
249
+		} = this.multipleStatus
250
+		if (before && after) {
251
+			if (!this.lastHover) {
252
+				this.lastHover = true
253
+				return
254
+			}
255
+			this.multipleStatus.before = fullDate
256
+			this.multipleStatus.after = ''
257
+			this.multipleStatus.data = []
258
+			this.multipleStatus.fulldate = ''
259
+			this.lastHover = false
260
+		} else {
261
+			if (!before) {
262
+				this.multipleStatus.before = fullDate
263
+				this.multipleStatus.after = undefined;
264
+				this.lastHover = false
265
+			} else {
266
+				this.multipleStatus.after = fullDate
267
+				if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
268
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
269
+						.after);
270
+				} else {
271
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
272
+						.before);
273
+				}
274
+				this.lastHover = true
275
+			}
276
+		}
277
+		this.getWeeks(fullDate)
278
+	}
279
+
280
+	/**
281
+	 *  鼠标 hover 更新多选状态
282
+	 */
283
+	setHoverMultiple(fullDate) {
284
+		//抖音小程序点击会触发hover事件,需要避免一下
285
+		// #ifndef MP-TOUTIAO
286
+		if (!this.range || this.lastHover) return
287
+		const {
288
+			before
289
+		} = this.multipleStatus
290
+
291
+		if (!before) {
292
+			this.multipleStatus.before = fullDate
293
+		} else {
294
+			this.multipleStatus.after = fullDate
295
+			if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
296
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
297
+			} else {
298
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
299
+			}
300
+		}
301
+		this.getWeeks(fullDate)
302
+		// #endif
303
+
304
+	}
305
+
306
+	/**
307
+	 * 更新默认值多选状态
308
+	 */
309
+	setDefaultMultiple(before, after) {
310
+		this.multipleStatus.before = before
311
+		this.multipleStatus.after = after
312
+		if (before && after) {
313
+			if (dateCompare(before, after)) {
314
+				this.multipleStatus.data = this.geDateAll(before, after);
315
+				this.getWeeks(after)
316
+			} else {
317
+				this.multipleStatus.data = this.geDateAll(after, before);
318
+				this.getWeeks(before)
319
+			}
320
+		}
321
+	}
322
+
323
+	/**
324
+	 * 获取每周数据
325
+	 * @param {Object} dateData
326
+	 */
327
+	getWeeks(dateData) {
328
+		const {
329
+			year,
330
+			month,
331
+		} = this.getDateObj(dateData)
332
+
333
+		const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
334
+		const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
335
+
336
+		const currentMonthDayAmount = new Date(year, month, 0).getDate()
337
+		const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
338
+
339
+		const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
340
+		const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
341
+
342
+		const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
343
+
344
+		const weeks = new Array(6)
345
+		for (let i = 0; i < calendarDays.length; i++) {
346
+			const index = Math.floor(i / 7)
347
+			if (!weeks[index]) {
348
+				weeks[index] = new Array(7)
349
+			}
350
+			weeks[index][i % 7] = calendarDays[i]
351
+		}
352
+
353
+		this.calendar = calendarDays
354
+		this.weeks = weeks
355
+	}
356
+}
357
+
358
+function getDateTime(date, hideSecond) {
359
+	return `${getDate(date)} ${getTime(date, hideSecond)}`
360
+}
361
+
362
+function getDate(date) {
363
+	date = fixIosDateFormat(date)
364
+	date = new Date(date)
365
+	const year = date.getFullYear()
366
+	const month = date.getMonth() + 1
367
+	const day = date.getDate()
368
+	return `${year}-${addZero(month)}-${addZero(day)}`
369
+}
370
+
371
+function getTime(date, hideSecond) {
372
+	date = fixIosDateFormat(date)
373
+	date = new Date(date)
374
+	const hour = date.getHours()
375
+	const minute = date.getMinutes()
376
+	const second = date.getSeconds()
377
+	return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
378
+}
379
+
380
+function addZero(num) {
381
+	if (num < 10) {
382
+		num = `0${num}`
383
+	}
384
+	return num
385
+}
386
+
387
+function getDefaultSecond(hideSecond) {
388
+	return hideSecond ? '00:00' : '00:00:00'
389
+}
390
+
391
+function dateCompare(startDate, endDate) {
392
+	startDate = new Date(fixIosDateFormat(typeof startDate === 'string' ? startDate.trim() : startDate))
393
+	endDate = new Date(fixIosDateFormat(typeof endDate === 'string' ? endDate.trim() : endDate))
394
+	return startDate <= endDate
395
+}
396
+
397
+function checkDate(date) {
398
+	const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
399
+	return date.match(dateReg)
400
+}
401
+//ios低版本15及以下,无法匹配 没有 ’秒‘ 时的情况,所以需要在末尾 秒 加上 问号
402
+const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/;
403
+
404
+function fixIosDateFormat(value) {
405
+	if (typeof value === 'string' && dateTimeReg.test(value)) {
406
+		value = value.replace(/-/g, '/')
407
+	}
408
+	return value
409
+}
410
+
411
+export {
412
+	Calendar,
413
+	getDateTime,
414
+	getDate,
415
+	getTime,
416
+	addZero,
417
+	getDefaultSecond,
418
+	dateCompare,
419
+	checkDate,
420
+	fixIosDateFormat
421
+}

+ 107 - 0
src/uni_modules/uni-datetime-picker/package.json

@@ -0,0 +1,107 @@
1
+{
2
+  "id": "uni-datetime-picker",
3
+  "displayName": "uni-datetime-picker 日期选择器",
4
+  "version": "2.2.42",
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
+    "uni-app": "^4.07",
17
+    "uni-app-x": ""
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "x",
42
+    "i18n": "x",
43
+    "widescreen": "x"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-scss",
48
+      "uni-icons"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "x",
54
+        "aliyun": "x",
55
+        "alipay": "x"
56
+      },
57
+      "client": {
58
+        "uni-app": {
59
+          "vue": {
60
+            "vue2": "√",
61
+            "vue3": "√"
62
+          },
63
+          "web": {
64
+            "safari": "√",
65
+            "chrome": "√"
66
+          },
67
+          "app": {
68
+            "vue": "√",
69
+            "nvue": "√",
70
+            "android": "√",
71
+            "ios": "√",
72
+            "harmony": "√"
73
+          },
74
+          "mp": {
75
+            "weixin": "√",
76
+            "alipay": "√",
77
+            "toutiao": "√",
78
+            "baidu": "√",
79
+            "kuaishou": "-",
80
+            "jd": "-",
81
+            "harmony": "-",
82
+            "qq": "√",
83
+            "lark": "-"
84
+          },
85
+          "quickapp": {
86
+            "huawei": "√",
87
+            "union": "√"
88
+          }
89
+        },
90
+        "uni-app-x": {
91
+          "web": {
92
+            "safari": "-",
93
+            "chrome": "-"
94
+          },
95
+          "app": {
96
+            "android": "-",
97
+            "ios": "-",
98
+            "harmony": "-"
99
+          },
100
+          "mp": {
101
+            "weixin": "-"
102
+          }
103
+        }
104
+      }
105
+    }
106
+  }
107
+}

+ 21 - 0
src/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
src/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
src/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
src/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" scoped>
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>

+ 105 - 0
src/uni_modules/uni-drawer/package.json

@@ -0,0 +1,105 @@
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
+    "uni-app": "^3.1.0",
17
+    "uni-app-x": "^3.1.0"
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "-",
42
+    "i18n": "-",
43
+    "widescreen": "-"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-scss"
48
+    ],
49
+    "encrypt": [],
50
+    "platforms": {
51
+      "cloud": {
52
+        "tcb": "√",
53
+        "aliyun": "√"
54
+      },
55
+      "client": {
56
+        "uni-app": {
57
+          "vue": {
58
+            "vue2": "-",
59
+            "vue3": "-"
60
+          },
61
+          "web": {
62
+            "safari": "-",
63
+            "chrome": "-"
64
+          },
65
+          "app": {
66
+            "vue": "-",
67
+            "nvue": "-",
68
+            "android": "-",
69
+            "ios": "-",
70
+            "harmony": "-"
71
+          },
72
+          "mp": {
73
+            "weixin": "-",
74
+            "alipay": "-",
75
+            "toutiao": "-",
76
+            "baidu": "-",
77
+            "kuaishou": "-",
78
+            "jd": "-",
79
+            "harmony": "-",
80
+            "qq": "-",
81
+            "lark": "-"
82
+          },
83
+          "quickapp": {
84
+            "huawei": "-",
85
+            "union": "-"
86
+          }
87
+        },
88
+        "uni-app-x": {
89
+          "web": {
90
+            "safari": "-",
91
+            "chrome": "-"
92
+          },
93
+          "app": {
94
+            "android": "-",
95
+            "ios": "-",
96
+            "harmony": "-"
97
+          },
98
+          "mp": {
99
+            "weixin": "-"
100
+          }
101
+        }
102
+      }
103
+    }
104
+  }
105
+}

+ 10 - 0
src/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 

+ 119 - 0
src/uni_modules/uni-easyinput/changelog.md

@@ -0,0 +1,119 @@
1
+## 1.1.21(2025-08-26)
2
+- 修复 在 @input 中修改 v-model 不生效的问题
3
+## 1.1.20(2025-08-19)
4
+- 修复 微信小程序平台样式警告问题
5
+## 1.1.19(2024-07-18)
6
+- 修复 初始值传入 null 导致input报错的bug
7
+## 1.1.18(2024-04-11)
8
+- 修复 easyinput组件双向绑定问题
9
+## 1.1.17(2024-03-28)
10
+- 修复 在头条小程序下丢失事件绑定的问题
11
+## 1.1.16(2024-03-20)
12
+- 修复 在密码输入情况下 清除和小眼睛覆盖bug 在edge浏览器下显示双眼睛bug
13
+## 1.1.15(2024-02-21)
14
+- 新增 左侧插槽:left
15
+## 1.1.14(2024-02-19)
16
+- 修复 onBlur的emit传值错误
17
+## 1.1.12(2024-01-29)
18
+- 补充 adjust-position文档属性补充
19
+## 1.1.11(2024-01-29)
20
+- 补充 adjust-position属性传递值:(Boolean)当键盘弹起时,是否自动上推页面
21
+## 1.1.10(2024-01-22)
22
+- 去除 移除无用的log输出
23
+## 1.1.9(2023-04-11)
24
+- 修复 vue3 下 keyboardheightchange 事件报错的bug
25
+## 1.1.8(2023-03-29)
26
+- 优化 trim 属性默认值
27
+## 1.1.7(2023-03-29)
28
+- 新增 cursor-spacing 属性
29
+## 1.1.6(2023-01-28)
30
+- 新增 keyboardheightchange 事件,可监听键盘高度变化
31
+## 1.1.5(2022-11-29)
32
+- 优化 主题样式
33
+## 1.1.4(2022-10-27)
34
+- 修复 props 中背景颜色无默认值的bug
35
+## 1.1.0(2022-06-30)
36
+
37
+- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容
38
+- 新增 clear 事件,点击右侧叉号图标触发
39
+- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发
40
+- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等
41
+
42
+## 1.0.5(2022-06-07)
43
+
44
+- 优化 clearable 显示策略
45
+
46
+## 1.0.4(2022-06-07)
47
+
48
+- 优化 clearable 显示策略
49
+
50
+## 1.0.3(2022-05-20)
51
+
52
+- 修复 关闭图标某些情况下无法取消的 bug
53
+
54
+## 1.0.2(2022-04-12)
55
+
56
+- 修复 默认值不生效的 bug
57
+
58
+## 1.0.1(2022-04-02)
59
+
60
+- 修复 value 不能为 0 的 bug
61
+
62
+## 1.0.0(2021-11-19)
63
+
64
+- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
65
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
66
+
67
+## 0.1.4(2021-08-20)
68
+
69
+- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
70
+
71
+## 0.1.3(2021-08-11)
72
+
73
+- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
74
+
75
+## 0.1.2(2021-07-30)
76
+
77
+- 优化 vue3 下事件警告的问题
78
+
79
+## 0.1.1
80
+
81
+- 优化 errorMessage 属性支持 Boolean 类型
82
+
83
+## 0.1.0(2021-07-13)
84
+
85
+- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
86
+
87
+## 0.0.16(2021-06-29)
88
+
89
+- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
90
+
91
+## 0.0.15(2021-06-21)
92
+
93
+- 修复 passwordIcon 属性拼写错误的 bug
94
+
95
+## 0.0.14(2021-06-18)
96
+
97
+- 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标
98
+- 修复 confirmType 属性不生效的问题
99
+
100
+## 0.0.13(2021-06-04)
101
+
102
+- 修复 disabled 状态可清出内容的 bug
103
+
104
+## 0.0.12(2021-05-12)
105
+
106
+- 新增 组件示例地址
107
+
108
+## 0.0.11(2021-05-07)
109
+
110
+- 修复 input-border 属性不生效的问题
111
+
112
+## 0.0.10(2021-04-30)
113
+
114
+- 修复 ios 遮挡文字、显示一半的问题
115
+
116
+## 0.0.9(2021-02-05)
117
+
118
+- 调整为 uni_modules 目录规范
119
+- 优化 兼容 nvue 页面

+ 54 - 0
src/uni_modules/uni-easyinput/components/uni-easyinput/common.js

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

File diff suppressed because it is too large
+ 674 - 0
src/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue


+ 107 - 0
src/uni_modules/uni-easyinput/package.json

@@ -0,0 +1,107 @@
1
+{
2
+  "id": "uni-easyinput",
3
+  "displayName": "uni-easyinput 增强输入框",
4
+  "version": "1.1.21",
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
+    "uni-app": "^4.07",
17
+    "uni-app-x": ""
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "x",
42
+    "i18n": "x",
43
+    "widescreen": "x"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-scss",
48
+      "uni-icons"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "x",
54
+        "aliyun": "x",
55
+        "alipay": "x"
56
+      },
57
+      "client": {
58
+        "uni-app": {
59
+          "vue": {
60
+            "vue2": "√",
61
+            "vue3": "√"
62
+          },
63
+          "web": {
64
+            "safari": "√",
65
+            "chrome": "√"
66
+          },
67
+          "app": {
68
+            "vue": "√",
69
+            "nvue": "-",
70
+            "android": "√",
71
+            "ios": "√",
72
+            "harmony": "√"
73
+          },
74
+          "mp": {
75
+            "weixin": "√",
76
+            "alipay": "√",
77
+            "toutiao": "√",
78
+            "baidu": "√",
79
+            "kuaishou": "√",
80
+            "jd": "√",
81
+            "harmony": "√",
82
+            "qq": "√",
83
+            "lark": "-"
84
+          },
85
+          "quickapp": {
86
+            "huawei": "-",
87
+            "union": "-"
88
+          }
89
+        },
90
+        "uni-app-x": {
91
+          "web": {
92
+            "safari": "-",
93
+            "chrome": "-"
94
+          },
95
+          "app": {
96
+            "android": "-",
97
+            "ios": "-",
98
+            "harmony": "-"
99
+          },
100
+          "mp": {
101
+            "weixin": "-"
102
+          }
103
+        }
104
+      }
105
+    }
106
+  }
107
+}

+ 11 - 0
src/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 

+ 25 - 0
src/uni_modules/uni-fab/changelog.md

@@ -0,0 +1,25 @@
1
+## 1.2.6(2024-10-12)
2
+- 修复 微信小程序中的getSystemInfo警告
3
+## 1.2.5(2023-03-29)
4
+- 新增 pattern.icon 属性,可自定义图标
5
+## 1.2.4(2022-09-07)
6
+小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false)
7
+## 1.2.3(2022-09-05)
8
+- 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310)
9
+## 1.2.2(2021-12-29)
10
+- 更新 组件依赖
11
+## 1.2.1(2021-11-19)
12
+- 修复 阴影颜色不正确的bug
13
+## 1.2.0(2021-11-19)
14
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
15
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab)
16
+## 1.1.1(2021-11-09) 
17
+- 新增 提供组件设计资源,组件样式调整
18
+## 1.1.0(2021-07-30)
19
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
20
+## 1.0.7(2021-05-12)
21
+- 新增 组件示例地址
22
+## 1.0.6(2021-02-05)
23
+- 调整为uni_modules目录规范
24
+- 优化 按钮背景色调整
25
+- 优化 兼容pc端

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

@@ -0,0 +1,491 @@
1
+<template>
2
+	<view class="uni-cursor-point">
3
+		<view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop) && content.length > 0" :class="{
4
+        'uni-fab--leftBottom': leftBottom,
5
+        'uni-fab--rightBottom': rightBottom,
6
+        'uni-fab--leftTop': leftTop,
7
+        'uni-fab--rightTop': rightTop
8
+      }" class="uni-fab"
9
+				:style="nvueBottom"
10
+			>
11
+			<view :class="{
12
+          'uni-fab__content--left': horizontal === 'left',
13
+          'uni-fab__content--right': horizontal === 'right',
14
+          'uni-fab__content--flexDirection': direction === 'vertical',
15
+          'uni-fab__content--flexDirectionStart': flexDirectionStart,
16
+          'uni-fab__content--flexDirectionEnd': flexDirectionEnd,
17
+		  'uni-fab__content--other-platform': !isAndroidNvue
18
+        }" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }"
19
+				class="uni-fab__content" elevation="5">
20
+				<view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" />
21
+				<view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }"
22
+					class="uni-fab__item" @click="_onItemClick(index, item)">
23
+					<image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image"
24
+						mode="aspectFit" />
25
+					<text class="uni-fab__item-text"
26
+						:style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text>
27
+				</view>
28
+				<view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" />
29
+			</view>
30
+		</view>
31
+		<view :class="{
32
+		  'uni-fab__circle--leftBottom': leftBottom,
33
+		  'uni-fab__circle--rightBottom': rightBottom,
34
+		  'uni-fab__circle--leftTop': leftTop,
35
+		  'uni-fab__circle--rightTop': rightTop,
36
+		  'uni-fab__content--other-platform': !isAndroidNvue
37
+		}" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor, 'bottom': nvueBottom }" @click="_onClick">
38
+			<uni-icons class="fab-circle-icon" :type="styles.icon" :color="styles.iconColor" size="32"
39
+				:class="{'uni-fab__plus--active': isShow && content.length > 0}"></uni-icons>
40
+			<!-- <view class="fab-circle-v"  :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view>
41
+			<view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow  && content.length > 0}"></view> -->
42
+		</view>
43
+	</view>
44
+</template>
45
+
46
+<script>
47
+	let platform = 'other'
48
+	// #ifdef APP-NVUE
49
+	platform = uni.getSystemInfoSync().platform
50
+	// #endif
51
+
52
+	/**
53
+	 * Fab 悬浮按钮
54
+	 * @description 点击可展开一个图形按钮菜单
55
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=144
56
+	 * @property {Object} pattern 可选样式配置项
57
+	 * @property {Object} horizontal = [left | right] 水平对齐方式
58
+	 * 	@value left 左对齐
59
+	 * 	@value right 右对齐
60
+	 * @property {Object} vertical = [bottom | top] 垂直对齐方式
61
+	 * 	@value bottom 下对齐
62
+	 * 	@value top 上对齐
63
+	 * @property {Object} direction = [horizontal | vertical] 展开菜单显示方式
64
+	 * 	@value horizontal 水平显示
65
+	 * 	@value vertical 垂直显示
66
+	 * @property {Array} content 展开菜单内容配置项
67
+	 * @property {Boolean} popMenu 是否使用弹出菜单
68
+	 * @event {Function} trigger 展开菜单点击事件,返回点击信息
69
+	 * @event {Function} fabClick 悬浮按钮点击事件
70
+	 */
71
+	export default {
72
+		name: 'UniFab',
73
+		emits: ['fabClick', 'trigger'],
74
+		props: {
75
+			pattern: {
76
+				type: Object,
77
+				default () {
78
+					return {}
79
+				}
80
+			},
81
+			horizontal: {
82
+				type: String,
83
+				default: 'left'
84
+			},
85
+			vertical: {
86
+				type: String,
87
+				default: 'bottom'
88
+			},
89
+			direction: {
90
+				type: String,
91
+				default: 'horizontal'
92
+			},
93
+			content: {
94
+				type: Array,
95
+				default () {
96
+					return []
97
+				}
98
+			},
99
+			show: {
100
+				type: Boolean,
101
+				default: false
102
+			},
103
+			popMenu: {
104
+				type: Boolean,
105
+				default: true
106
+			}
107
+		},
108
+		data() {
109
+			return {
110
+				fabShow: false,
111
+				isShow: false,
112
+				isAndroidNvue: platform === 'android',
113
+				styles: {
114
+					color: '#3c3e49',
115
+					selectedColor: '#007AFF',
116
+					backgroundColor: '#fff',
117
+					buttonColor: '#007AFF',
118
+					iconColor: '#fff',
119
+					icon: 'plusempty'
120
+				}
121
+			}
122
+		},
123
+		computed: {
124
+			contentWidth(e) {
125
+				return (this.content.length + 1) * 55 + 15 + 'px'
126
+			},
127
+			contentWidthMin() {
128
+				return '55px'
129
+			},
130
+			// 动态计算宽度
131
+			boxWidth() {
132
+				return this.getPosition(3, 'horizontal')
133
+			},
134
+			// 动态计算高度
135
+			boxHeight() {
136
+				return this.getPosition(3, 'vertical')
137
+			},
138
+			// 计算左下位置
139
+			leftBottom() {
140
+				return this.getPosition(0, 'left', 'bottom')
141
+			},
142
+			// 计算右下位置
143
+			rightBottom() {
144
+				return this.getPosition(0, 'right', 'bottom')
145
+			},
146
+			// 计算左上位置
147
+			leftTop() {
148
+				return this.getPosition(0, 'left', 'top')
149
+			},
150
+			rightTop() {
151
+				return this.getPosition(0, 'right', 'top')
152
+			},
153
+			flexDirectionStart() {
154
+				return this.getPosition(1, 'vertical', 'top')
155
+			},
156
+			flexDirectionEnd() {
157
+				return this.getPosition(1, 'vertical', 'bottom')
158
+			},
159
+			horizontalLeft() {
160
+				return this.getPosition(2, 'horizontal', 'left')
161
+			},
162
+			horizontalRight() {
163
+				return this.getPosition(2, 'horizontal', 'right')
164
+			},
165
+			// 计算 nvue bottom
166
+			nvueBottom() {
167
+				// #ifdef APP-NVUE
168
+				const safeBottom = uni.getSystemInfoSync().windowBottom;
169
+				return 30 + safeBottom
170
+				// #endif
171
+				// #ifndef APP-NVUE
172
+				return 30
173
+				// #endif
174
+			}
175
+		},
176
+		watch: {
177
+			pattern: {
178
+				handler(val, oldVal) {
179
+					this.styles = Object.assign({}, this.styles, val)
180
+				},
181
+				deep: true
182
+			}
183
+		},
184
+		created() {
185
+			this.isShow = this.show
186
+			if (this.top === 0) {
187
+				this.fabShow = true
188
+			}
189
+			// 初始化样式
190
+			this.styles = Object.assign({}, this.styles, this.pattern)
191
+		},
192
+		methods: {
193
+			_onClick() {
194
+				this.$emit('fabClick')
195
+				if (!this.popMenu) {
196
+					return
197
+				}
198
+				this.isShow = !this.isShow
199
+			},
200
+			open() {
201
+				this.isShow = true
202
+			},
203
+			close() {
204
+				this.isShow = false
205
+			},
206
+			/**
207
+			 * 按钮点击事件
208
+			 */
209
+			_onItemClick(index, item) {
210
+				if (!this.isShow) {
211
+					return
212
+				}
213
+				this.$emit('trigger', {
214
+					index,
215
+					item
216
+				})
217
+			},
218
+			/**
219
+			 * 获取 位置信息
220
+			 */
221
+			getPosition(types, paramA, paramB) {
222
+				if (types === 0) {
223
+					return this.horizontal === paramA && this.vertical === paramB
224
+				} else if (types === 1) {
225
+					return this.direction === paramA && this.vertical === paramB
226
+				} else if (types === 2) {
227
+					return this.direction === paramA && this.horizontal === paramB
228
+				} else {
229
+					return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin
230
+				}
231
+			}
232
+		}
233
+	}
234
+</script>
235
+
236
+<style lang="scss" >
237
+	$uni-shadow-base:0 1px 5px 2px rgba($color: #000000, $alpha: 0.3) !default;
238
+
239
+	.uni-fab {
240
+		position: fixed;
241
+		/* #ifndef APP-NVUE */
242
+		display: flex;
243
+		/* #endif */
244
+		justify-content: center;
245
+		align-items: center;
246
+		z-index: 10;
247
+		border-radius: 45px;
248
+		box-shadow: $uni-shadow-base;
249
+	}
250
+
251
+	.uni-cursor-point {
252
+		/* #ifdef H5 */
253
+		cursor: pointer;
254
+		/* #endif */
255
+	}
256
+
257
+	.uni-fab--active {
258
+		opacity: 1;
259
+	}
260
+
261
+	.uni-fab--leftBottom {
262
+		left: 15px;
263
+		bottom: 30px;
264
+		/* #ifdef H5 */
265
+		left: calc(15px + var(--window-left));
266
+		bottom: calc(30px + var(--window-bottom));
267
+		/* #endif */
268
+		// padding: 10px;
269
+	}
270
+
271
+	.uni-fab--leftTop {
272
+		left: 15px;
273
+		top: 30px;
274
+		/* #ifdef H5 */
275
+		left: calc(15px + var(--window-left));
276
+		top: calc(30px + var(--window-top));
277
+		/* #endif */
278
+		// padding: 10px;
279
+	}
280
+
281
+	.uni-fab--rightBottom {
282
+		right: 15px;
283
+		bottom: 30px;
284
+		/* #ifdef H5 */
285
+		right: calc(15px + var(--window-right));
286
+		bottom: calc(30px + var(--window-bottom));
287
+		/* #endif */
288
+		// padding: 10px;
289
+	}
290
+
291
+	.uni-fab--rightTop {
292
+		right: 15px;
293
+		top: 30px;
294
+		/* #ifdef H5 */
295
+		right: calc(15px + var(--window-right));
296
+		top: calc(30px + var(--window-top));
297
+		/* #endif */
298
+		// padding: 10px;
299
+	}
300
+
301
+	.uni-fab__circle {
302
+		position: fixed;
303
+		/* #ifndef APP-NVUE */
304
+		display: flex;
305
+		/* #endif */
306
+		justify-content: center;
307
+		align-items: center;
308
+		width: 55px;
309
+		height: 55px;
310
+		background-color: #3c3e49;
311
+		border-radius: 45px;
312
+		z-index: 11;
313
+		// box-shadow: $uni-shadow-base;
314
+	}
315
+
316
+	.uni-fab__circle--leftBottom {
317
+		left: 15px;
318
+		bottom: 30px;
319
+		/* #ifdef H5 */
320
+		left: calc(15px + var(--window-left));
321
+		bottom: calc(30px + var(--window-bottom));
322
+		/* #endif */
323
+	}
324
+
325
+	.uni-fab__circle--leftTop {
326
+		left: 15px;
327
+		top: 30px;
328
+		/* #ifdef H5 */
329
+		left: calc(15px + var(--window-left));
330
+		top: calc(30px + var(--window-top));
331
+		/* #endif */
332
+	}
333
+
334
+	.uni-fab__circle--rightBottom {
335
+		right: 15px;
336
+		bottom: 30px;
337
+		/* #ifdef H5 */
338
+		right: calc(15px + var(--window-right));
339
+		bottom: calc(30px + var(--window-bottom));
340
+		/* #endif */
341
+	}
342
+
343
+	.uni-fab__circle--rightTop {
344
+		right: 15px;
345
+		top: 30px;
346
+		/* #ifdef H5 */
347
+		right: calc(15px + var(--window-right));
348
+		top: calc(30px + var(--window-top));
349
+		/* #endif */
350
+	}
351
+
352
+	.uni-fab__circle--left {
353
+		left: 0;
354
+	}
355
+
356
+	.uni-fab__circle--right {
357
+		right: 0;
358
+	}
359
+
360
+	.uni-fab__circle--top {
361
+		top: 0;
362
+	}
363
+
364
+	.uni-fab__circle--bottom {
365
+		bottom: 0;
366
+	}
367
+
368
+	.uni-fab__plus {
369
+		font-weight: bold;
370
+	}
371
+
372
+	// .fab-circle-v {
373
+	// 	position: absolute;
374
+	// 	width: 2px;
375
+	// 	height: 24px;
376
+	// 	left: 0;
377
+	// 	top: 0;
378
+	// 	right: 0;
379
+	// 	bottom: 0;
380
+	// 	/* #ifndef APP-NVUE */
381
+	// 	margin: auto;
382
+	// 	/* #endif */
383
+	// 	background-color: white;
384
+	// 	transform: rotate(0deg);
385
+	// 	transition: transform 0.3s;
386
+	// }
387
+
388
+	// .fab-circle-h {
389
+	// 	position: absolute;
390
+	// 	width: 24px;
391
+	// 	height: 2px;
392
+	// 	left: 0;
393
+	// 	top: 0;
394
+	// 	right: 0;
395
+	// 	bottom: 0;
396
+	// 	/* #ifndef APP-NVUE */
397
+	// 	margin: auto;
398
+	// 	/* #endif */
399
+	// 	background-color: white;
400
+	// 	transform: rotate(0deg);
401
+	// 	transition: transform 0.3s;
402
+	// }
403
+
404
+	.fab-circle-icon {
405
+		transform: rotate(0deg);
406
+		transition: transform 0.3s;
407
+		font-weight: 200;
408
+	}
409
+
410
+	.uni-fab__plus--active {
411
+		transform: rotate(135deg);
412
+	}
413
+
414
+	.uni-fab__content {
415
+		/* #ifndef APP-NVUE */
416
+		box-sizing: border-box;
417
+		display: flex;
418
+		/* #endif */
419
+		flex-direction: row;
420
+		border-radius: 55px;
421
+		overflow: hidden;
422
+		transition-property: width, height;
423
+		transition-duration: 0.2s;
424
+		width: 55px;
425
+		border-color: #DDDDDD;
426
+		border-width: 1rpx;
427
+		border-style: solid;
428
+	}
429
+
430
+	.uni-fab__content--other-platform {
431
+		border-width: 0px;
432
+		box-shadow: $uni-shadow-base;
433
+	}
434
+
435
+	.uni-fab__content--left {
436
+		justify-content: flex-start;
437
+	}
438
+
439
+	.uni-fab__content--right {
440
+		justify-content: flex-end;
441
+	}
442
+
443
+	.uni-fab__content--flexDirection {
444
+		flex-direction: column;
445
+		justify-content: flex-end;
446
+	}
447
+
448
+	.uni-fab__content--flexDirectionStart {
449
+		flex-direction: column;
450
+		justify-content: flex-start;
451
+	}
452
+
453
+	.uni-fab__content--flexDirectionEnd {
454
+		flex-direction: column;
455
+		justify-content: flex-end;
456
+	}
457
+
458
+	.uni-fab__item {
459
+		/* #ifndef APP-NVUE */
460
+		display: flex;
461
+		/* #endif */
462
+		flex-direction: column;
463
+		justify-content: center;
464
+		align-items: center;
465
+		width: 55px;
466
+		height: 55px;
467
+		opacity: 0;
468
+		transition: opacity 0.2s;
469
+	}
470
+
471
+	.uni-fab__item--active {
472
+		opacity: 1;
473
+	}
474
+
475
+	.uni-fab__item-image {
476
+		width: 20px;
477
+		height: 20px;
478
+		margin-bottom: 4px;
479
+	}
480
+
481
+	.uni-fab__item-text {
482
+		color: #FFFFFF;
483
+		font-size: 12px;
484
+		line-height: 12px;
485
+		margin-top: 2px;
486
+	}
487
+
488
+	.uni-fab__item--first {
489
+		width: 55px;
490
+	}
491
+</style>

+ 107 - 0
src/uni_modules/uni-fab/package.json

@@ -0,0 +1,107 @@
1
+{
2
+  "id": "uni-fab",
3
+  "displayName": "uni-fab 悬浮按钮",
4
+  "version": "1.2.6",
5
+  "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。",
6
+  "keywords": [
7
+    "uni-ui",
8
+    "uniui",
9
+    "按钮",
10
+    "悬浮按钮",
11
+    "fab"
12
+  ],
13
+  "repository": "https://github.com/dcloudio/uni-ui",
14
+  "engines": {
15
+    "HBuilderX": "",
16
+    "uni-app": "^3.1.0",
17
+    "uni-app-x": "^3.1.0"
18
+  },
19
+  "directories": {
20
+    "example": "../../temps/example_temps"
21
+  },
22
+  "dcloudext": {
23
+    "sale": {
24
+      "regular": {
25
+        "price": "0.00"
26
+      },
27
+      "sourcecode": {
28
+        "price": "0.00"
29
+      }
30
+    },
31
+    "contact": {
32
+      "qq": ""
33
+    },
34
+    "declaration": {
35
+      "ads": "无",
36
+      "data": "无",
37
+      "permissions": "无"
38
+    },
39
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
40
+    "type": "component-vue",
41
+    "darkmode": "-",
42
+    "i18n": "-",
43
+    "widescreen": "-"
44
+  },
45
+  "uni_modules": {
46
+    "dependencies": [
47
+      "uni-scss",
48
+      "uni-icons"
49
+    ],
50
+    "encrypt": [],
51
+    "platforms": {
52
+      "cloud": {
53
+        "tcb": "√",
54
+        "aliyun": "√",
55
+        "alipay": "x"
56
+      },
57
+      "client": {
58
+        "uni-app": {
59
+          "vue": {
60
+            "vue2": "-",
61
+            "vue3": "-"
62
+          },
63
+          "web": {
64
+            "safari": "-",
65
+            "chrome": "-"
66
+          },
67
+          "app": {
68
+            "vue": "-",
69
+            "nvue": "-",
70
+            "android": "-",
71
+            "ios": "-",
72
+            "harmony": "-"
73
+          },
74
+          "mp": {
75
+            "weixin": "-",
76
+            "alipay": "-",
77
+            "toutiao": "-",
78
+            "baidu": "-",
79
+            "kuaishou": "-",
80
+            "jd": "-",
81
+            "harmony": "-",
82
+            "qq": "-",
83
+            "lark": "-"
84
+          },
85
+          "quickapp": {
86
+            "huawei": "-",
87
+            "union": "-"
88
+          }
89
+        },
90
+        "uni-app-x": {
91
+          "web": {
92
+            "safari": "-",
93
+            "chrome": "-"
94
+          },
95
+          "app": {
96
+            "android": "-",
97
+            "ios": "-",
98
+            "harmony": "-"
99
+          },
100
+          "mp": {
101
+            "weixin": "-"
102
+          }
103
+        }
104
+      }
105
+    }
106
+  }
107
+}

+ 9 - 0
src/uni_modules/uni-fab/readme.md

@@ -0,0 +1,9 @@
1
+## Fab 悬浮按钮
2
+> **组件名:uni-fab**
3
+> 代码块: `uFab`
4
+
5
+
6
+点击可展开一个图形按钮菜单
7
+
8
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab)
9
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 0 - 0
src/uni_modules/uni-fav/changelog.md


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