Przeglądaj źródła

首页增加公告弹窗

Your Name 4 lat temu
rodzic
commit
1d0ea7d14f

+ 2 - 2
configs/env.js

@@ -9,8 +9,8 @@ let configs = {
 if (process.env.NODE_ENV === 'development') {
 	// 测试环境
 	configs = Object.assign(configs, {
-		// baseUrl: 'https://app.sunzee.com.cn/ShenzeeServer',
-		baseUrl: 'http://127.0.0.1:8090',
+		baseUrl: 'https://app.sunzee.com.cn/ShenzeeServer',
+		// baseUrl: 'http://127.0.0.1:8090',
 	});
 }
 

+ 2 - 2
main.js

@@ -54,8 +54,8 @@ const i18n = new VueI18n({
 });
 
 App.mpType = 'app'
-Vue.prototype.serverurl="http://127.0.0.1:8090";
-// Vue.prototype.serverurl="https://app.sunzee.com.cn/ShenzeeServer";
+// Vue.prototype.serverurl="http://127.0.0.1:8090";
+Vue.prototype.serverurl="https://app.sunzee.com.cn/ShenzeeServer";
 Vue.prototype.$store = store;
 Vue.prototype._i18n = i18n;
 const app = new Vue({

Plik diff jest za duży
+ 238 - 0
packageA/pages/user/editor/editor-icon.css


+ 255 - 0
packageA/pages/user/editor/editor.vue

@@ -0,0 +1,255 @@
+<template>
+	<view class="container">
+		<view class="page-body">
+			<view class="select-input">
+				<input class="input" style="display:none;" />
+				<text>标题:</text><input class="input" placeholder="请输入标题" />
+			</view>
+			<view class="head-wrapper">
+				<view class='wrapper'>
+					<view class='toolbar' @tap="format" style="height: 120px;overflow-y: auto;">
+						<view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold">
+						</view>
+						<view :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti"
+							data-name="italic"></view>
+						<view :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian"
+							data-name="underline"></view>
+						<view :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian"
+							data-name="strike"></view>
+						<view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi"
+							data-name="align" data-value="left"></view>
+						<view :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi"
+							data-name="align" data-value="center"></view>
+						<view :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi"
+							data-name="align" data-value="right"></view>
+						<view :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi"
+							data-name="align" data-value="justify"></view>
+						<view :class="formats.lineHeight ? 'ql-active' : ''" class="iconfont icon-line-height"
+							data-name="lineHeight" data-value="2"></view>
+						<view :class="formats.letterSpacing ? 'ql-active' : ''" class="iconfont icon-Character-Spacing"
+							data-name="letterSpacing" data-value="2em"></view>
+						<view :class="formats.marginTop ? 'ql-active' : ''" class="iconfont icon-722bianjiqi_duanqianju"
+							data-name="marginTop" data-value="20px"></view>
+						<view :class="formats.previewarginBottom ? 'ql-active' : ''"
+							class="iconfont icon-723bianjiqi_duanhouju" data-name="marginBottom" data-value="20px">
+						</view>
+						<view class="iconfont icon-clearedformat" @tap="removeFormat"></view>
+						<view :class="formats.fontFamily ? 'ql-active' : ''" class="iconfont icon-font"
+							data-name="fontFamily" data-value="Pacifico"></view>
+						<view :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-fontsize"
+							data-name="fontSize" data-value="24px"></view>
+
+						<view :class="formats.color === '#0000ff' ? 'ql-active' : ''" class="iconfont icon-text_color"
+							data-name="color" data-value="#0000ff"></view>
+						<view :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
+							class="iconfont icon-fontbgcolor" data-name="backgroundColor" data-value="#00ff00"></view>
+
+						<view class="iconfont icon-date" @tap="insertDate"></view>
+						<view class="iconfont icon--checklist" data-name="list" data-value="check"></view>
+						<view :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie"
+							data-name="list" data-value="ordered"></view>
+						<view :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie"
+							data-name="list" data-value="bullet"></view>
+						<view class="iconfont icon-undo" @tap="undo"></view>
+						<view class="iconfont icon-redo" @tap="redo"></view>
+
+						<view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
+						<view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
+						<view class="iconfont icon-fengexian" @tap="insertDivider"></view>
+						<view class="iconfont icon-charutupian" @tap="insertImage"></view>
+						<view :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1"
+							data-name="header" :data-value="1"></view>
+						<view :class="formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao"
+							data-name="script" data-value="sub"></view>
+						<view :class="formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao"
+							data-name="script" data-value="super"></view>
+						<view class="iconfont icon-shanchu" @tap="clear"></view>
+						<view :class="formats.direction === 'rtl' ? 'ql-active' : ''"
+							class="iconfont icon-direction-rtl" data-name="direction" data-value="rtl"></view>
+
+					</view>
+
+					<view class="editor-wrapper">
+						<editor id="editor" class="ql-container" placeholder="开始输入..." showImgSize showImgToolbar
+							showImgResize @statuschange="onStatusChange" :read-only="readOnly" @ready="onEditorReady">
+						</editor>
+					</view>
+				</view>
+			</view>
+			<!-- <button>提交</button> -->
+			<view class="btn-area">
+				<button type="primary" formType="submit">确认</button>
+			</view>
+
+
+		</view>
+
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				readOnly: false,
+				formats: {}
+			}
+		},
+		methods: {
+			readOnlyChange() {
+				this.readOnly = !this.readOnly
+			},
+			onEditorReady() {
+				uni.createSelectorQuery().select('#editor').context((res) => {
+					this.editorCtx = res.context
+				}).exec()
+			},
+			undo() {
+				this.editorCtx.undo()
+			},
+			redo() {
+				this.editorCtx.redo()
+			},
+			format(e) {
+				let {
+					name,
+					value
+				} = e.target.dataset
+				if (!name) return
+				// console.log('format', name, value)
+				this.editorCtx.format(name, value)
+
+			},
+			onStatusChange(e) {
+				const formats = e.detail
+				this.formats = formats
+			},
+			insertDivider() {
+				this.editorCtx.insertDivider({
+					success: function() {
+						console.log('insert divider success')
+					}
+				})
+			},
+			clear() {
+				this.editorCtx.clear({
+					success: function(res) {
+						console.log("clear success")
+					}
+				})
+			},
+			removeFormat() {
+				this.editorCtx.removeFormat()
+			},
+			insertDate() {
+				const date = new Date()
+				const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
+				this.editorCtx.insertText({
+					text: formatDate
+				})
+			},
+			insertImage() {
+				uni.chooseImage({
+					count: 1,
+					success: (res) => {
+						this.editorCtx.insertImage({
+							src: res.tempFilePaths[0],
+							alt: '图像',
+							success: function() {
+								console.log('insert image success')
+							}
+						})
+					}
+				})
+			}
+		},
+		onLoad() {
+			uni.loadFontFace({
+				family: 'Pacifico',
+				source: 'url("https://sungd.github.io/Pacifico.ttf")'
+			})
+		},
+	}
+</script>
+
+<style lang="scss">
+	@import "./editor-icon.css";
+
+	.page-body {
+		height: calc(100vh - var(--window-top) - var(--status-bar-height));
+	}
+
+	.head-wrapper {
+		border-style: solid;
+		border-width: 2px;
+		margin: 10px;
+	}
+
+	.wrapper {
+		height: 100%;
+	}
+
+	.editor-wrapper {
+		height: calc(100vh - var(--window-top) - var(--status-bar-height) - 50px - 240px);
+		background: #fff;
+	}
+
+	.iconfont {
+		display: inline-block;
+		padding: 8px 8px;
+		width: 24px;
+		height: 24px;
+		cursor: pointer;
+		font-size: 20px;
+	}
+
+	.toolbar {
+		box-sizing: border-box;
+		border-bottom: 0;
+		font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
+	}
+
+
+	.ql-container {
+		box-sizing: border-box;
+		padding: 12px 15px;
+		width: 100%;
+		min-height: 30vh;
+		height: 100%;
+		margin-top: 20px;
+		font-size: 16px;
+		line-height: 1.5;
+	}
+
+	.ql-active {
+		color: #06c;
+	}
+
+	.btn-area {
+		padding: 50upx;
+		padding-top: 20upx;
+		padding-bottom: 0upx;
+
+		button {
+			background-color: #007AFF;
+		}
+	}
+
+	.select-input {
+		display: flex;
+		flex-direction: row;
+		justify-content: flex-start;
+		font-size: 16px;
+		margin: 30upx 0 50upx 30upx;
+
+		input {
+			padding-left: 20upx;
+			// padding-top: 10upx;
+			background-color: #FFFFFF;
+			width: 500upx;
+			height: 50upx;
+			box-shadow: 0upx 0upx 20upx #D3D3D3;
+			border-radius: 5upx;
+		}
+	}
+</style>

BIN
packageA/pages/user/editor/iconfont.ttf


+ 7 - 1
pages.json

@@ -258,8 +258,14 @@
 					"navigationBarTitleText": "退款",
 					"enablePullDownRefresh": false
 				}
-
+					
 			}, {
+				"path": "pages/user/editor/editor",
+				"style": {
+					"navigationBarTitleText": "编辑公告",
+					"enablePullDownRefresh": false
+				}
+			},{
 				"path": "pages/user/orderEit",
 				"style": {
 					"navigationBarTitleText": "详情",

+ 60 - 1
pages/Charts/mainStatistics.vue

@@ -193,6 +193,41 @@
 					style="background-color: #FFFFFF;"></canvas>
 			</view>
 		</view>
+		<view>
+			<uni-popup id="popupDialog" ref="popupDialog" type="dialog">
+				<uni-popup-dialog :type="msgType" title="通知" content="欢迎使用 unisdfdd-popup!"
+								  :before-close="true" @confirm="dialogConfirm" @close="dialogClose">
+					<scroll-view  scroll-y="true" class="scroll-Y" :style="{height:scrollHeight*0.8+'px'}">
+						<view class="scroll-view-item ">
+							投诉处理通知书 <br/>
+							背景:消费者投诉是指消费者在买糖之后,在微信或者支付宝平台对该笔订单发起投诉。该功能是微信及支付宝所拥有的功能。
+							消费者投诉后,支付宝或者微信会通知到我们公司,我们公司再通知到商家。商家接到消费者投诉后需要进行处理。
+							如果商家在收到短信24小时后还不处理,我们将会进行退款处理。<br/>
+							处理需知:我们申泽会以短信的信息通知到商家,通知的内容里我们会加上消费者的联系方式,以供商家去核实情况,如果没有消费者的联系方式,可以在微信群联系我们,然后把商家的联系方式发送到我们这边,我们公司会把商家的联系方式发送到消费者的微信上,让消费者自己去联系商家。
+							以下是处理方式:<br/>
+							1,退款<br/>
+							退款一般是在消费者离开机器现场后,在微信或者支付平台上投诉的情况。<br/>
+							1.1在小程序“申泽智能物联”上退款<br/>
+							我们推荐使用这种方式,因为在我们申泽的平台上退款后,我们会有已退款的记录凭证,以免后续不必要的纠纷。
+							如果商家在我们公司的账户里没钱,可推荐红包或转账的方式<br/>
+							1.2用红包或转账的方式<br/>
+							商家可用红包或转账的方式给消费者退款,但是需要收集3个资料:消费者的支付凭证,截图红包或者转账的界面(红包最好写上备注),
+							聊天界面-消费者同意用红包或转账的方式退款。商家收集这3个资料给到我们公司,由我们公司反馈上去。<br/>
+							2,不退款<br/>
+							不退款一般发生在消费者还在机器的情况下,商家和消费者取得了联系,用其他方式补偿。<br/>
+							2.1重新做糖<br/>
+							商家可以用重新做糖给消费者的形式补偿,但是商家也需要收集证据。
+								需要收集:1消费者的支付凭证;2聊天界面-消费者同意截图;商家需要把这些收集好。
+								如果消费者投诉了这个有重新做糖的订单,可以把这些信息发送到我们申泽这边,这样就可以不用退款。<br/>
+							2.2消费者提供不了证据<br/>
+							这个情况是在商家与消费者取得联系后,消费者无法提供证据说明商品有问题。
+							商家可以把聊天记录--确认消费者提供不了证据的界面的截图提供到我们这边。由我们把情况反馈上去。<br/>
+						</view>
+					
+					</scroll-view>
+				</uni-popup-dialog>
+			</uni-popup>
+		</view>
 	</view>
 </template>
 
@@ -248,9 +283,20 @@
 				index: 0,
 				id: 0,
 				length:0,
-				type: null,
+				// type:null,
+				type: 'top',
+				msgType: 'success',
+				value: '默认输入的内容',
+				scrollHeight:uni.getSystemInfoSync().windowHeight - 130,
 			}
 		},
+		onReady() {
+			// 页面打开自动打开对话框
+			setTimeout(() => {
+				// this.msgType = 'success'
+				this.$refs.popupDialog.open()
+			}, 500)
+		},
 		computed: {
 			...mapState(['loginUser']),
 			pnameData: {
@@ -309,6 +355,9 @@
 			this.equipmentStatus();
 		},
 		methods: {
+			// open() {
+			// 	this.show = true;
+			// },
 			...mapActions('chart', ['getStatistics', 'getMachineNum']),
 			async init() {
 				if (!this.pname) {
@@ -903,6 +952,16 @@
 					});
 				}
 			},
+			// 点击确定
+			dialogConfirm(done){
+				this.$refs.popupDialog.close()
+			},
+			//点击取消
+			dialogClose(done){
+				// this.msgType = 'success'
+				// 需要执行 done 才能关闭对话框
+				this.$refs.popupDialog.close()
+			}	
 		}
 	}
 </script>

+ 8 - 0
pages/User/user.vue

@@ -42,6 +42,9 @@
 				<view>
 					<uni-list-item title="退款" @click="refuse()" />
 				</view>
+				<view>
+					<uni-list-item title="编辑公告" @click="notice()" />
+				</view>
 			</uni-list>
 		</view>
 		<view class="xiugai2">
@@ -175,6 +178,11 @@
 					url: '../../packageA/pages/user/refuse',
 				});
 			},
+			notice(){
+				uni.navigateTo({
+					url: '../../packageA/pages/user/editor/editor',
+				});
+			},
 			trant() {
 				uni.reLaunch({
 					url: '../WeixinSwicth/WeixinSwicth',

+ 33 - 0
uni_modules/uni-popup/changelog.md

@@ -0,0 +1,33 @@
+## 1.6.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.5.0(2021-06-23)
+- 新增 mask-click 遮罩层点击事件
+## 1.4.5(2021-06-22)
+- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
+## 1.4.4(2021-06-18)
+- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
+## 1.4.3(2021-06-08)
+- 修复 错误的 watch 字段
+- 修复 safeArea 属性不生效的问题
+- 修复 点击内容,再点击遮罩无法关闭的Bug
+## 1.4.2(2021-05-12)
+- 新增 组件示例地址
+## 1.4.1(2021-04-29)
+- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
+## 1.4.0 (2021-04-29)
+- 新增 type 属性的 left\right 值,支持左右弹出
+- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
+- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
+- 新增 safeArea 属性,是否适配底部安全区
+- 修复 App\h5\微信小程序底部安全区占位不对的Bug
+- 修复 App 端弹出等待的Bug
+- 优化 提升低配设备性能,优化动画卡顿问题
+- 优化 更简单的组件自定义方式
+## 1.2.9(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 1.2.8(2021-02-05)
+- 调整为uni_modules目录规范
+## 1.2.7(2021-02-05)
+- 调整为uni_modules目录规范
+- 新增 支持 PC 端
+- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

+ 45 - 0
uni_modules/uni-popup/components/uni-popup-dialog/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    this.$once('hook:beforeDestroy', () => {
+      document.removeEventListener('keyup', listener)
+    })
+  },
+	render: () => {}
+}
+// #endif

+ 243 - 0
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue

@@ -0,0 +1,243 @@
+<template>
+	<view class="uni-popup-dialog">
+		<view class="uni-dialog-title">
+			<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{title}}</text>
+		</view>
+		<view v-if="mode === 'base'" class="uni-dialog-content">
+			<slot>
+				<text class="uni-dialog-content-text">{{content}}</text>
+			</slot>
+		</view>
+		<view v-else class="uni-dialog-content">
+			<slot>
+				<input class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus" >
+			</slot>
+		</view>
+		<view class="uni-dialog-button-group">
+			<view class="uni-dialog-button" @click="closeDialog">
+				<text class="uni-dialog-button-text">取消</text>
+			</view>
+			<view class="uni-dialog-button uni-border-left" @click="onOk">
+				<text class="uni-dialog-button-text uni-button-color">确定</text>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	/**
+	 * PopUp 弹出层-对话框样式
+	 * @description 弹出层-对话框样式
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} value input 模式下的默认值
+	 * @property {String} placeholder input 模式下输入提示
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} mode = [base|input] 模式、
+	 * 	@value base 基础对话框
+	 * 	@value input 可输入对话框
+	 * @property {String} content 对话框内容
+	 * @property {Boolean} beforeClose 是否拦截取消事件
+	 * @event {Function} confirm 点击确认按钮触发
+	 * @event {Function} close 点击取消按钮触发
+	 */
+
+	export default {
+		name: "uniPopupDialog",
+		mixins: [popup],
+		props: {
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			placeholder: {
+				type: [String, Number],
+				default: '请输入内容'
+			},
+			type: {
+				type: String,
+				default: 'error'
+			},
+			mode: {
+				type: String,
+				default: 'base'
+			},
+			title: {
+				type: String,
+				default: '提示'
+			},
+			content: {
+				type: String,
+				default: ''
+			},
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				dialogType: 'error',
+				focus: false,
+				val: ""
+			}
+		},
+		watch: {
+			type(val) {
+				this.dialogType = val
+			},
+			mode(val) {
+				if (val === 'input') {
+					this.dialogType = 'info'
+				}
+			},
+			value(val) {
+				this.val = val
+			}
+		},
+		created() {
+			// 对话框遮罩不可点击
+			this.popup.disableMask()
+			// this.popup.closeMask()
+			if (this.mode === 'input') {
+				this.dialogType = 'info'
+				this.val = this.value
+			} else {
+				this.dialogType = this.type
+			}
+		},
+		mounted() {
+			this.focus = true
+		},
+		methods: {
+			/**
+			 * 点击确认按钮
+			 */
+			onOk() {
+				if (this.mode === 'input'){
+					this.$emit('confirm', this.val)
+				}else{
+					this.$emit('confirm')
+				}
+				if(this.beforeClose) return
+				this.popup.close()
+			},
+			/**
+			 * 点击取消按钮
+			 */
+			closeDialog() {
+				this.$emit('close')
+				if(this.beforeClose) return
+				this.popup.close()
+			},
+			close(){
+				this.popup.close()
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-popup-dialog {
+		width: 300px;
+		border-radius: 15px;
+		background-color: #fff;
+	}
+
+	.uni-dialog-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 15px;
+		padding-bottom: 5px;
+	}
+
+	.uni-dialog-title-text {
+		font-size: 16px;
+		font-weight: 500;
+	}
+
+	.uni-dialog-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 5px 15px 15px 15px;
+	}
+
+	.uni-dialog-content-text {
+		font-size: 14px;
+		color: #6e6e6e;
+	}
+
+	.uni-dialog-button-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-top-color: #f5f5f5;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-dialog-button {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+
+		flex: 1;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+	}
+
+	.uni-border-left {
+		border-left-color: #f0f0f0;
+		border-left-style: solid;
+		border-left-width: 1px;
+	}
+
+	.uni-dialog-button-text {
+		font-size: 14px;
+	}
+
+	.uni-button-color {
+		color: #007aff;
+	}
+
+	.uni-dialog-input {
+		flex: 1;
+		font-size: 14px;
+		border: 1px #eee solid;
+		height: 40px;
+		padding: 0 10px;
+		border-radius: 5px;
+		color: #555;
+	}
+
+	.uni-popup__success {
+		color: #4cd964;
+	}
+
+	.uni-popup__warn {
+		color: #f0ad4e;
+	}
+
+	.uni-popup__error {
+		color: #dd524d;
+	}
+
+	.uni-popup__info {
+		color: #909399;
+	}
+</style>

+ 143 - 0
uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue

@@ -0,0 +1,143 @@
+<template>
+	<view class="uni-popup-message">
+		<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
+			<slot>
+				<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	/**
+	 * PopUp 弹出层-消息提示
+	 * @description 弹出层-消息提示
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} message 消息提示文字
+	 * @property {String} duration 显示时间,设置为 0 则不会自动关闭
+	 */
+
+	export default {
+		name: 'uniPopupMessage',
+		mixins:[popup],
+		props: {
+			/**
+			 * 主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'success'
+			},
+			/**
+			 * 消息文字
+			 */
+			message: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 显示时间,设置为 0 则不会自动关闭
+			 */
+			duration: {
+				type: Number,
+				default: 3000
+			},
+			maskShow:{
+				type:Boolean,
+				default:false
+			}
+		},
+		data() {
+			return {}
+		},
+		created() {
+			this.popup.maskShow = this.maskShow
+			this.popup.messageChild = this
+		},
+		methods: {
+			timerClose(){
+				if(this.duration === 0) return
+				clearTimeout(this.timer) 
+				this.timer = setTimeout(()=>{
+					this.popup.close()
+				},this.duration)
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-message {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+	}
+
+	.uni-popup-message__box {
+		background-color: #e1f3d8;
+		padding: 10px 15px;
+		border-color: #eee;
+		border-style: solid;
+		border-width: 1px;
+		flex: 1;
+	}
+
+	@media screen and (min-width: 500px) {
+		.fixforpc-width {
+			margin-top: 20px;
+			border-radius: 4px;
+			flex: none;
+			min-width: 380px;
+			/* #ifndef APP-NVUE */
+			max-width: 50%;
+			/* #endif */
+			/* #ifdef APP-NVUE */
+			max-width: 500px;
+			/* #endif */
+		}
+	}
+
+	.uni-popup-message-text {
+		font-size: 14px;
+		padding: 0;
+	}
+
+	.uni-popup__success {
+		background-color: #e1f3d8;
+	}
+
+	.uni-popup__success-text {
+		color: #67C23A;
+	}
+
+	.uni-popup__warn {
+		background-color: #faecd8;
+	}
+
+	.uni-popup__warn-text {
+		color: #E6A23C;
+	}
+
+	.uni-popup__error {
+		background-color: #fde2e2;
+	}
+
+	.uni-popup__error-text {
+		color: #F56C6C;
+	}
+
+	.uni-popup__info {
+		background-color: #F2F6FC;
+	}
+
+	.uni-popup__info-text {
+		color: #909399;
+	}
+</style>

+ 171 - 0
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue

@@ -0,0 +1,171 @@
+<template>
+	<view class="uni-popup-share">
+		<view class="uni-share-title"><text class="uni-share-title-text">{{title}}</text></view>
+		<view class="uni-share-content">
+			<view class="uni-share-content-box">
+				<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
+					<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
+					<text class="uni-share-text">{{item.text}}</text>
+				</view>
+
+			</view>
+		</view>
+		<view class="uni-share-button-box">
+			<button class="uni-share-button" @click="close">取消</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	export default {
+		name: 'UniPopupShare',
+		mixins:[popup],
+		props: {
+			title: {
+				type: String,
+				default: '分享到'
+			},
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				bottomData: [{
+						text: '微信',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
+						name: 'wx'
+					},
+					{
+						text: '支付宝',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
+						name: 'wx'
+					},
+					{
+						text: 'QQ',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
+						name: 'qq'
+					},
+					{
+						text: '新浪',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
+						name: 'sina'
+					},
+					{
+						text: '百度',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
+						name: 'copy'
+					},
+					{
+						text: '其他',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
+						name: 'more'
+					}
+				]
+			}
+		},
+		created() {},
+		methods: {
+			/**
+			 * 选择内容
+			 */
+			select(item, index) {
+				this.$emit('select', {
+					item,
+					index
+				})
+				this.close()
+				
+			},
+			/**
+			 * 关闭窗口
+			 */
+			close() {
+				if(this.beforeClose) return
+				this.popup.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-share {
+		background-color: #fff;
+	}
+	.uni-share-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		height: 40px;
+	}
+	.uni-share-title-text {
+		font-size: 14px;
+		color: #666;
+	}
+	.uni-share-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 10px;
+	}
+
+	.uni-share-content-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: wrap;
+		width: 360px;
+	}
+
+	.uni-share-content-item {
+		width: 90px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		padding: 10px 0;
+		align-items: center;
+	}
+
+	.uni-share-content-item:active {
+		background-color: #f5f5f5;
+	}
+
+	.uni-share-image {
+		width: 30px;
+		height: 30px;
+	}
+
+	.uni-share-text {
+		margin-top: 10px;
+		font-size: 14px;
+		color: #3B4144;
+	}
+
+	.uni-share-button-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		padding: 10px 15px;
+	}
+
+	.uni-share-button {
+		flex: 1;
+		border-radius: 50px;
+		color: #666;
+		font-size: 16px;
+	}
+
+	.uni-share-button::after {
+		border-radius: 50px;
+	}
+</style>

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

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    // this.$once('hook:beforeDestroy', () => {
+    //   document.removeEventListener('keyup', listener)
+    // })
+  },
+	render: () => {}
+}
+// #endif

+ 26 - 0
uni_modules/uni-popup/components/uni-popup/popup.js

@@ -0,0 +1,26 @@
+
+export default {
+	data() {
+		return {
+			
+		}
+	},
+	created(){
+		this.popup = this.getParent()
+	},
+	methods:{
+		/**
+		 * 获取父元素实例
+		 */
+		getParent(name = 'uniPopup') {
+			let parent = this.$parent;
+			let parentName = parent.$options.name;
+			while (parentName !== name) {
+				parent = parent.$parent;
+				if (!parent) return false
+				parentName = parent.$options.name;
+			}
+			return parent;
+		},
+	}
+}

+ 401 - 0
uni_modules/uni-popup/components/uni-popup/uni-popup.vue

@@ -0,0 +1,401 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']" @touchmove.stop.prevent="clear">
+		<view @touchstart="touchstart" >
+			<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
+			<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+				<view class="uni-popup__wrapper" :style="{ backgroundColor: bg }" :class="[popupstyle]" @click="clear"><slot /></view>
+			</uni-transition>
+		</view>
+		<!-- #ifdef H5 -->
+		<keypress v-if="maskShow" @esc="onTap" />
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+// #ifdef H5
+import keypress from './keypress.js'
+// #endif
+
+/**
+ * PopUp 弹出层
+ * @description 弹出层组件,为了解决遮罩弹层的问题
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+ * @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
+ * 	@value top 顶部弹出
+ * 	@value center 中间弹出
+ * 	@value bottom 底部弹出
+ * 	@value left		左侧弹出
+ * 	@value right  右侧弹出
+ * 	@value message 消息提示
+ * 	@value dialog 对话框
+ * 	@value share 底部分享示例
+ * @property {Boolean} animation = [ture|false] 是否开启动画
+ * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
+ * @property {String}  backgroundColor 					主窗口背景色
+ * @property {Boolean} safeArea									是否适配底部安全区
+ * @event {Function} change 打开关闭弹窗触发,e={show: false}
+ */
+
+export default {
+	name: 'uniPopup',
+	components: {
+		// #ifdef H5
+		keypress
+		// #endif
+	},
+	props: {
+		// 开启动画
+		animation: {
+			type: Boolean,
+			default: true
+		},
+		// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+		// message: 消息提示 ; dialog : 对话框
+		type: {
+			type: String,
+			default: 'center'
+		},
+		// maskClick
+		maskClick: {
+			type: Boolean,
+			default: true
+		},
+		backgroundColor: {
+			type: String,
+			default: 'none'
+		},
+		safeArea:{
+			type: Boolean,
+			default: true
+		}
+	},
+
+	watch: {
+		/**
+		 * 监听type类型
+		 */
+		type: {
+			handler: function(type) {
+				if (!this.config[type]) return
+				this[this.config[type]](true)
+			},
+			immediate: true
+		},
+		isDesktop: {
+			handler: function(newVal) {
+				if (!this.config[newVal]) return
+				this[this.config[this.type]](true)
+			},
+			immediate: true
+		},
+		/**
+		 * 监听遮罩是否可点击
+		 * @param {Object} val
+		 */
+		maskClick: {
+			handler: function(val) {
+				this.mkclick = val
+			},
+			immediate: true
+		}
+	},
+	data() {
+		return {
+			duration: 300,
+			ani: [],
+			showPopup: false,
+			showTrans: false,
+			popupWidth: 0,
+			popupHeight: 0,
+			config: {
+				top: 'top',
+				bottom: 'bottom',
+				center: 'center',
+				left: 'left',
+				right: 'right',
+				message: 'top',
+				dialog: 'center',
+				share: 'bottom'
+			},
+			maskClass: {
+				position: 'fixed',
+				bottom: 0,
+				top: 0,
+				left: 0,
+				right: 0,
+				backgroundColor: 'rgba(0, 0, 0, 0.4)'
+			},
+			transClass: {
+				position: 'fixed',
+				left: 0,
+				right: 0
+			},
+			maskShow: true,
+			mkclick: true,
+			popupstyle: this.isDesktop ? 'fixforpc-top' : 'top'
+		}
+	},
+	computed: {
+		isDesktop() {
+			return this.popupWidth >= 500 && this.popupHeight >= 500
+		},
+		bg() {
+			if (this.backgroundColor === '' || this.backgroundColor === 'none') {
+				return 'transparent'
+			}
+			return this.backgroundColor
+		}
+	},
+	mounted() {
+		const fixSize = () => {
+			const { windowWidth, windowHeight, windowTop, safeAreaInsets } = uni.getSystemInfoSync()
+			this.popupWidth = windowWidth
+			this.popupHeight = windowHeight + windowTop
+			// 是否适配底部安全区
+			if(this.safeArea){
+				this.safeAreaInsets = safeAreaInsets
+			}else{
+				this.safeAreaInsets = 0
+			}
+		}
+		fixSize()
+		// #ifdef H5
+		// window.addEventListener('resize', fixSize)
+		// this.$once('hook:beforeDestroy', () => {
+		// 	window.removeEventListener('resize', fixSize)
+		// })
+		// #endif
+	},
+	created() {
+		this.mkclick = this.maskClick
+		if (this.animation) {
+			this.duration = 300
+		} else {
+			this.duration = 0
+		}
+		// TODO 处理 message 组件生命周期异常的问题
+		this.messageChild = null
+		// TODO 解决头条冒泡的问题
+		this.clearPropagation = false
+	},
+	methods: {
+		/**
+		 * 公用方法,不显示遮罩层
+		 */
+		closeMask() {
+			this.maskShow = false
+		},
+		/**
+		 * 公用方法,遮罩层禁止点击
+		 */
+		disableMask() {
+			this.mkclick = false
+		},
+		// TODO nvue 取消冒泡
+		clear(e) {
+			// #ifndef APP-NVUE
+			e.stopPropagation()
+			// #endif
+			this.clearPropagation = true
+		},
+
+		open(direction) {
+			let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
+			if (!(direction && innerType.indexOf(direction) !== -1)) {
+				direction = this.type
+			}
+			if (!this.config[direction]) {
+				console.error('缺少类型:', direction)
+				return
+			}
+			this[this.config[direction]]()
+			this.$emit('change', {
+				show: true,
+				type: direction
+			})
+		},
+		close(type) {
+			this.showTrans = false
+			this.$emit('change', {
+				show: false,
+				type: this.type
+			})
+			clearTimeout(this.timer)
+			// // 自定义关闭事件
+			// this.customOpen && this.customClose()
+			this.timer = setTimeout(() => {
+				this.showPopup = false
+			}, 300)
+		},
+		// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
+		touchstart(){
+			this.clearPropagation = false
+		},
+
+		onTap() {
+			if (this.clearPropagation) {
+				// fix by mehaotian 兼容 nvue
+				this.clearPropagation = false
+				return
+			}
+			this.$emit('maskClick')
+			if (!this.mkclick) return
+			this.close()
+		},
+		/**
+		 * 顶部弹出样式处理
+		 */
+		top(type) {
+			this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
+			this.ani = ['slide-top']
+			this.transClass = {
+				position: 'fixed',
+				left: 0,
+				right: 0,
+				backgroundColor: this.bg
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+			this.$nextTick(() => {
+				if (this.messageChild && this.type === 'message') {
+					this.messageChild.timerClose()
+				}
+			})
+		},
+		/**
+		 * 底部弹出样式处理
+		 */
+		bottom(type) {
+			this.popupstyle = 'bottom'
+			this.ani = ['slide-bottom']
+
+			this.transClass = {
+				position: 'fixed',
+				left: 0,
+				right: 0,
+				bottom: 0,
+				paddingBottom: (this.safeAreaInsets && this.safeAreaInsets.bottom) || 0,
+				backgroundColor: this.bg
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		},
+		/**
+		 * 中间弹出样式处理
+		 */
+		center(type) {
+			this.popupstyle = 'center'
+			this.ani = ['zoom-out', 'fade']
+			this.transClass = {
+				position: 'fixed',
+				/* #ifndef APP-NVUE */
+				display: 'flex',
+				flexDirection: 'column',
+				/* #endif */
+				bottom: 0,
+				left: 0,
+				right: 0,
+				top: 0,
+				justifyContent: 'center',
+				alignItems: 'center'
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		},
+		left(type) {
+			this.popupstyle = 'left'
+			this.ani = ['slide-left']
+			this.transClass = {
+				position: 'fixed',
+				left: 0,
+				bottom: 0,
+				top: 0,
+				backgroundColor: this.bg,
+				/* #ifndef APP-NVUE */
+				display: 'flex',
+				flexDirection: 'column'
+				/* #endif */
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		},
+		right(type) {
+			this.popupstyle = 'right'
+			this.ani = ['slide-right']
+			this.transClass = {
+				position: 'fixed',
+				bottom: 0,
+				right: 0,
+				top: 0,
+				backgroundColor: this.bg,
+				/* #ifndef APP-NVUE */
+				display: 'flex',
+				flexDirection: 'column'
+				/* #endif */
+			}
+			// TODO 兼容 type 属性 ,后续会废弃
+			if (type) return
+			this.showPopup = true
+			this.showTrans = true
+		}
+	}
+}
+</script>
+<style lang="scss" scoped>
+.uni-popup {
+	position: fixed;
+	/* #ifndef APP-NVUE */
+	z-index: 99;
+	/* #endif */
+	&.top,
+	&.left,
+	&.right {
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+	}
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+		/* iphonex 等安全区设置,底部安全区适配 */
+		/* #ifndef APP-NVUE */
+		// padding-bottom: constant(safe-area-inset-bottom);
+		// padding-bottom: env(safe-area-inset-bottom);
+		/* #endif */
+		&.left,
+		&.right {
+			/* #ifdef H5 */
+			padding-top: var(--window-top);
+			/* #endif */
+			/* #ifndef H5 */
+			padding-top: 0;
+			/* #endif */
+			flex: 1;
+		}
+	}
+}
+
+.fixforpc-z-index {
+	/* #ifndef APP-NVUE */
+	z-index: 999;
+	/* #endif */
+}
+
+.fixforpc-top {
+	top: 0;
+}
+</style>

+ 83 - 0
uni_modules/uni-popup/package.json

@@ -0,0 +1,83 @@
+{
+  "id": "uni-popup",
+  "displayName": "uni-popup 弹出层",
+  "version": "1.6.0",
+  "description": " Popup 组件,提供常用的弹层",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "弹出层"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "category": [
+      "前端组件",
+      "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": [
+      "uni-transition"
+    ],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 296 - 0
uni_modules/uni-popup/readme.md

@@ -0,0 +1,296 @@
+
+
+## Popup 弹出层
+> **组件名:uni-popup**
+> 代码块: `uPopup`
+> 关联组件:`uni-popup-dialog`,`uni-popup-message`,`uni-popup-share`,`uni-transition`
+
+
+弹出层组件,在应用中弹出一个消息提示窗口、提示框等
+
+
+> **注意事项**
+> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
+> - 组件需要依赖 `sass` 插件 ,请自行手动安装
+> - `uni-popup-message` 、 `uni-popup-dialog` 等扩展ui组件,需要和 `uni-popup` 配套使用,暂不支持单独使用
+> - `nvue` 中使用 `uni-popup` 时,尽量将组件置于其他元素后面,避免出现层级问题
+> - `uni-popup` 并不能完全阻止页面滚动,可在打开 `uni-popup` 的时候手动去做一些处理,禁止页面滚动
+> - 如果想在页面渲染完毕后就打开 `uni-popup` ,请在 `onReady` 或 `mounted` 生命周期内调用,确保组件渲染完毕
+> - 在微信小程序开发者工具中,启用真机调试,popup 会延时出现,是因为 setTimeout 在真机调试中的延时问题导致的,预览和发布小程序不会出现此问题
+> - 使用 `npm` 方式引入组件,如果确认引用正确,但是提示未注册组件或显示不正常,请尝试重新编译项目
+> - `uni-popup` 中尽量不要使用 `scroll-view` 嵌套过多的内容,可能会影响组件的性能,导致组件无法打开或者打开卡顿
+> - `uni-popup` 不会覆盖原生 tabbar 和原生导航栏
+> - 组件支持 nvue ,需要在 `manifest.json > app-plus` 节点下配置 `"nvueStyleCompiler" : "uni-app"` 
+> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
+
+
+
+### 安装方式
+
+本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
+
+如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
+
+
+### 基本用法 
+
+**示例**
+
+```html
+<button @click="open">打开弹窗</button>
+<uni-popup ref="popup" type="bottom">底部弹出 Popup</uni-popup>
+```
+
+```javascript
+export default {
+   methods:{
+      open(){
+        // 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
+        this.$refs.popup.open('top')
+      }
+   }
+}
+
+```
+
+### 设置主窗口背景色
+
+在大多数场景下,并不需要设置 `background-color` 属性,因为`uni-popup`的主窗口默认是透明的,在向里面插入内容的时候 ,样式完全交由用户定制,如果设置了背景色 ,例如 `uni-popup-dialog` 中的圆角就很难去实现,不设置背景色,更适合用户去自由发挥。
+
+而也有特例,需要我们主动去设置背景色,例如 `type = 'bottom'` 的时候 ,在异型屏中遇到了底部安全区问题(如 iphone 11),因为 `uni-popup`的主要内容避开了安全区(设置`safe-area:true`),导致底部的颜色我们无法自定义,这时候使用 `background-color` 就可以解决这个问题。 
+
+**示例**
+
+```html
+<button @click="open">打开弹窗</button>
+<uni-popup ref="popup" type="bottom" background-color="#fff">底部弹出 Popup</uni-popup>
+```
+
+### 禁用打开动画
+在某些场景 ,可能不希望弹层有动画效果 ,只需要将 `animation` 属性设置为 `false` 即可以关闭动画。
+
+**示例**
+
+```html
+<button @click="open">打开弹窗</button>
+<uni-popup ref="popup" type="center" :animation="false">中间弹出 Popup</uni-popup>
+```
+
+### 禁用点击遮罩关闭
+默认情况下,点击遮罩会自动关闭`uni-popup`,如不想点击关闭,只需将`mask-click`设置为`false`,这时候要关闭`uni-popup`,只能手动调用 `close` 方法。
+
+**示例**
+
+```html
+<button @click="open">打开弹窗</button>
+<uni-popup ref="popup" :mask-click="false">
+	<text>Popup</text>
+	<button @click="close">关闭</button>
+</uni-popup>
+```
+
+```javascript
+export default {
+	data() {
+		return {}
+	},
+	onReady() {},
+	methods: {
+		open() {
+			this.$refs.popup.open('top')
+		},
+		close() {
+			this.$refs.popup.close()
+		}
+	}
+}
+
+```
+
+## API
+
+### Popup Props 
+
+|属性名|类型|默认值|说明|
+|:-:|:-:|:-:|:-:|
+|animation|Boolean|true|是否开启动画|
+|type|String|'center'|弹出方式|
+|mask-click|Boolean|true|蒙版点击是否关闭弹窗|
+|background-color|String|'none'|主窗口背景色|
+|safe-area|Boolean|true|是否适配底部安全区|
+
+#### Type Options
+
+|属性名|说明|
+|:-:| :-:|
+|top|顶部弹出	|
+|center|居中弹出|
+|bottom|底部弹出|
+|left|左侧弹出|
+|right|右侧弹出|
+|message|预置样式 :消息提示|
+|dialog|预置样式 :对话框|
+|share|预置样式 :底部弹出分享示例	|
+
+
+### Popup Methods
+
+|方法称名	|说明|参数|
+|:-:|:-:|:-:|
+|open|打开弹出层|open(String:type) ,如果参数可代替 type 属性|
+|close|关闭弹出层	|-|
+
+
+### Popup Events
+
+|事件称名|说明|返回值|
+|:-:|:-:|:-:|
+|change|组件状态发生变化触发|e={show: true|false,type:当前模式}|
+|maskClick|点击遮罩层触发|-|
+
+
+## 扩展组件说明
+`uni-popup` 其实并没有任何样式,只提供基础的动画效果,给用户一个弹出层解决方案,仅仅是这样并不能满足开发需求,所以我们提供了三种基础扩展样式
+
+### uni-popup-message 提示信息
+
+将 `uni-popup` 的`type`属性改为 `message`,并引入对应组件即可使用消息提示 ,*该组件不支持单独使用*
+
+**示例**
+
+```html
+<uni-popup ref="popup" type="message">
+	<uni-popup-message type="success" message="成功消息" :duration="2000"></uni-popup-message>
+</uni-popup>
+```
+
+### PopupMessage Props
+
+|属性名|类型|默认值|说明|
+|:-:|:-:|:-:|:-:|
+|type|String|success|消息提示主题|
+|message|String|-|消息提示文字|
+|duration|Number|3000|消息显示时间,超过显示时间组件自动关闭,设置为0 将不会关闭,需手动调用 close 方法关闭|
+
+#### Type Options
+
+|属性名|说明|
+|:-:| :-:|
+|success|成功	|
+|warn|警告|
+|error|失败|
+|info|消息|
+
+### PopupMessage Slots
+
+|名称|说明|					
+|:-:|:-:|						
+|default|消息内容,会覆盖 message 属性|
+
+### uni-popup-dialog 对话框
+
+将 `uni-popup` 的`type`属性改为 `dialog`,并引入对应组件即可使用对话框 ,*该组件不支持单独使用*
+
+**示例**
+
+```html
+<button @click="open">打开弹窗</button>
+<uni-popup ref="popup" type="dialog">
+	<uni-popup-dialog mode="input" message="成功消息" :duration="2000" :before-close="true" @close="close" @confirm="confirm"></uni-popup-dialog>
+</uni-popup>
+```
+
+```javascript
+export default {
+	methods: {
+		open() {
+			this.$refs.popup.open()
+		},
+		/**
+		 * 点击取消按钮触发
+		 * @param {Object} done
+		 */
+		close() {
+			// TODO 做一些其他的事情,before-close 为true的情况下,手动执行 close 才会关闭对话框
+			// ...
+			this.$refs.popup.close()
+		},
+		/**
+		 * 点击确认按钮触发
+		 * @param {Object} done
+		 * @param {Object} value
+		 */
+		confirm(value) {
+			// 输入框的值
+			console.log(value)
+			// TODO 做一些其他的事情,手动执行 close 才会关闭对话框
+			// ...
+			this.$refs.popup.close()
+		}
+	}
+}
+```
+
+### PopupDialog Props
+
+|属性名|类型|默认值|说明|
+|:-:|:-:|:-:|:-:|
+|type|String|success|对话框标题主题,可选值: success/warn/info/error|
+|mode|String|base| 对话框模式,可选值:base(提示对话框)/input(可输入对话框)|
+|title|String|-|对话框标题|
+|content|String|-|对话框内容,base模式下生效|
+|value| String\Number|-|输入框默认值,input模式下生效|
+|placeholder|String|-|输入框提示文字,input模式下生效|
+|before-close|Boolean|false	| 是否拦截按钮事件,如为true,则不会关闭对话框,关闭需要手动执行 uni-popup 的 close 方法|
+
+#### PopupDialog Events
+
+|事件称名	|说明|返回值|
+|:-:|:-:|:-:|
+|close|点击dialog取消按钮触发|-|
+|confirm|点击dialog确定按钮触发|e={value:input模式下输入框的值}|
+
+### PopupDialog Slots
+
+|名称|说明|					
+|:-:|:-:|						
+|default|自定义内容,回覆盖原有的内容显示|
+
+### uni-popup-share 分享示例
+
+分享示例,不作为最终可使用的组件,只做为样式组件,供用户自行修改,`后续的开发计划是实现实际的分享逻辑,参数可配置`。
+
+将 `uni-popup` 的 `type` 属性改为 `share`,并引入对应组件即可使用 ,*该组件不支持单独使用*
+
+**示例**
+
+```html
+<uni-popup ref="popup" type="share">
+	<uni-popup-share title="分享到" @select="select"></uni-popup-share>
+</uni-popup>
+```
+
+### PopupShare Props
+
+|属性名|类型|默认值|说明|
+|:-:|:-:|:-:| :-:			|
+|title|String|-|分享弹窗标题|
+|before-close|Boolean|false	| 是否拦截按钮事件,如为true,则不会关闭对话框,关闭需要手动执行 uni-popup 的 close 方法|
+
+### PopupShare Events
+
+|事件称名|说明|返回值|
+|:-:|:-:|:-:|
+|select|选择触发|e = {item,index}:所选参数|
+
+**Tips**
+- share 分享组件,只是作为一个扩展示例,如果需要修改数据源,请到组件内修改
+
+## 帮助
+在使用中如遇到无法解决的问题,请提 [Issues](https://github.com/dcloudio/uni-ui/issues) 给我们。
+
+
+
+## 组件示例
+
+点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/popup/popup](https://hellouniapp.dcloud.net.cn/pages/extUI/popup/popup)

+ 11 - 0
uni_modules/uni-transition/changelog.md

@@ -0,0 +1,11 @@
+## 1.1.1(2021-05-12)
+- 新增 示例地址
+- 修复 示例项目缺少组件的Bug
+## 1.1.0(2021-04-22)
+- 新增 通过方法自定义动画
+- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
+- 优化 动画触发逻辑,使动画更流畅
+- 优化 支持单独的动画类型
+- 优化 文档示例
+## 1.0.2(2021-02-05)
+- 调整为uni_modules目录规范

+ 128 - 0
uni_modules/uni-transition/components/uni-transition/createAnimation.js

@@ -0,0 +1,128 @@
+// const defaultOption = {
+// 	duration: 300,
+// 	timingFunction: 'linear',
+// 	delay: 0,
+// 	transformOrigin: '50% 50% 0'
+// }
+// #ifdef APP-NVUE
+const nvueAnimation = uni.requireNativePlugin('animation')
+// #endif
+class MPAnimation {
+	constructor(options, _this) {
+		this.options = options
+		this.animation = uni.createAnimation(options)
+		this.currentStepAnimates = {}
+		this.next = 0
+		this.$ = _this
+
+	}
+
+	_nvuePushAnimates(type, args) {
+		let aniObj = this.currentStepAnimates[this.next]
+		let styles = {}
+		if (!aniObj) {
+			styles = {
+				styles: {},
+				config: {}
+			}
+		} else {
+			styles = aniObj
+		}
+		if (animateTypes1.includes(type)) {
+			if (!styles.styles.transform) {
+				styles.styles.transform = ''
+			}
+			let unit = ''
+			if(type === 'rotate'){
+				unit = 'deg'
+			}
+			styles.styles.transform += `${type}(${args+unit}) `
+		} else {
+			styles.styles[type] = `${args}`
+		}
+		this.currentStepAnimates[this.next] = styles
+	}
+	_animateRun(styles = {}, config = {}) {
+		let ref = this.$.$refs['ani'].ref
+		if (!ref) return
+		return new Promise((resolve, reject) => {
+			nvueAnimation.transition(ref, {
+				styles,
+				...config
+			}, res => {
+				resolve()
+			})
+		})
+	}
+
+	_nvueNextAnimate(animates, step = 0, fn) {
+		let obj = animates[step]
+		if (obj) {
+			let {
+				styles,
+				config
+			} = obj
+			this._animateRun(styles, config).then(() => {
+				step += 1
+				this._nvueNextAnimate(animates, step, fn)
+			})
+		} else {
+			this.currentStepAnimates = {}
+			typeof fn === 'function' && fn()
+			this.isEnd = true
+		}
+	}
+
+	step(config = {}) {
+		// #ifndef APP-NVUE
+		this.animation.step(config)
+		// #endif
+		// #ifdef APP-NVUE
+		this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
+		this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
+		this.next++
+		// #endif
+		return this
+	}
+
+	run(fn) {
+		// #ifndef APP-NVUE
+		this.$.animationData = this.animation.export()
+		this.$.timer = setTimeout(() => {
+			typeof fn === 'function' && fn()
+		}, this.$.durationTime)
+		// #endif
+		// #ifdef APP-NVUE
+		this.isEnd = false
+		let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
+		if(!ref) return
+		this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
+		this.next = 0
+		// #endif
+	}
+}
+
+
+const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
+	'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
+	'translateZ'
+]
+const animateTypes2 = ['opacity', 'backgroundColor']
+const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
+animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
+	MPAnimation.prototype[type] = function(...args) {
+		// #ifndef APP-NVUE
+		this.animation[type](...args)
+		// #endif
+		// #ifdef APP-NVUE
+		this._nvuePushAnimates(type, args)
+		// #endif
+		return this
+	}
+})
+
+export function createAnimation(option, _this) {
+	if(!_this) return
+	clearTimeout(_this.timer)
+	return new MPAnimation(option, _this)
+}

+ 276 - 0
uni_modules/uni-transition/components/uni-transition/uni-transition.vue

@@ -0,0 +1,276 @@
+<template>
+	<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
+</template>
+
+<script>
+import { createAnimation } from './createAnimation'
+
+/**
+ * Transition 过渡动画
+ * @description 简单过渡动画组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=985
+ * @property {Boolean} show = [false|true] 控制组件显示或隐藏
+ * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
+ *  @value fade 渐隐渐出过渡
+ *  @value slide-top 由上至下过渡
+ *  @value slide-right 由右至左过渡
+ *  @value slide-bottom 由下至上过渡
+ *  @value slide-left 由左至右过渡
+ *  @value zoom-in 由小到大过渡
+ *  @value zoom-out 由大到小过渡
+ * @property {Number} duration 过渡动画持续时间
+ * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
+ */
+export default {
+	name: 'uniTransition',
+	props: {
+		show: {
+			type: Boolean,
+			default: false
+		},
+		modeClass: {
+			type: [Array, String],
+			default() {
+				return 'fade'
+			}
+		},
+		duration: {
+			type: Number,
+			default: 300
+		},
+		styles: {
+			type: Object,
+			default() {
+				return {}
+			}
+		},
+		customClass:{
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			isShow: false,
+			transform: '',
+			opacity: 1,
+			animationData: {},
+			durationTime: 300,
+			config: {}
+		}
+	},
+	watch: {
+		show: {
+			handler(newVal) {
+				if (newVal) {
+					this.open()
+				} else {
+					// 避免上来就执行 close,导致动画错乱
+					if (this.isShow) {
+						this.close()
+					}
+				}
+			},
+			immediate: true
+		}
+	},
+	computed: {
+		// 生成样式数据
+		stylesObject() {
+			let styles = {
+				...this.styles,
+				'transition-duration': this.duration / 1000 + 's'
+			}
+			let transform = ''
+			for (let i in styles) {
+				let line = this.toLine(i)
+				transform += line + ':' + styles[i] + ';'
+			}
+			return transform
+		},
+		// 初始化动画条件
+		transformStyles() {
+			return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
+		}
+	},
+	created() {
+		// 动画默认配置
+		this.config = {
+			duration: this.duration,
+			timingFunction: 'ease',
+			transformOrigin: '50% 50%',
+			delay: 0
+		}
+		this.durationTime = this.duration
+	},
+	methods: {
+		/**
+		 *  ref 触发 初始化动画
+		 */
+		init(obj = {}) {
+			if (obj.duration) {
+				this.durationTime = obj.duration
+			}
+			this.animation = createAnimation(Object.assign(this.config, obj))
+		},
+		/**
+		 * 点击组件触发回调
+		 */
+		onClick() {
+			this.$emit('click', {
+				detail: this.isShow
+			})
+		},
+		/**
+		 * ref 触发 动画分组
+		 * @param {Object} obj
+		 */
+		step(obj, config = {}) {
+			if (!this.animation) return
+			for (let i in obj) {
+				try {
+					if(typeof obj[i] === 'object'){
+						this.animation[i](...obj[i])
+					}else{
+						this.animation[i](obj[i])
+					}
+				} catch (e) {
+					console.error(`方法 ${i} 不存在`)
+				}
+			}
+			this.animation.step(config)
+			return this
+		},
+		/**
+		 *  ref 触发 执行动画
+		 */
+		run(fn) {
+			if (!this.animation) return
+			this.animation.run(fn)
+		},
+		// 开始过度动画
+		open() {
+			clearTimeout(this.timer)
+			this.transform = ''
+			this.isShow = true
+			let { opacity, transform } = this.styleInit(false)
+			if (typeof opacity !== 'undefined') {
+				this.opacity = opacity
+			}
+			this.transform = transform
+			// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
+			this.$nextTick(() => {
+				// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
+				this.timer = setTimeout(() => {
+					this.animation = createAnimation(this.config, this)
+					this.tranfromInit(false).step()
+					this.animation.run()
+					this.$emit('change', {
+						detail: this.isShow
+					})
+				}, 20)
+			})
+		},
+		// 关闭过度动画
+		close(type) {
+			if (!this.animation) return
+			this.tranfromInit(true)
+				.step()
+				.run(() => {
+					this.isShow = false
+					this.animationData = null
+					this.animation = null
+					let { opacity, transform } = this.styleInit(false)
+					this.opacity = opacity || 1
+					this.transform = transform
+					this.$emit('change', {
+						detail: this.isShow
+					})
+				})
+		},
+		// 处理动画开始前的默认样式
+		styleInit(type) {
+			let styles = {
+				transform: ''
+			}
+			let buildStyle = (type, mode) => {
+				if (mode === 'fade') {
+					styles.opacity = this.animationType(type)[mode]
+				} else {
+					styles.transform += this.animationType(type)[mode] + ' '
+				}
+			}
+			if (typeof this.modeClass === 'string') {
+				buildStyle(type, this.modeClass)
+			} else {
+				this.modeClass.forEach(mode => {
+					buildStyle(type, mode)
+				})
+			}
+			return styles
+		},
+		// 处理内置组合动画
+		tranfromInit(type) {
+			let buildTranfrom = (type, mode) => {
+				let aniNum = null
+				if (mode === 'fade') {
+					aniNum = type ? 0 : 1
+				} else {
+					aniNum = type ? '-100%' : '0'
+					if (mode === 'zoom-in') {
+						aniNum = type ? 0.8 : 1
+					}
+					if (mode === 'zoom-out') {
+						aniNum = type ? 1.2 : 1
+					}
+					if (mode === 'slide-right') {
+						aniNum = type ? '100%' : '0'
+					}
+					if (mode === 'slide-bottom') {
+						aniNum = type ? '100%' : '0'
+					}
+				}
+				this.animation[this.animationMode()[mode]](aniNum)
+			}
+			if (typeof this.modeClass === 'string') {
+				buildTranfrom(type, this.modeClass)
+			} else {
+				this.modeClass.forEach(mode => {
+					buildTranfrom(type, mode)
+				})
+			}
+
+			return this.animation
+		},
+		animationType(type) {
+			return {
+				fade: type ? 1 : 0,
+				'slide-top': `translateY(${type ? '0' : '-100%'})`,
+				'slide-right': `translateX(${type ? '0' : '100%'})`,
+				'slide-bottom': `translateY(${type ? '0' : '100%'})`,
+				'slide-left': `translateX(${type ? '0' : '-100%'})`,
+				'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
+				'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
+			}
+		},
+		// 内置动画类型与实际动画对应字典
+		animationMode() {
+			return {
+				fade: 'opacity',
+				'slide-top': 'translateY',
+				'slide-right': 'translateX',
+				'slide-bottom': 'translateY',
+				'slide-left': 'translateX',
+				'zoom-in': 'scale',
+				'zoom-out': 'scale'
+			}
+		},
+		// 驼峰转中横线
+		toLine(name) {
+			return name.replace(/([A-Z])/g, '-$1').toLowerCase()
+		}
+	}
+}
+</script>
+
+<style></style>

+ 83 - 0
uni_modules/uni-transition/package.json

@@ -0,0 +1,83 @@
+{
+  "id": "uni-transition",
+  "displayName": "uni-transition 过渡动画",
+  "version": "1.1.1",
+  "description": "元素的简单过渡动画",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "动画",
+    "过渡",
+    "过渡动画"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "category": [
+      "前端组件",
+      "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 397 - 0
uni_modules/uni-transition/readme.md

@@ -0,0 +1,397 @@
+
+
+## Transition 过渡动画
+> **组件名:uni-transition**
+> 代码块: `uTransition`
+
+
+元素过渡动画
+
+> **注意事项**
+> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
+> - 组件需要依赖 `sass` 插件 ,请自行手动安装
+> - rotate 旋转动画不需要填写 deg 单位,在小程序上填写单位动画不会执行
+> - NVUE 下修改宽高动画,不能定位到中心点
+> - 百度小程序下修改宽高 ,可能会影响其他动画,需注意
+> - nvue 不支持 costom-class , 请使用 styles
+> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
+
+
+### 安装方式
+
+本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
+
+如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
+
+### 基本用法
+
+在 ``template`` 中使用组件
+
+```html
+<template>
+	<view>
+		<button type="primary" @click="open">fade</button>
+		<uni-transition mode-class="fade" :styles="{'width':'100px','height':'100px','backgroundColor':'red'}" :show="show" @change="change" />
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			show: false,
+		}
+	},
+	onLoad() {},
+	methods: {
+		open(mode) {
+			this.show = !this.show
+		},
+		change() {
+			console.log('触发动画')
+		}
+	}
+}
+</script>
+```
+
+### 样式覆盖
+
+**注意:`nvue` 不支持 `custom-class` 属性 ,需要使用 `styles` 属性进行兼容**
+
+使用 `custom-class` 属性绑定样式,可以自定义 `uni-transition` 的样式
+
+```html
+<template>
+	<view class="content">
+		<uni-transition custom-class="custom-transition" mode-class="fade" :duration="0" :show="true" />
+	</view>
+</template>
+
+<style>
+/* 常规样式覆盖  */
+.content >>> .custom-transition {
+	width:100px;
+	height:100px;
+	background-color:red;
+}
+</style>
+<style lang="scss">
+/* 如果使用 scss 需要使用 /deep/  */
+.content /deep/ .custom-transition {
+	width:100px;
+	height:100px;
+	background-color:red;
+}
+</style>
+```
+
+
+如果使用 `styles` 注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
+
+```html
+<template>
+	<view class="content">
+		<uni-transition :styles="styles" mode-class="fade" :duration="0" :show="true" />
+	</view>
+</template>
+<script>
+export default {
+	data() {
+		return {
+			styles:{
+				'width':'100px',
+				'height':'100px',
+				'backgroundColor':'red'
+			}
+		}
+	}
+}
+</script>
+```
+
+### 自定义动画
+当内置动画类型不能满足需求的时候 ,可以使用 `step()` 和 `run()` 自定义动画,入参以及具体用法参考下方属性说明
+
+`init()` 方法可以覆盖默认配置
+
+
+```html
+<template>
+	<view>
+		<button type="primary" @click="run">执行动画</button>
+		<uni-transition ref="ani" :styles="{'width':'100px','height':'100px','backgroundColor':'red'}" :show="show"  />
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			show: true,
+		}
+	},
+	onReady() {
+		this.$refs.ani.init({
+			duration: 1000,
+			timingFunction: 'linear',
+			transformOrigin: '50% 50%',
+			delay: 500
+		})
+	},
+	methods: {
+		run() {
+			// 同时右平移到 100px,旋转 360 读
+			this.$refs.ani.step({
+				translateX: '100px',
+				rotate: '360'
+			})
+			// 上面的动画执行完成后,等待200毫秒平移到 0px,旋转到 0 读
+			this.$refs.ani.step({
+				translateX: '0px',
+				rotate: '0'
+			},
+			{
+				timingFunction: 'ease-in',
+				duration: 200
+			})
+			// 开始执行动画
+			this.$refs.ani.run(()=>{
+				console.log('动画支持完毕')
+			})
+		}
+	}
+}
+</script>
+```
+
+
+## API
+
+### Transition Props
+
+|属性名		|类型	|默认值	|说明					|
+|:-:	|:-:	|:-:					|:-:|
+|show				|Boolean|false	|控制组件显示或隐藏	|
+|mode-class |Array/String	|-		|内置过渡动画类型			|
+|custom-class |String	|-		|自定义类名	|
+|duration	 |Number	|300	|过渡动画持续时间		|
+|styles		 |Object	|-		|组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`	|
+
+#### mode-class 内置过渡动画类型说明
+**格式为** :`'fade'` 或者 `['fade','slide-top']`
+
+|属性名			|说明			|
+|:-:			|:-:			|
+|fade			|渐隐渐出过渡	|
+|slide-top		|由上至下过渡	|
+|slide-right	|由右至左过渡	|
+|slide-bottom	|由下至上过渡	|
+|slide-left		|由左至右过渡	|
+|zoom-in		|由小到大过渡	|
+|zoom-out		|由大到小过渡	|
+
+**注意** 
+
+组合使用时,同一种类型相反的过渡动画如(slide-top、slide-bottom)同时使用时,只有最后一个生效
+
+### Transition Events
+
+|事件名		|说明				|返回值			|
+|:-:			|:-:				|:-:			|
+|click		|点击组件触发		|-				|
+|change		|过渡动画结束时触发	| e = {detail:true}	|
+
+### Transition Methons
+
+|方法名|说明|参数|
+|:-:|:-:|:-:|
+|init()|手动初始化配置|Function(OBJECT:config)|
+|step()|动画队列|Function(OBJECT:type,OBJECT:config)|
+|run()|执行动画|Function(FUNCTION:callback)	|
+
+### init(OBJECT:config)
+**通过 ref 调用方法**
+
+手动设置动画配置,需要在页面渲染完毕后调用
+
+```javascript
+this.$refs.ani.init({
+	duration: 1000,
+	timingFunction:'ease',
+	delay:500,
+	transformOrigin:'left center'
+})
+```
+
+### step(OBJECT:type,OBJECT:config) 动画队列
+**通过 ref 调用方法**
+
+调用 `step()` 来表示一组动画完成,`step` 第一个参数可以传入任意多个动画方法,一组动画中的所有动画会同时开始,一组动画完成后才会进行下一组动画。`step` 第二个参数可以传入一个跟 `uni.createAnimation()` 一样的配置参数用于指定当前组动画的配置。
+
+Tips
+- 第一个参数支持的动画参考下面的 `支持的动画`
+- 第二个参数参考下面的 `动画配置`,可省略,如果省略继承`init`的配置
+
+
+```javascript
+this.$refs.ani.step({
+	translateX: '100px'
+},{
+	duration: 1000,
+	timingFunction:'ease',
+	delay:500,
+	transformOrigin:'left center'
+})
+```
+
+### run(FUNCTION:callback) 执行动画
+**通过 ref 调用方法**
+
+在执行 `step()` 后,需要调用 `run()` 来运行动画 ,否则动画会一直等待
+
+`run()` 方法可以传入一个 `callback` 函数 ,会在所有动画执行完毕后回调
+
+```javascript
+this.$refs.ani.step({
+	translateX: '100px'
+})
+this.$refs.ani.run(()=>{
+	console.log('动画执行完毕')
+})
+
+```
+
+### 动画配置
+动画配置 , `init()` 与 `step()` 第二个参数配置相同 ,如果配置`step() `第二个参数,将会覆盖 `init()` 的配置
+
+|属性名|值|必填|默认值|说明|平台差异|
+|:-:|:-:|:-:|:-:|:-:|:-:|
+|duration|Number|否|400|动画持续时间,单位ms|-|
+|timingFunction|String|否|"linear"|定义动画的效果|-|
+|delay|Number|否|0|动画延迟时间,单位 ms|-|
+|needLayout|Boolean|否|false	|动画执行是否影响布局|仅 nvue 支持|
+|transformOrigin|String	|否|"center center"|设置	[transform-origin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin)|-|
+
+
+### timingFunction 属性说明
+
+|值|说明|平台差异|
+|:-:|:-:|:-:|
+|linear|动画从头到尾的速度是相同的|-|
+|ease|动画以低速开始,然后加快,在结束前变慢|-|
+|ease-in|	动画以低速开始|-|
+|ease-in-out|	动画以低速开始和结束|-|
+|ease-out|动画以低速结束|-|
+|step-start|动画第一帧就跳至结束状态直到结束|nvue不支持|
+|step-end|动画一直保持开始状态,最后一帧跳到结束状态|nvue不支持|
+
+```javascript
+// init 配置
+this.$refs.ani.init({
+	duration: 1000,
+	timingFunction:'ease',
+	delay:500,
+	transformOrigin:'left center'
+})
+// step 配置
+this.$refs.ani.step({
+	translateX: '100px'
+},{
+	duration: 1000,
+	timingFunction:'ease',
+	delay:500,
+	transformOrigin:'left center'
+})
+```
+
+### 支持的动画
+动画方法 
+
+如果同一个动画方法有多个值,多个值使用数组分隔
+
+```javascript
+this.$refs.ani.step({
+	width:'100px',
+	scale: [1.2,0.8],
+})
+```
+
+**样式:**
+
+|属性名|值|说明|平台差异|
+|:-:|:-:|:-:|:-:|
+|opacity|value|透明度,参数范围 0~1|-|
+|backgroundColor|color|颜色值|-|
+|width|length|长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值|-|
+|height|length|长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值|-|
+|top|length|长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值|nvue 不支持|
+|left|length|长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值|nvue 不支持|
+|bottom|length|长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值|nvue 不支持|
+|right|length|长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值|nvue 不支持|
+
+```javascript
+this.$refs.ani.step({
+	opacity: 1,
+	backgroundColor: '#ff5a5f',
+	widht:'100px',
+	height:'50rpx',
+})
+```
+
+**旋转:**
+
+旋转属性的值不需要填写单位
+
+|属性名|值|说明|平台差异	|
+|:-:|:-:|:-:|:-:|
+|rotate|deg|deg的范围-180~180,从原点顺时针旋转一个deg角度	|-|
+|rotateX|deg|deg的范围-180~180,在X轴旋转一个deg角度				|-|
+|rotateY|deg|deg的范围-180~180,在Y轴旋转一个deg角度				|-|
+|rotateZ|deg|deg的范围-180~180,在Z轴旋转一个deg角度				|nvue不支持|
+|rotate3d|x,y,z,deg| 同	[transform-function rotate3d](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d())		|nvue不支持|
+
+```javascript
+this.$refs.ani.step({
+	rotateX: 45,
+	rotateY: 45
+})
+```
+
+**缩放:**
+
+|属性名|值|说明|平台差异|
+|:-:|:-:|:-:	|:-:|
+|scale|sx,[sy]|一个参数时,表示在X轴、Y轴同时缩放sx倍数;两个参数时表示在X轴缩放sx倍数,在Y轴缩放sy倍数|-|
+|scaleX|sx|在X轴缩放sx倍数|-|
+|scaleY|sy|在Y轴缩放sy倍数|-|
+|scaleZ|sz|在Z轴缩放sy倍数|nvue不支持|
+|scale3d|sx,sy,sz|在X轴缩放sx倍数,在Y轴缩放sy倍数,在Z轴缩放sz倍数|nvue不支持|
+
+```javascript
+this.$refs.ani.step({
+	scale: [1.2,0.8]
+})
+```
+
+**偏移:**
+
+|属性名|值|说明|平台差异|
+|:-:|:-:|:-:|:-:|
+|translate|tx,[ty]|一个参数时,表示在X轴偏移tx,单位px;两个参数时,表示在X轴偏移tx,在Y轴偏移ty,单位px。|-|
+|translateX|tx|	在X轴偏移tx,单位px|-|
+|translateY|ty|	在Y轴偏移tx,单位px|-|
+|translateZ|tz|	在Z轴偏移tx,单位px|nvue不支持|
+|translate3d|tx,ty,tz|	在X轴偏移tx,在Y轴偏移ty,在Z轴偏移tz,单位px|nvue不支持|
+
+```javascript
+this.$refs.ani.step({
+	translateX: '100px'
+})
+```
+
+
+
+## 组件示例
+
+点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/transition/transition](https://hellouniapp.dcloud.net.cn/pages/extUI/transition/transition)