Ver Fonte

盆友圈

achao há 2 anos atrás
pai
commit
e356efe06e

+ 9 - 0
xiaochengxu/README.md

@@ -223,3 +223,12 @@ static/styles/index.scss
 ```
 @click.native.stop="delSearchVal"
 ```
+## showToast
+
+```
+uni.showToast({
+	icon: "success",
+	title: '保存成功!',
+	duration: 2000
+});
+```

+ 52 - 0
xiaochengxu/common/util.js

@@ -0,0 +1,52 @@
+function friendlyDate(timestamp) {
+	var formats = {
+		'year': '%n% 年前',
+		'month': '%n% 月前',
+		'day': '%n% 天前',
+		'hour': '%n% 小时前',
+		'minute': '%n% 分钟前',
+		'second': '%n% 秒前',
+	};
+
+	var now = Date.now();
+	var seconds = Math.floor((now - timestamp) / 1000);
+	var minutes = Math.floor(seconds / 60);
+	var hours = Math.floor(minutes / 60);
+	var days = Math.floor(hours / 24);
+	var months = Math.floor(days / 30);
+	var years = Math.floor(months / 12);
+
+	var diffType = '';
+	var diffValue = 0;
+	if (years > 0) {
+		diffType = 'year';
+		diffValue = years;
+	} else {
+		if (months > 0) {
+			diffType = 'month';
+			diffValue = months;
+		} else {
+			if (days > 0) {
+				diffType = 'day';
+				diffValue = days;
+			} else {
+				if (hours > 0) {
+					diffType = 'hour';
+					diffValue = hours;
+				} else {
+					if (minutes > 0) {
+						diffType = 'minute';
+						diffValue = minutes;
+					} else {
+						diffType = 'second';
+						diffValue = seconds === 0 ? (seconds = 1) : seconds;
+					}
+				}
+			}
+		}
+	}
+	return formats[diffType].replace('%n%', diffValue);
+}
+export {
+	friendlyDate
+}

+ 8 - 5
xiaochengxu/components/ossutil/uploadFile.js

@@ -12,7 +12,7 @@ const Crypto = require('./crypto.js');
  *@param - successc:成功回调
  *@param - failc:失败回调
  */ 
-const uploadFile = function (filePath, dir, successc, failc) {
+const uploadFile = function (filePath, dir, successc, failc,type) {
   if (!filePath || filePath.length < 9) {
     uni.showModal({
       title: '图片错误',
@@ -21,9 +21,12 @@ const uploadFile = function (filePath, dir, successc, failc) {
     })
     return;
   }
-  //图片名字 可以自行定义,     这里是采用当前的时间戳 + 150内的随机数来给图片命名的
-  const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
-  
+   let aliyunFileKey=''
+  if(type=="image"){
+	     aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
+  }else{
+	    aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.mp4';
+  }  
   const aliyunServerURL = env.uploadImageUrl;//OSS地址,需要https
   const accessid = env.OSSAccessKeyId;
   const policyBase64 = getPolicyBase64();
@@ -41,7 +44,7 @@ const uploadFile = function (filePath, dir, successc, failc) {
       'success_action_status': '200',
     },
     success: function (res) {
-			console.log(res);
+			console.log("文件地址",res);
       if (res.statusCode != 200) {
         failc(new Error('上传错误:' + JSON.stringify(res)))
         return;

+ 2 - 2
xiaochengxu/config/index.js

@@ -1,8 +1,8 @@
 const dev = {
 	// baseUrlNew: 'http://192.168.110.72:8182',
-	// baseUrlNew: 'http://192.168.110.138:8182',
+	baseUrlNew: 'http://192.168.110.138:8182',
 	// baseUrlNew: 'http://192.168.110.82:8182',
-	baseUrlNew: 'https://cardapi.eliangeyun.com',
+	// baseUrlNew: 'https://cardapi.eliangeyun.com',
 	h5Appid: 'wxb66b599f7f61b46f',
 	debug: false
 }

+ 18 - 0
xiaochengxu/pages.json

@@ -210,6 +210,24 @@
             }
             
         }
+        ,{
+            "path" : "pages/circle/friendSCirlce",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "动态",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        ,{
+            "path" : "pages/circle/addFriendCirlce",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "添加动态",
+                "enablePullDownRefresh": false
+            }
+            
+        }
     ],
 	"tabBar": {
 		"custom": false,

+ 155 - 0
xiaochengxu/pages/circle/addFriendCirlce.vue

@@ -0,0 +1,155 @@
+<template>
+	<view class="content">
+		<view class="row1">
+			<u-input placeholder="这一刻得想法..." focus :border="false" v-model="formData.content"></u-input>
+		</view>
+		<view class="row2">
+			<cl-upload v-model="fileList" @onSuccess="onSuccess" @showType='getShowType($event)'></cl-upload>
+		</view>
+		<view class="row3 flex flex-between" @click="selectPlace">
+			<view class="left flex">
+				<u-icon name="map-fill" color="#112253" size="18"></u-icon>
+				<text style="margin:0 20rpx;">{{formData.location?formData.location:'所在位置'}}</text>
+			</view>
+			<view class="right">
+				<u-icon name="arrow-right" color="#112253" size="20"></u-icon>
+			</view>
+		</view>
+		<view class="row4 flex flex-between">
+			<view class="left flex">
+				<u-icon name="pushpin" color="#112253" size="18"></u-icon>
+				<text style="margin:0 20rpx;">开启评论</text>
+				<u-switch v-model="switchStatus" activeColor="#112253" @change="change($event,1)" size="20"></u-switch>
+			</view>
+			<view class="right">
+				<u-icon name="arrow-right" color="#112253" size="20"></u-icon>
+			</view>
+		</view>
+		<view @click="submit" class="submit">提交</view>
+	</view>
+</template>
+
+<script>
+	var that;
+	import uploadImage from '@/components/ossutil/uploadFile.js';
+	export default {
+		data() {
+			return {
+				userInfo: {},
+				switchStatus: true,
+				fileList: [],
+				formData: {
+					commonId: '',
+					head: '',
+					nickname: '',
+					content: '',
+					image: '',
+					location: '',
+					commentFlag: '',
+					positioning:'',
+					circleId:'',
+					mediaType:''
+					
+				}
+			};
+		},
+		onShow() {
+			this.userInfo = uni.getStorageSync("userInfo")
+		},
+		onLoad(options) {
+			that = this
+			this.formData.circleId = options.id
+		},
+		methods: {
+			getShowType(type){debugger
+			console.log("文件类型",type)
+				this.mediaType = type
+			},
+			onSuccess(reslut) {
+				// 把服务端返回的图片地址添加到list中与组件数据同步
+				this.fileList.push(reslut)
+			},
+			submit() {debugger
+				this.formData.commonId = this.userInfo.id
+				this.formData.head = this.userInfo.head
+				this.formData.nickname = this.userInfo.nickname
+				this.formData.image = this.fileList.toString()
+				this.formData.mediaType = this.mediaType
+				if (this.switchStatus) {
+					this.formData.commentFlag  = 1
+				} else {
+					this.formData.commentFlag  = 0
+				}
+				if (!this.formData.content) {
+					uni.showToast({
+						icon: "none",
+						title: '内容不能为空!',
+						duration: 2000
+					});
+					return
+				}
+				this.$request.baseRequest('admin.unimall.circleFriendsInfo', 'add', {
+					circleFriendsInfo: JSON.stringify(this.formData)
+				}, failres => {
+					uni.showToast({
+						icon: "none",
+						title: failres.errmsg,
+						duration: 3000
+					});
+
+					uni.hideLoading()
+				}).then(res => {
+					debugger
+				})
+				uni.showToast({
+					icon: "success",
+					title: '保存成功!',
+					duration: 2000
+				});
+				setTimeout(() => {
+					uni.navigateBack()
+				}, 2000)
+			},
+			selectPlace() {
+				uni.chooseLocation({
+					success: function(res) {
+						that.formData.location = res.address
+						console.log('位置名称:' + res.name);
+						console.log('详细地址:' + res.address);
+						console.log('纬度:' + res.latitude);
+						console.log('经度:' + res.longitude);
+					}
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.content {
+		background-color: #fff;
+		padding: 40rpx;
+
+		.row2,
+		.row3 {
+			border-bottom: 1px solid #E6E6E6;
+			padding: 30rpx 0;
+		}
+
+		.row4 {
+			padding-top: 30rpx;
+		}
+	}
+
+	.submit {
+		position: fixed;
+		bottom: 100rpx;
+		background: #112253;
+		color: #fff;
+		width: calc(100% - 80rpx);
+		padding: 20rpx;
+		box-sizing: border-box;
+		text-align: center;
+		border-radius: 20rpx;
+	}
+</style>

+ 515 - 0
xiaochengxu/pages/circle/circle-item.vue

@@ -0,0 +1,515 @@
+<template>
+	<!-- <mescroll-uni :ref="'mescrollRef' + i" @init="mescrollInit" :height="height" :down="downOption" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick" @scroll="scroll"> -->
+	<view class="u-demo-block">
+		<view class="u-demo-block__content" v-for="(item, index) in list" :key="index" @touchstart="click">
+			<view class="album" style="padding: 30upx;">
+					<image :src="item.head" mode="" style="width: 80upx;height: 80upx;border-radius: 10rpx;"></image>
+				<view class="album__content">
+					<view class="album__info">
+						<text class="info-name" @click="toDetail(item)">{{ item.nickname }}</text>
+						<text class="info-content" @click="toDetail(item)">{{ item.content }}</text>
+						<template v-if="item.mediaType == 1">
+							<u-album v-if="item.urlList && item.urlList.length > 0" :urls="item.urlList" multipleSize="90" space='10'></u-album>
+							<u-album v-else :urls="item.urlList"></u-album>
+						</template>
+						<view v-else class="video">
+								<video v-show="isShowVideo"
+									class="video-info"
+									:show-center-play-btn="false"
+									:src="item.image"
+									id="myVideo"
+									:autoplay="true"
+									:loop="true"
+									:controls= "true"
+								></video>
+							<view v-if="item.direction==1&&!isShowVideo" class="relative">
+								<image :src="item.image + '?x-oss-process=video/snapshot,t_1000,f_jpg,w_800,h_600,m_fast,ar_auto'" mode="aspectFill" style="width: 424rpx;height:320rpx"></image>
+								<image class="cover" src="/static/play.png" @click="playVideo"></image>
+							</view>
+							<view v-if="item.direction==2&&!isShowVideo" class="relative">
+								<image :src="item.image + '?x-oss-process=video/snapshot,t_1000,f_jpg,w_800,h_600,m_fast,ar_auto'" mode="aspectFill" style="width: 320rpx;height:424rpx"></image>
+								<image class="cover1" src="/static/play.png" @click="playVideo"></image>
+							</view>
+						</view>
+					</view>
+					<view class="location" v-if="item.location">
+						<view class="location-left">
+							<text class="location-left-name">{{ item.location }}</text>
+						</view>
+					</view>
+					<view class="time">
+							<text class="time-text">{{$u.timeFrom(new Date(item.gmtCreate).getTime(),'yyyy年mm月dd日')}}</text>
+						</view>
+					<view class="comment">
+						<text class="time-text">{{ item.dateTime }}</text>
+						<view class="comment-right">
+							<view class="comment-item" @click="doThumb(item, index)">
+								<image class="image-love" :src="item.helpFlag==1 ? '../../static/love-fill.png' : '../../static/love.png'"></image>
+								<text class="number">{{ item.count }}</text>
+							</view>
+							<view class="comment-item" @click="doComment(item, null, index)">
+								<image class="image-comment" src="../../static/comment.png"></image>
+								<text class="number">{{ item.count1 }}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="thumb-comment" v-if="item.circleFriendsDetailList&&(item.circleFriendsDetailList.length > 0 || item.comments.circleFriendsDetailList.length > 0)">
+				<view class="thumbs" v-if="item.circleFriendsDetailList&&item.circleFriendsDetailList.length > 0">
+					<image class="image-love thumbs-icon" src="../../static/love.png"></image>
+					<view class="thumbs-headers">
+						<view class="thumbs-headers-img" v-for="(item2, index2) in item.circleFriendsDetailList" :key="index2">
+							<view class="thumbs-item">
+								<text class="thumbs-name">{{ item2.nickname }}</text>
+								<text class="thumbs-name" v-if="index2 != item.circleFriendsDetailList.length - 1">,</text>
+							</view>
+						</view>
+						<text class="thumbs-headers-img thumbs-headers-more" v-if="item.circleFriendsDetailList.length > 20">等</text>
+					</view>
+				</view>
+			<!-- 	<view class="comments" v-if="item.comments.records.length > 0">
+					<image class="image-comment comments-icon" src="../../static/comment.png"></image>
+					<view class="comments-headers">
+						<view class="comments-headers-item" v-for="(item3, index3) in item.comments.records" :key="index3">
+							<view class="item-left">
+								<u-avatar :src="item3.userAvatar" shape="square" size="32" @click="onJump('/pages/friends/friendInfo?friendId=' + item3.userId)"></u-avatar>
+							</view>
+							<view class="item-right">
+								<view class="item-right-name">
+									<text class="item-right-name-top">{{ item3.userName }}</text>
+									<text class="item-right-name-bottom">{{ item3.createTime }}</text>
+								</view>
+								<view class="comment-content">
+									<view class="comment-response" v-if="item3.comUserName">
+										<text class="comment-response-txt">回复</text>
+										<text class="comment-response-name">{{ item3.comUserName }}</text>
+										<text class="comment-response-txt">:</text>
+									</view>
+									<text class="item-right-info" @click="doComment(item, item3, index)">{{ item3.comment }}</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view> -->
+			</view>
+			<u-line></u-line>
+		</view>
+	</view>
+	<!-- </mescroll-uni> -->
+</template>
+
+<script>
+import { mapState, mapMutations } from 'vuex';
+import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
+import MescrollMoreItemMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-more-item.js';
+// import { apiGoods } from '@/api/mock.js';
+
+export default {
+	mixins: [MescrollMixin, MescrollMoreItemMixin], // 注意此处还需使用MescrollMoreItemMixin (必须写在MescrollMixin后面)
+	data() {
+		return {
+			isShowVideo:false,
+			videoContext:'',
+			downOption: {
+				auto: false // 不自动加载 (mixin已处理第一个tab触发downCallback)
+			},
+			upOption: {
+				onScroll: true,
+				auto: false, // 不自动加载
+				// page: {
+				// 	num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+				// 	size: 10 // 每页数据的数量
+				// },
+				noMoreSize: 5, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
+				empty: {
+					tip: '空空如也', // 提示
+					icon: 'http://cdn.uviewui.com/uview/empty/data.png'
+				}
+			},
+			showInput: false
+		};
+	},
+	props: {
+		list: {
+			type: Array,
+			default: function(e) {
+				return [];
+			}
+		}
+	},
+
+	computed: {
+		...mapState(['locateInformation'])
+	},
+
+	created() {},
+
+	filters: {
+		formatDistance(distance) {
+			var strDistance = '';
+			if (distance < 1) {
+				//一公里以内的
+				strDistance = distance * 1000 + 'm';
+			} else {
+				distance = distance.toFixed(1);
+				strDistance = distance + 'km';
+			}
+			return strDistance;
+		}
+	},
+
+	methods: {
+		playVideo(val) {
+			this.isShowVideo = true
+			console.log('--play--');
+			// 点击显示全屏
+			this.videoContext = uni.createVideoContext('myVideo')
+			this.videoContext.requestFullScreen();
+		},
+
+		onJump(url) {
+			uni.navigateTo({
+				url: url
+			});
+		},
+
+		toDetail(item) {
+			uni.navigateTo({
+				url: '/pages/circle/circleDetail?item=' + JSON.stringify(item)
+			});
+		},
+
+		click() {
+			this.$emit('click');
+		},
+
+		doThumb(item, index) {
+			item.index = index;
+			this.$emit('doThumb', item);
+		},
+
+		doComment(item, comment, index) {
+			item.index = index;
+			this.$emit('doComment', item, comment);
+		},
+
+		doComment2Com(item, comment) {
+			this.$emit('doComment2Com', item, comment);
+		}
+	}
+};
+</script>
+<style lang="scss">
+.view {
+	flex-direction: column;
+}
+.album {
+	display: flex;
+	flex-direction: row;
+	align-items: flex-start;
+}
+.album__avatar {
+	background-color: #fff;
+	padding: 5px;
+	border-radius: 3px;
+}
+.album__content {
+	margin-left: 10px;
+	flex: 1;
+}
+.album__info {
+	margin-bottom: 15upx;
+	display: flex;
+	flex-direction: column;
+}
+.info-name {
+	color: #5786cc;
+	font-size: 30upx;
+	font-weight: bold;
+}
+.info-content {
+	color: #333;
+	font-size: 30upx;
+	padding: 5px 0 8px 0;
+}
+.video {
+	position: relative;
+	image {
+		width: 100%;
+		height: 180px;
+	}
+	.cover {
+	width: 50px;
+	    height: 50px;
+	    position: absolute;
+	    z-index: 5;
+	    left: -180rpx;
+	    right: 0;
+	    bottom: 0;
+	    top: 0;
+	    margin: auto;
+	}
+	.cover1 {
+	width: 50px;
+	    height: 50px;
+	    position: absolute;
+	    z-index: 5;
+	    left: -270rpx;
+	    right: 0;
+	    bottom: 0;
+	    top: 0;
+	    margin: auto;
+	}
+}
+.video-info {
+	width: 280px;
+	height: 200px;
+}
+.video-cover {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 270px;
+	height: 200px;
+}
+.location {
+}
+.location-left {
+	display: flex;
+	flex-direction: row;
+}
+.location-left-name {
+	font-size: 24upx;
+	color: #5786cc;
+}
+.location-left-distance {
+	margin-left: 30upx;
+	font-size: 24upx;
+	color: #5786cc;
+}
+
+.location-right {
+	color: #999;
+	font-size: 24upx;
+}
+
+.time-text {
+	color: #666;
+	font-size: 24upx;
+}
+
+.comment {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	justify-content: space-between;
+	margin-top: 15upx;
+}
+
+.comment-right {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	justify-content: center;
+}
+
+.comment-item {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	margin-left: 40upx;
+	color: #666;
+}
+
+.image-love {
+	width: 32upx;
+	height: 32upx;
+	margin-right: 6upx;
+}
+.image-comment {
+	width: 30upx;
+	height: 30upx;
+	margin-right: 8upx;
+}
+
+.number {
+	color: #666;
+	padding-left: 5upx;
+	font-size: 26upx;
+}
+.yzb {
+	margin-right: 8upx;
+}
+.yzb-pinglun1 {
+}
+.thumb-on {
+	font-size: 32upx;
+	color: red;
+}
+.thumb-off {
+	font-size: 32upx;
+}
+
+.thumb-comment {
+	padding: 20upx;
+}
+.thumbs {
+	padding: 20upx;
+	border-radius: 10upx;
+	background-color: #f8f8f8;
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+}
+.thumbs-icon {
+	margin-right: 10upx;
+	color: #666;
+	/* margin-top: 5upx; */
+}
+
+.thumbs-headers {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	overflow: hidden;
+	white-space: nowrap;
+	flex-wrap: wrap;
+	flex: 1;
+}
+.thumbs-headers-img {
+	margin-left: 10upx;
+	margin-bottom: 10upx;
+}
+
+.thumbs-headers-more {
+	font-size: 30upx;
+	color: #999;
+}
+
+.thumbs-item {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+}
+
+.thumbs-name {
+	font-size: 28upx;
+	color: #5786cc;
+}
+
+.comments {
+	margin-top: 1upx;
+	padding: 20upx;
+	border-radius: 10upx;
+	background-color: #f8f8f8;
+	display: flex;
+	flex-direction: row;
+}
+.comments-icon {
+	margin-right: 10upx;
+	color: #666;
+	margin-top: 15upx;
+	font-size: 24upx;
+}
+.comments-headers {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+}
+.comments-headers-item {
+	flex: 1;
+	display: flex;
+	flex-direction: row;
+	/* align-items: center; */
+	margin-bottom: 20upx;
+}
+.item-left {
+	margin: 0 10upx;
+	margin-top: 10upx;
+}
+.item-right {
+	margin-left: 5upx;
+	display: flex;
+	flex: 1;
+	flex-direction: column;
+}
+.item-right-name {
+	display: flex;
+	flex: 1;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: center;
+}
+.item-right-name-top {
+	color: #5786cc;
+	font-size: 26upx;
+}
+.item-right-name-bottom {
+	font-size: 24upx;
+	color: #999;
+	margin-top: 10upx;
+}
+.item-right-info {
+	font-size: 28upx;
+	color: #333;
+	flex: 1;
+}
+
+.comment-content {
+	display: flex;
+	flex-direction: row;
+	align-items: flex-start;
+	margin-top: 3upx;
+	flex: 1;
+}
+.comment-response {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	margin-right: 10upx;
+}
+.comment-response-txt {
+	font-size: 28upx;
+}
+.comment-response-name {
+	font-size: 28upx;
+	color: #5786cc;
+	padding: 0 8upx;
+}
+
+.bottom {
+	bottom: 0;
+	position: fixed;
+	height: 120rpx;
+	width: 100%;
+	padding: 0 20rpx;
+	box-sizing: border-box;
+	background-color: #f8f8f8;
+	align-items: center;
+	justify-content: center;
+	display: flex;
+
+	.bottom-bt {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		input {
+			width: 550rpx;
+			height: 70rpx;
+			padding: 0 15rpx;
+			border-radius: 10rpx;
+			background-color: #fff;
+			margin-right: 20rpx;
+		}
+		button {
+			padding: 0 20rpx;
+			height: 60rpx;
+			line-height: 60rpx;
+			background-color: #12ae85;
+			color: #fff;
+			font-size: 28rpx;
+		}
+	}
+}
+</style>

+ 45 - 9
xiaochengxu/pages/circle/detail.vue

@@ -39,15 +39,19 @@
 						<span @click="changeCardStatus(2)" :class="selectIndex==2?'active':'text'">未交换</span>
 					</view>
 					<view class="right flex">
-						<view class="flex"  v-if="dataObj.circleCardInfo" @click="myCardClick">
+					<!-- 	<view class="flex"  v-if="dataObj.circleCardInfo" @click="myCardClick">
 							<image src="../../static/imgs/cirlce/account.png" mode="widthFix"
 								style="width: 36rpx;margin-right: 15rpx;height: auto;"></image>我的名片
-						</view>
+						</view> -->
 						<view class="">
+							<image src="../../static/imgs/card/share1.png" mode="widthFix"
+								style="width: 50rpx;margin-left: 30rpx;height: 50rpx;" @click="toFriendsCirlce()"></image>
 							<image src="../../static/imgs/card/buju1.png" mode="widthFix"
-								style="width: 50rpx;margin-left: 30rpx;height: 50rpx;" v-if="layout==1" @click="changeLayout(2)"></image>
-								<image src="../../static/imgs/card/buju2.png" mode="widthFix"
-									style="width: 50rpx;margin-left: 30rpx;height: 50rpx;" v-if="layout==2" @click="changeLayout(1)"></image>
+								style="width: 50rpx;margin-left: 30rpx;height: 50rpx;" @click="change()"></image>
+			<!-- 				<image src="../../static/imgs/card/buju1.png" mode="widthFix"
+								style="width: 50rpx;margin-left: 30rpx;height: 50rpx;" v-if="layout==1" @click="changeLayout(2)"></image> -->
+						<!-- 		<image src="../../static/imgs/card/buju2.png" mode="widthFix"
+									style="width: 50rpx;margin-left: 30rpx;height: 50rpx;" v-if="layout==2" @click="changeLayout(1)"></image> -->
 						</view>
 					</view>
 				</view>
@@ -57,7 +61,7 @@
 			<mescroll-uni height='1200' :up="upOption" :down="downOption" ref="mescrollRef" @init="mescrollInit" @up="upCallback" @down="downCallback" >
 			<view class="content3" v-for="(item,index) in changeCardList"
 				:style="item.currentBackground?'background:url('+item.currentBackground+');background-size:100% 100%':''"
-				:key="index" v-if="layout==1">
+				:key="index" v-if="layout">
 				<view class="flex item">
 					<view class="top flex">
 						<view class="left">
@@ -92,7 +96,7 @@
 						@click="changeCard(item)" v-if="item.notDisplay!=1"></image>
 				</view>
 			</view>
-			<view class="layout2" v-if="layout==2">
+			<view class="layout2" v-if="!layout">
 				<view class="list_box">
 					<view class="list-item" v-for="(item,index) in changeCardList" :key="index" @click="itemClick(item,index)">
 						<view class="left">
@@ -122,6 +126,8 @@
 		
 		<u-picker :immediateChange ="true" @cancel="isShowCard=false" :show="isShowCard" :columns="cardList" keyName="cardBusiness"
 			@confirm="cardConfirm"></u-picker>
+			<u-picker :immediateChange ="true" @cancel="changeMore=false" :show="changeMore" :columns="moreList" keyName="name"
+				@confirm="changeMoreConfirm"></u-picker>
 		<u-toast ref="uToast"></u-toast>
 		<u-modal :show="show" :content='content' @confirm="$u.debounce(joinCircle, 500)" showCancelButton
 			@cancel="show=false" @close="show=false" closeOnClickOverlay></u-modal>
@@ -201,7 +207,18 @@
 		mixins: [MescrollMixin], // 使用mixin
 		data() {
 			return {
-				layout:1,
+				moreList:[
+					[
+						{
+							"name":'切换名片'
+						},
+						{
+							"name":'更换布局'
+						}
+					]
+				],
+				changeMore:false,
+				layout:true,
 				showAuthorizeUser: false,
 				showAuthorizePhone: false,
 				customStyleUnOk: {
@@ -286,8 +303,27 @@
 			}
 		},
 		methods: {
+			toFriendsCirlce(){
+				uni.navigateTo({
+					url:"/pages/circle/friendSCirlce?id="+this.dataObj.id
+				})
+			},
+			changeMoreConfirm(val){
+				console.log(val)
+				if(val.value[0].name=="切换名片"){
+					this.isShowCard = true
+					this.isMyCard = true
+				}else{
+					this.layout = !this.layout
+				}
+				
+				this.changeMore = false
+			},
+			change(){
+				this.changeMore = true
+			},
 			changeLayout(type){
-				this.layout = type
+				
 			},
 			//获取昵称输入内容
 			userNameInput(e) {

+ 378 - 0
xiaochengxu/pages/circle/friendSCirlce.vue

@@ -0,0 +1,378 @@
+<template>
+	<view class="u-page">
+		<mescroll-uni :up="upOption" :down="downOption" ref="mescrollRef" @init="mescrollInit" @up="upCallback"
+			@down="downCallback">
+			<circle-item ref="mescrollItem" :list="list" @click="showInput = false" @doThumb="doThumb"
+				@doComment="doComment"></circle-item>
+		</mescroll-uni>
+		<view class="bottom" v-if="showInput">
+			<view class="bottom-bt">
+				<input class="bottom-bt-input" placeholder="请输入内容" v-model="commentValue" />
+				<text class="bottom-bt-button" @click="submitComment">发送</text>
+			</view>
+		</view>
+		<image src="../../static/imgs/cirlce/add.png" mode="widthFix" class="add" @click="addCircle"></image>
+		<u-action-sheet :show="show2" @close="show2 = false" @select="sheetSelect" :actions="actions2"
+			cancelText="取消"></u-action-sheet>
+	</view>
+</template>
+
+<script>
+	var that;
+	import circleItem from './circle-item.vue';
+	import {
+		mapState,
+		mapMutations
+	} from 'vuex';
+	import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
+	import {
+		friendlyDate
+	} from '@/common/util.js';
+
+	export default {
+		mixins: [MescrollMixin],
+		components: {
+			circleItem
+		},
+		data() {
+			return {
+				formData: {
+					circleFriendsId: '',
+					commonId: '',
+					head: '',
+					nickname: '',
+					interactionFlag: '',
+
+				},
+				userInfo: {},
+				circleId: '',
+				downOption: {
+					auto: false,
+					textColor: '#bbb'
+				},
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					auto: false,
+					noMoreSize: 1,
+					textNoMore: '没有更多了~',
+					textColor: '#bbb'
+				},
+				list: [],
+				requestParams: {
+					searchType: 1,
+					latitude: 0,
+					longitude: 0,
+					pageSize: 10,
+					pageNo: 1
+				},
+
+				show2: false,
+				actions2: [{
+					name: '删除'
+				}],
+				showInput: false
+			};
+		},
+		onLoad(options) {
+			that = this
+			this.circleId = options.id
+			this.userInfo = uni.getStorageSync("userInfo")
+			// 需要固定swiper的高度 (需减去悬浮tabs的高度64rpx)
+			// this.height = uni.getSystemInfoSync().windowHeight - uni.upx2px(100) + 'px';
+			// this.tabHeight = uni.upx2px(100) + 'px';
+			this.loadData(true);
+		},
+		methods: {
+			addCircle() {
+				uni.navigateTo({
+					url: "/pages/circle/addFriendCirlce?id=" + this.circleId
+				})
+			},
+			/*下拉刷新的回调 */
+			downCallback() {
+				// this.loadData(true);
+				this.mescroll.resetUpScroll();
+			},
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
+			upCallback(page) {
+				console.log('upCallback=================', page);
+				this.requestParams.pageNo = page.num;
+				this.requestParams.pageSize = page.size;
+				this.loadData();
+			},
+
+			scroll() {},
+
+			change(index) {
+				console.log('change=========', index);
+				this.requestParams.searchType = index + 1;
+				this.loadData(true);
+			},
+			// 处理图片
+			imageInfo(url) {
+				let promise = new Promise(function(resolve, reject) {
+					uni.getImageInfo({
+						src: url,
+						success: function(image) {
+							resolve(image)
+							console.log(image.width);
+							console.log(image.height);
+						}
+					});
+				})
+				return promise
+			},
+			loadData(refresh) {
+				this.$request.baseRequest('admin.unimall.circleFriendsInfo', 'list', {
+					page: this.requestParams.pageNo,
+					limit: this.requestParams.pageSize,
+					circleId: this.circleId,
+					currentCommonId: this.userInfo.id
+				}, failres => {
+					console.log('res+++++', failres.errmsg)
+					uni.showToast({
+						icon: "none",
+						title: failres.errmsg,
+						duration: 3000
+					});
+					uni.hideLoading()
+				}).then(async res => {
+					console.log(res)
+					if (this.requestParams.pageNo.num == 1) this.list = [];
+					let curPageLen = res.data.items.length;
+					let totalPage = res.data.total;
+					this.list = res.data.items
+					for (let i = 0; i < this.list.length; i++) {
+						this.list[i].urlList = this.list[i].image.split(",")
+						if (this.list[i].mediaType == 2) {
+							let _image = await this.imageInfo(this.list[i].image +
+								'?x-oss-process=video/snapshot,t_1000,f_jpg,w_800,h_600,m_fast,ar_auto')
+							console.log("_image", _image)
+							if (_image.width > _image.height) {
+								this.list[i].direction = 1
+							} else {
+								this.list[i].direction = 2
+							}
+						}
+					}
+					this.$nextTick(() => {
+						that.mescroll.endBySize(curPageLen, totalPage)
+					});
+					uni.hideLoading()
+				})
+			},
+
+			getCircleDetail(id, index) {
+				let param = {
+					id: id,
+					searchType: 1,
+					latitude: 0,
+					longitude: 0
+				};
+				if (this.locateInformation.location) {
+					param.latitude = this.locateInformation.location.lat;
+					param.longitude = this.locateInformation.location.lng;
+				}
+				getCircleDetail({
+						params: param
+					})
+					.then(res => {
+						console.log('getCircleDetail====', res);
+						if (res) {
+							let data = res;
+							if (data.mediaType == 1) {
+								let arr = data.url.split(',');
+								if (arr.length > 1) {
+									let list = [];
+									arr.forEach(item2 => {
+										list.push(item2);
+									});
+									data.urlList = list;
+								} else {
+									data.urlList = arr;
+								}
+							}
+							data.dateTime = friendlyDate(new Date(data.createTime.replace(/\-/g, '/')).getTime());
+							this.list[index].thumbNumber = data.thumbNumber;
+							this.list[index].commentNumber = data.commentNumber;
+							this.list[index].thumbed = data.thumbed;
+							this.list[index].thumbs = data.thumbs;
+							this.list[index].comments = data.comments;
+							console.log('data====', data);
+							console.log('index====', index);
+							console.log('this.list====', this.list);
+							this.$forceUpdate();
+						} else {}
+					})
+					.catch(err => {
+						console.log(err, 'catch');
+					});
+			},
+			//点赞
+			async doThumb(item) {
+				if (item.helpFlag == 0) {
+					this.formData = {
+						circleFriendsId: item.id,
+						commonId: this.userInfo.id,
+						head: this.userInfo.head,
+						nickname: this.userInfo.nickname
+					}
+					
+					this.formData.interactionFlag = 1
+					this.$request.baseRequest('admin.unimall.circleFriendsDetail', 'add', {
+						circleFriendsDetail: JSON.stringify(this.formData)
+					}, failres => {
+						console.log('res+++++', failres.errmsg)
+						uni.showToast({
+							icon: "none",
+							title: failres.errmsg,
+							duration: 3000
+						});
+						uni.hideLoading()
+					}).then(async res => {
+						console.log(res)
+						uni.showToast({
+							icon: "success",
+							title: '点赞成功!',
+							duration: 2000
+						});
+						this.loadData()
+					})
+				} else {
+					this.$request.baseRequest('admin.unimall.circleFriendsDetail', 'cancelLike', {
+						circleFriendsId: item.id,
+						commonId: this.userInfo.id,
+					}, failres => {
+						console.log('res+++++', failres.errmsg)
+						uni.showToast({
+							icon: "none",
+							title: failres.errmsg,
+							duration: 3000
+						});
+						uni.hideLoading()
+					}).then(async res => {
+						console.log(res)
+						uni.showToast({
+							icon: "success",
+							title: '取消成功!',
+							duration: 2000
+						});
+						this.loadData()
+					})
+				}
+			},
+
+			doComment(item, comment) {
+				this.selectedComment = comment;
+				this.selectedCircle = item;
+				if (comment != null && comment.userId == this.userInfo.id) {
+					this.show2 = true;
+					return;
+				}
+				this.showInput = !this.showInput;
+				this.commentValue = '';
+				this.$forceUpdate();
+			},
+
+			async submitComment() {
+				if (!this.commentValue) {
+					uni.$u.toast('请输入内容');
+					return;
+				}
+				let params = {
+					comment: this.commentValue,
+					circleId: this.selectedCircle.id,
+					circleUserId: this.selectedCircle.userId
+				};
+				if (this.selectedComment) {
+					let tmp = {
+						pid: this.selectedComment.id,
+						comUserId: this.selectedComment.userId
+					};
+					params = uni.$u.deepMerge(params, tmp);
+				}
+				let res = await addComment(params);
+				if (res) {
+					uni.$u.toast('评论成功');
+					this.getCircleDetail(this.selectedCircle.id, this.selectedCircle.index);
+				}
+			},
+
+			sheetSelect(val) {
+				console.log('---sheetSelect---');
+				this.doDelComment();
+			},
+
+			async doDelComment() {
+				let params = {
+					id: this.selectedComment.id,
+					circleId: this.selectedComment.circleId
+				};
+				let res = await delComment(params);
+				console.log('doDelComment', res);
+				if (res) {
+					uni.$u.toast('删除成功');
+					this.getCircleDetail(this.selectedCircle.id, this.selectedCircle.index);
+				}
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.u-swiper {
+		position: relative;
+	}
+
+	.bottom {
+		bottom: 0;
+		position: fixed;
+		height: 120upx;
+		width: 750upx;
+		// padding: 0 20upx;
+		background-color: #eee;
+		align-items: center;
+		justify-content: center;
+		display: flex;
+		z-index: 999;
+	}
+
+	.bottom-bt {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+	}
+
+	.bottom-bt-input {
+		width: 550upx;
+		height: 70upx;
+		padding: 0 15upx;
+		border-radius: 10upx;
+		background-color: #fff;
+		margin-right: 20upx;
+		font-size: 28upx;
+		color: #333;
+	}
+
+	.bottom-bt-button {
+		padding: 0 20upx;
+		height: 55upx;
+		line-height: 55upx;
+		background-color: #344577;
+		font-size: 28upx;
+		color: #fff;
+		border-radius: 10upx;
+	}
+
+	.add {
+		width: 84rpx;
+		height: auto;
+		position: fixed;
+		bottom: 200rpx;
+		right: 30rpx;
+		height: auto;
+		z-index: 999;
+	}
+</style>

BIN
xiaochengxu/static/comment.png


BIN
xiaochengxu/static/imgs/card/share1.png


BIN
xiaochengxu/static/love-fill.png


BIN
xiaochengxu/static/love.png


BIN
xiaochengxu/static/play.png


+ 69 - 0
xiaochengxu/uni_modules/cl-upload/changelog.md

@@ -0,0 +1,69 @@
+## 1.3.6(2023-05-22)
+添加自定义图片资源配置
+## 1.3.5(2023-05-04)
+修复小程序cloudType为other的时候获取不到视频删除按钮焦点问题
+## 1.3.4(2023-04-27)
+修复小程序unicloud自动上传不自动同步数据问题
+## 1.3.3(2023-04-27)
+修复删除按钮层级问题
+## 1.3.2(2023-04-24)
+修复安卓APP配置cloudType:other 无法显示http封面问题
+## 1.3.1(2023-04-19)
+修改版本信息
+## 1.3.0(2023-04-19)
+修复手动上传已知问题; 新增删除前,上传前钩子函数
+## 1.2.9(2023-03-22)
+修复已知问题
+## 1.2.8(2023-03-22)
+修复已知问题
+## 1.2.7(2023-03-21)
+修复部分视频封面白屏问题
+## 1.2.6(2023-03-21)
+修复部分视频封面白屏问题
+## 1.2.5(2023-03-08)
+兼容vue3中v-model数据绑定
+## 1.2.4(2023-03-06)
+1.修复低版本微信小程序video图层兼容问题;
+2.修复unicloud上传v-model不同步问题;
+## 1.2.3(2023-02-02)
+添加限制图片视频大小功能
+## 1.2.2(2023-02-01)
+兼容支付宝小程序手动上传视频
+## 1.2.1(2023-01-31)
+更新图片资源地址
+## 1.2.0(2022-12-12)
+兼容uniCloud上传
+## 1.1.9(2022-12-12)
+1. 添加最大视频限制
+2. 优化部分功能
+## 1.1.8(2022-12-09)
+修复部分设备上传视频第一帧黑屏问题
+## 1.1.7(2022-12-01)
+修复h5上传开启压缩后一直显示“正在压缩中”问题
+## 1.1.6(2022-11-24)
+兼容支付宝小程序手动上传
+## 1.1.5(2022-11-07)
+添加加载配置
+## 1.1.4(2022-11-07)
+修复图片限制数量后可以多次选择最大数量图片问题
+## 1.1.3(2022-10-28)
+添加提示弹窗配置
+## 1.1.1(2022-10-28)
+修复已知问题
+## 1.1.0(2022-08-25)
+修复base64压缩问题
+## 1.0.9(2022-08-25)
+修复服务器返回数据格式问题
+## 1.0.8(2022-08-01)
+优化代码格式、逻辑
+## 1.0.6(2022-07-30)
+修改已知问题
+## 1.0.5(2022-07-30)
+1. aspect-ratio兼容问题,添加height属性保底
+2. 添加监听上传进度变化事件
+## 1.0.3(2022-07-30)
+添加按钮控制,事件说明
+## 1.0.2(2022-07-30)
+优化服务器接口返回数据上传
+## 1.0.1(2022-07-29)
+初始化组件

+ 54 - 0
xiaochengxu/uni_modules/cl-upload/components/cl-image/cl-image.vue

@@ -0,0 +1,54 @@
+<template>
+	<image class="image" :src="imgSrc" mode="aspectFill" :disabled="false" :controls='false' @error="imgerror"></image>
+</template>
+
+<script>
+	export default {
+		props: {
+			src: {
+				type: String,
+				default: ''
+			},
+			cloudType: {
+				type: String,
+				default: 'oss'
+			},
+		},
+		data() {
+			return {
+				imgSrc: ''
+			};
+		},
+		mounted() {
+			this.setCloudFunction()
+		},
+		methods: {
+			imgerror(even) {
+				this.imgSrc =  `https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/887c60f0-27f8-46d1-8769-2c45be0f3d7d.png`
+			},
+			setCloudFunction() {
+				switch (this.cloudType){
+					case 'oss':
+						this.imgSrc = this.src + '?x-oss-process=video/snapshot,t_0,f_jpg'
+						break;
+					case 'process':
+						this.imgSrc = this.src + '?ci-process=snapshot&time=0.01'
+						break;
+					case 'vframe':
+						this.imgSrc = this.src + '?vframe/jpg/offset/0'
+						break;
+					default:
+						this.imgSrc = this.src
+						break;
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	image {
+		width: 100%;
+		height: 100%;
+	}
+</style>

+ 981 - 0
xiaochengxu/uni_modules/cl-upload/components/cl-upload/cl-upload.vue

@@ -0,0 +1,981 @@
+<template>
+	<view class="cl-updata">
+		<view class="file-list" :style="[listRowStyle]">
+
+			<view v-for="(item, index) in previewList" @tap="onClickRow(item, index)" class="file-list-row"
+				:style="[rowStyle]" :key="index">
+
+				<image v-if="fileUrlType(item) === 'image'" :src="item.path || item" :style="[imgStyle]"
+					mode="aspectFill">
+				</image>
+
+				<view v-else class="_video" :style="[imgStyle]">
+
+					<!-- #ifdef MP-WEIXIN || MP-ALIPAY -->
+					<video v-if="!autoUpload || cloudType === 'other'" class="_video" :style="[imgStyle]"
+						:src="item.url || item" :show-center-play-btn="false" :show-fullscreen-btn="false"
+						:show-play-btn="false" :show-loading="false" :enable-progress-gesture="false" :controls="false">
+						<!-- <cover-view class="video-fixed" ></cover-view> -->
+						<cover-view @tap="onPlay(item, index)" class="play">
+							<image style="width: 100%;" :src="playImg" mode="widthFix"></image>
+						</cover-view>
+					</video>
+
+					<!-- #endif -->
+
+					<!-- #ifdef APP-PLUS -->
+					<video v-if="cloudType === 'other'" class="_video" :style="[imgStyle]" :src="item" :poster="item"
+						:controls="false" :show-center-play-btn="false" :show-fullscreen-btn="false"
+						:show-play-btn="false" :show-loading="false" :enable-progress-gesture="false">
+						<cover-image class="app_play" :src="playImg" @tap="onPlay(item, index)"></cover-image>
+						<cover-view class="remove" v-if="remove" @tap="onRemove(item, index)">
+							<cover-image class="image" :src="deleteImg" mode="widthFix"
+								@tap="onRemove(item, index)"></cover-image>
+						</cover-view>
+					</video>
+					<!-- #endif -->
+
+					<!-- #ifndef MP-WEIXIN || MP-ALIPAY || APP-PLUS -->
+					<video v-if="cloudType === 'other'" class="_video" :autoplay="false" :style="[imgStyle]" :src="item"
+						:controls="false" :show-center-play-btn="false" :show-fullscreen-btn="false"
+						:show-play-btn="false" :show-loading="false" :enable-progress-gesture="false">
+						<!-- <cover-view class="video-fixed" @tap="onPlay(item, index)"></cover-view> -->
+						<cover-view @tap="onPlay(item, index)" class="play">
+							<image style="width: 100%;" :src="playImg" mode="widthFix"></image>
+						</cover-view>
+					</video>
+
+					<!-- #endif -->
+
+					<cl-image v-else class="pay" :style="[imgStyle]" :cloudType="cloudType"
+						:src="(item.path || item)"></cl-image>
+
+					<view class="play" @tap="onPlay(item, index)">
+						<image class="play-img" :src="playImg" mode="widthFix"></image>
+					</view>
+
+				</view>
+
+				<view class="remove" v-if="remove" @tap.stop="onRemove(item, index)">
+					<image class="image" :src="deleteImg" mode="widthFix"></image>
+				</view>
+			</view>
+
+			<view v-if="add && FileList.length < max" @tap="onClickAdd" :style="[rowStyle]"
+				class="file-list-row add-image">
+				<image :src="addImg" mode="widthFix"></image>
+			</view>
+		</view>
+
+
+		<view v-if="tempVideoUrl" class="mask">
+			<image @tap="tempVideoUrl = ''" class="_root" :src="closeImg" mode="widthFix"></image>
+
+			<view class="block" @tap.stop>
+				<video autoplay :src="tempVideoUrl"></video>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import uploadImage from '@/components/ossutil/uploadFile.js';
+	import ClImage from '../cl-image/cl-image.vue'
+	export default {
+		name: "cl-upload",
+		components: {
+			ClImage
+		},
+		props: {
+			//受控图片列表
+			// #ifdef VUE2
+			value: {
+				type: Array,
+				default: () => [],
+			},
+			// #endif
+
+			// #ifdef VUE3
+			modelValue: {
+				type: Array,
+				default: () => [],
+			},
+			// #endif
+
+			// 存储云类型 oss阿里云  vframe七牛云   process腾讯云  other其他
+			cloudType: {
+				type: String,
+				default: 'oss'
+			},
+			// 标识符,即后端接口参数名
+			fileName: {
+				type: String,
+				default: 'file'
+			},
+			// // 文件类型 'image', 'video', 'all'
+			// fileType: {
+			// 	type: String,
+			// 	default: 'all'
+			// },
+			// 上传图片参数
+			imageFormData: {
+				type: Object,
+				default: () => {}
+			},
+			// 上传视频参数
+			videoFromData: {
+				type: Object,
+				default: () => {}
+			},
+
+			// 必选参数,上传的地址
+			action: {
+				type: String,
+				default: ''
+			},
+
+			// 设置上传的请求头部
+			headers: {
+				type: Object,
+				default: () => {}
+			},
+
+			// 上传时附带的额外参数
+			data: {
+				type: Object,
+				default: () => {}
+			},
+
+			// 是否开启预览图片
+			isPreviewImage: {
+				type: Boolean,
+				default: true
+			},
+
+			// 图片指示器样式,可取值:"default" - 底部圆点指示器; "number" - 顶部数字指示器; "none" - 不显示指示器。
+			indicator: {
+				type: String,
+				default: 'none'
+			},
+			// 是否在选取文件后立即进行上传	
+			autoUpload: {
+				type: Boolean,
+				default: true
+			},
+			// 是否显示删除按钮
+			remove: {
+				type: Boolean,
+				default: true
+			},
+			// 是否添加按钮
+			// add: {
+			// 	type: Boolean,
+			// 	default: true
+			// },
+			// 最多显示数量
+			max: {
+				type: Number,
+				default: 9
+			},
+			// 视频最大上传数量
+			maxVideo: {
+				type: Number,
+				default: 0
+			},
+			// 列表样式
+			listStyle: {
+				type: Object,
+				default: () => {}
+			},
+			// 删除提示弹窗标题
+			deleteTitle: {
+				type: String,
+				default: '提示'
+			},
+			// 删除提示弹窗文案
+			deleteText: {
+				type: String,
+				default: '您确认要删除吗?'
+			},
+			// 是否开启删除前钩子
+			useBeforeDelete: {
+				type: Boolean,
+				default: false
+			},
+			// 是否开启上传前钩子
+			useBeforeUpload: {
+				type: Boolean,
+				default: false
+			},
+			// 添加按钮图片
+			addImg: {
+				type: String,
+				default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/bb1550b3-e0a8-4a90-a86f-00f8c6afa9fb.png'
+			},
+			// 播放按钮图片
+			playImg: {
+				type: String,
+				default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/ae40402f-aa53-4344-b553-2322799bebd6.png'
+			},
+			// 删除按钮图片
+			deleteImg: {
+				type: String,
+				default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/d20177a5-417e-4c5d-a266-1988361c543d.png'
+			},
+			// 关闭视频按钮图片
+			closeImg: {
+				type: String,
+				default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/cde4362d-7ec7-4cac-a692-12e1f576be1e.png'
+			},
+		},
+		data() {
+			return {
+				add:true,
+				// 文件类型 'image', 'video', 'all'
+				fileType: "all",
+				// 渲染列表
+				FileList: [],
+
+				// 预览视频地址
+				tempVideoUrl: '',
+
+				// 临时文件列表
+				tempFile_paths: [],
+
+			};
+		},
+		watch: {
+			// #ifdef VUE2
+			'value': {
+				handler: function(newVal, oldVal) {
+					this.FileList = newVal;
+				},
+				deep: true,
+				immediate: true
+			},
+			// #endif
+
+			// #ifdef VUE3
+			'modelValue': {
+				handler: function(newVal, oldVal) {
+					this.FileList = newVal;
+				},
+				deep: true,
+				immediate: true
+			},
+			// #endif
+		},
+		computed: {
+			previewList() {
+				return this.FileList.map(item => item.path || item)
+			},
+			listRowStyle() {
+				const style = {
+					'grid-template-columns': `repeat(${this.listStyle?.columns || 4}, 1fr)`, // 每行数量	
+					'grid-column-gap': this.listStyle?.columnGap || '40rpx', // 行间距	
+					'grid-row-gap': this.listStyle?.rowGap || '40rpx', // 列间距
+					'padding': this.listStyle?.padding || '0rpx' // 列表内边距
+				}
+
+				return style;
+			},
+			rowStyle() {
+				const style = {
+					'aspect-ratio': this.listStyle?.height ? '' : this.listStyle?.ratio || '1/1', // 图片比例	
+					'height': this.listStyle?.height || '140rpx',
+				}
+
+				return style;
+			},
+			imgStyle() {
+				const style = {
+					'border-radius': this.listStyle?.radius || '6rpx', // 图片圆角
+				}
+
+				return style;
+			}
+		},
+		methods: {
+			/**
+			 * 删除已选择文件
+			 * @param {object} item 文件信息
+			 * @param {number} index 文件索引
+			 * */
+			onRemove(item, index) {
+				if(this.FileList.length==1){
+					this.fileType = "all"
+					this.add = true
+				}
+				const imageItem = this.FileList[index];
+
+				if (this.useBeforeDelete) {
+					this.$emit('beforeDelete', imageItem, index, () => {
+						return deleteItem()
+					})
+				}
+
+				if (!this.useBeforeDelete) {
+					uni.showModal({
+						title: this.deleteTitle,
+						content: this.deleteText,
+						success: (res) => {
+							if (res.confirm) {
+								deleteItem()
+							}
+						}
+					});
+				}
+
+				const deleteItem = () => {
+					const tempFileIndex = this.tempFile_paths.indexOf(item || item.path);
+
+					if (tempFileIndex > -1) {
+						this.tempFile_paths.splice(tempFileIndex, 1)
+					}
+
+					this.FileList.splice(index, 1)
+
+					// #ifdef VUE2 
+					this.$emit('input', this.FileList)
+					// #endif
+
+					// #ifdef VUE3
+					this.$emit("update:modelValue", this.FileList);
+					// #endif
+				}
+
+			},
+
+			/**
+			 * 点击已选择文件
+			 * @param {object} item 文件信息
+			 * @param {number} index 文件索引 
+			 * */
+			onClickRow(item, index) {
+				this.previewImage(item?.url ?? item, index);
+				this.$emit('onImage', {
+					item,
+					index
+				})
+			},
+
+			/**
+			 * 点击选择图片按钮
+			 * */
+			onClickAdd() {
+
+				switch (this.fileType) {
+					case 'image':
+						this.onChooseFile(1);
+						break;
+					case 'video':
+						this.onChooseFile(2);
+						break;
+					case 'all':
+						uni.showActionSheet({
+							itemList: ['照片', '视频'],
+							success: (res) => {
+								const tapIndex = res.tapIndex;
+								if (tapIndex === 0) {
+									this.onChooseFile(1);
+								} else {
+									this.onChooseFile(2);
+								}
+							},
+							fail: (res) => {
+								console.error(res.errMsg);
+							}
+						});
+						break;
+					default:
+						this.onChooseFile(1);
+						break;
+				}
+			},
+
+
+			/**
+			 * 从本地选择文件。
+			 * @param { number } updataType 选择类型 1:图片 2视频
+			 * */
+			async onChooseFile(updataType) {
+				const that = this;
+				if (updataType === 1) {
+					this.fileType = 'image'
+					this.$emit('showType', "1")
+					const data = Object.assign({}, {
+						// 最多可以选择的图片张数,默认9
+						count: 9,
+						// 仅对 mediaType 为 image 时有效,是否压缩所选文件
+						// #ifndef MP-TOUTIAO
+						sizeType: ['original', 'compressed'],
+						// #endif
+						// album 从相册选图,camera 使用相机,默认二者都有。
+						sourceType: ['camera', 'album'],
+
+						compress: false
+					}, this.imageFormData)
+
+					data['count'] = this.max - this.FileList.length
+
+					uni.chooseImage({
+						...data,
+						success: async (res) => {
+							let tempFiles = res.tempFiles
+							const compress = that.imageFormData?.compress || false;
+
+							// 限制图片上传尺寸
+							if (that.imageFormData?.size ?? false) {
+								tempFiles.map((imgInfo, index) => {
+									const maxSize = that.imageFormData.size * 1024 * 1024
+									if (imgInfo.size > maxSize) {
+										tempFiles.splice(index, 1)
+										that.$emit('onImageSize', imgInfo)
+										return uni.showToast({
+											title: `图片最大上传${that.imageFormData.size}MB`,
+											duration: 2000,
+											icon: 'none'
+										});
+									}
+								})
+							}
+
+							// 开启压缩图片
+							if (compress) {
+								const compressImage = tempFiles.map(imageItem => {
+									return that.compressImage(imageItem.path)
+								})
+
+								Promise.all(compressImage).then(result => {
+									upload(result);
+								})
+
+							} else {
+								upload(tempFiles);
+							}
+
+							function upload(tempImages) {
+								if (that.autoUpload) {
+									tempImages.map(item => {
+										that.onBeforeUploadFile(item, 'image')
+									})
+								} else {
+									that.FileList = [...that.FileList, ...tempImages]
+									tempImages.map(item => {
+										that.tempFile_paths.push(item)
+									})
+								}
+							}
+
+						},
+						fail(err) {
+							console.error('选择图片失败', err)
+							that.$emit('onError', err)
+						}
+
+					})
+				}
+
+				if (updataType === 2) {
+					console.log("上传视频")
+					this.fileType = "video"
+					this.$emit('showType', "2")
+					this.add = false
+					// 限制视频最大上传数量
+					const VIDEO_REGEXP = /\.(mp4|flv|avi)/i
+					const videoList = await that.FileList.filter(item => {
+						const fileUrl = item?.url ?? item
+						return VIDEO_REGEXP.test(fileUrl)
+					})
+
+					if (that.maxVideo > 0 && videoList.length >= that.maxVideo) {
+						that.$emit('onVideoMax', that.maxVideo, videoList.length)
+						return uni.showToast({
+							title: '视频数量已超出',
+							duration: 2000,
+							icon: 'none'
+						});
+					}
+
+					const data = Object.assign({}, {
+						// 	拍摄视频最长拍摄时间,单位秒。最长支持 60 秒。
+						maxDuration: 60,
+						// #ifndef MP-TOUTIAO
+						// 'front'、'back',默认'back'
+						camera: "back",
+						// #endif
+
+						// album 从相册选视频,camera 使用相机拍摄,默认二者都有。
+						sourceType: ['camera', 'album'],
+						// 是否压缩所选的视频源文件,默认值为 true,需要压缩。
+						compressed: true,
+						// 'front'、'back',默认'back'
+					}, this.videoFromData)
+
+					uni.chooseVideo({
+						...data,
+						success: (res) => {
+							let tempFilePath = {
+								...res
+							}
+							tempFilePath['path'] = res.tempFilePath
+
+							// 限制视频上传尺寸
+							if (that.videoFromData?.size ?? false) {
+								const maxSize = that.videoFromData.size * 1024 * 1024
+
+								if (tempFilePath.size > maxSize) {
+									uni.showToast({
+										title: `视频最大上传${that.videoFromData.size}MB`,
+										duration: 2000,
+										icon: 'none'
+									});
+									return false;
+								}
+
+							}
+							if (that.autoUpload) {
+								that.onBeforeUploadFile(tempFilePath, 'video')
+							} else {
+								that.FileList.push(tempFilePath)
+								that.tempFile_paths.push(tempFilePath)
+							}
+						},
+						fail(err) {
+							console.error('选择视频失败', err)
+						}
+
+					})
+				}
+			},
+
+			onBeforeUploadFile(tempFile) {
+				if (this.useBeforeUpload) {
+					return this.$emit('beforeUpload', tempFile, () => {
+						return this.updataFile(tempFile);
+					})
+				}
+				return this.updataFile(tempFile);
+			},
+
+			/**
+			 * 上传文件到服务器
+			 * @param { tempFile } 临时文件
+			 * */
+			updataFile(tempFile) {
+				const that = this;
+				const filePath = tempFile.path || tempFile;
+				const fileType = this.fileUrlType(filePath) == 'image' ? '.png' : '.mp4';
+				const fileName = tempFile.name || Date.now() + fileType;
+
+				uni.showLoading({
+					title: '正在上传中...',
+					icon: 'loading'
+				})
+
+				return new Promise((resolve, reject) => {
+					// uniCloud上传
+					if (that.action === 'uniCloud') {
+
+						uniCloud.uploadFile({
+							cloudPath: String(fileName),
+							filePath: filePath,
+							// #ifdef MP-ALIPAY 
+							fileType: fileType,
+							// #endif
+							onUploadProgress: (progressEvent) => {
+								const percentCompleted = Math.round(
+									(progressEvent.loaded * 100) / progressEvent.total
+								);
+								that.$emit('onProgress', percentCompleted)
+							},
+							success(result) {
+								if (that.autoUpload) {
+									that.FileList.push(result.fileID)
+								} else {
+									that.FileList.map((item, index) => {
+										if (item === filePath || item.path === filePath) {
+											that.FileList.splice(index, 1, result.fileID)
+										}
+									})
+								}
+
+								// #ifdef VUE2
+								that.$emit('input', that.FileList)
+								// #endif
+								// #ifdef VUE3
+								that.$emit("update:modelValue", that.FileList);
+								// #endif
+
+								resolve(result.fileID)
+								uni.hideLoading();
+								that.$emit('onProgress', {
+									...result
+								})
+							},
+							fail: (error) => {
+								uni.hideLoading();
+								console.error('error', error);
+								that.$emit('onError', error)
+								reject(error)
+							}
+						})
+						return false;
+					}
+					const uploadTask = uploadImage(filePath, 'cardImages/',
+						result => {
+							const data = result
+							uni.hideLoading();
+							that.success(data)
+
+							if (!this.autoUpload) {
+								that.FileList.map((item, index) => {
+									if (item === filePath || item.path === filePath) {
+										that.FileList.splice(index, 1)
+									}
+								})
+							}
+							resolve(data)
+						}, "", that.fileUrlType(filePath)
+					);
+					// 接口服务上传
+					// const uploadTask = uni.uploadFile({
+					// 	url: that.action,
+					// 	filePath: filePath,
+					// 	name: that.fileName,
+					// 	formData: that.data,
+					// 	header: that.headers,
+					// 	success: (uploadFileRes) => {
+					// 		const data = JSON.parse(uploadFileRes.data)
+					// 		uni.hideLoading();
+					// 		that.success(data)
+
+					// 		if (!this.autoUpload) {
+					// 			that.FileList.map((item, index) => {
+					// 				if (item === filePath || item.path === filePath) {
+					// 					that.FileList.splice(index, 1)
+					// 				}
+					// 			})
+					// 		}
+
+					// 		resolve(data)
+					// 	},
+					// 	fail: (error) => {
+					// 		uni.hideLoading();
+					// 		console.error('error', error);
+					// 		that.$emit('onError', error)
+					// 		reject(error)
+					// 	}
+					// });
+
+					// uploadTask.onProgressUpdate((res) => {
+					// 	that.$emit('onProgress', {
+					// 		...res,
+					// 		...tempFile
+					// 	})
+					// });
+				})
+			},
+
+			/**
+			 * 手动上传
+			 * */
+			submit() {
+
+				return new Promise((resolve, reject) => {
+					if (this.tempFile_paths.length <= 0) {
+						return console.error('没有可上传文件');
+					}
+
+					const result = this.tempFile_paths.map(item => {
+						return this.onBeforeUploadFile(item || item.path)
+					})
+
+					Promise.all(result).then(res => {
+						this.tempFile_paths = []
+						resolve(res)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+
+			},
+
+			/**
+			 * 返回数据
+			 * */
+			success(data) {
+				this.$emit('onSuccess', data);
+
+				// 自定义数据结构-选择性开启
+				// const list = data.map(item=> {
+				// 	return JSON.parse(item).data.link;
+				// })
+				// this.$emit('input', [...this.FileList, ...list]);
+			},
+			/**
+			 * 压缩图片
+			 * @param {array} tempFilePaths 临时路径数组
+			 * @return {array} 被压缩过的路径数组
+			 * */
+			async compressImage(tempFilePaths) {
+				const that = this;
+
+				return new Promise((resolve, reject) => {
+
+					if (typeof tempFilePaths !== 'string') {
+						console.error('压缩路径错误')
+						reject([])
+					}
+
+					uni.showLoading({
+						title: '压缩中...',
+						icon: 'loading',
+					})
+
+					// #ifdef H5
+					this.canvasDataURL(tempFilePaths, {
+						quality: that.imageFormData.quality / 100
+					}, (base64Codes) => {
+						resolve(base64Codes);
+						uni.hideLoading();
+					})
+					// #endif
+
+					// #ifndef H5
+					uni.compressImage({
+						src: tempFilePaths,
+						quality: that.imageFormData.quality || 80,
+						success: res => {
+							resolve(res.tempFilePath);
+							uni.hideLoading();
+						},
+						fail(err) {
+							reject(err);
+							uni.hideLoading();
+						}
+					})
+					// #endif
+
+				})
+			},
+
+			/**
+			 * H5压缩图片质量
+			 * */
+			canvasDataURL(path, obj, callback) {
+				var img = new Image();
+				img.src = path;
+				img.onload = function() {
+					var that = this;
+					// 默认按比例压缩
+					var w = that.width,
+						h = that.height,
+						scale = w / h;
+					w = obj.width || w;
+					h = obj.height || (w / scale);
+					var quality = 0.8; // 默认图片质量为0.8
+					//生成canvas
+					var canvas = document.createElement('canvas');
+					var ctx = canvas.getContext('2d');
+					// 创建属性节点
+					var anw = document.createAttribute("width");
+					anw.nodeValue = w;
+					var anh = document.createAttribute("height");
+					anh.nodeValue = h;
+					canvas.setAttributeNode(anw);
+					canvas.setAttributeNode(anh);
+					ctx.drawImage(that, 0, 0, w, h);
+					// 图像质量
+					if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
+						quality = obj.quality;
+					}
+					// quality值越小,所绘制出的图像越模糊
+					var base64 = canvas.toDataURL('image/jpeg', quality);
+					// 回调函数返回base64的值
+					callback(base64);
+				}
+			},
+
+			/**
+			 * 预览图片
+			 * @param {string, object} item 文件信息
+			 * */
+			previewImage(item) {
+				if (this.fileUrlType(item) === 'video') return false;
+				if (!this.isPreviewImage) return false;
+
+				const imgs = this.FileList.filter(item => {
+					return this.fileUrlType(item) !== 'video'
+				}).map(item => item?.path ?? item)
+				const current = imgs.indexOf(item || item.path);
+
+				uni.previewImage({
+					current: current,
+					urls: imgs,
+					success() {},
+					fail(err) {
+						console.log(err);
+					}
+				})
+			},
+
+			/**
+			 * 预览视频
+			 * @param {string, object} item 文件信息
+			 * */
+			onPlay(item, index) {
+				this.$emit('onVideo', {
+					item,
+					index
+				})
+				this.tempVideoUrl = item.url || item;
+			},
+
+			/**
+			 * 是否img类型
+			 * @param {string, object} item 文件信息
+			 * */
+			fileUrlType(file) {
+				const filePath = file.path || file;
+
+				if (this.isBase64(filePath)) return 'image'
+
+				const fileType = filePath.split('.').pop();
+
+				const IMAGE_REGEXP = /(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg|image)/i
+				if (IMAGE_REGEXP.test(fileType)) {
+					return 'image';
+				} else {
+					return 'video';
+				}
+			},
+			isBase64(str) {
+				if (str === '' || typeof str !== 'string') return console.error('文件路径错误, base64', str);
+				return str.includes('blob:') || str.includes('data:image');
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.cl-updata {
+
+		.file-list {
+			display: grid;
+
+			&-row {
+				display: inline-flex;
+				align-items: center;
+				position: relative;
+
+				image {
+					height: 100%;
+					width: 100%;
+				}
+
+				._video {
+					position: relative;
+					width: 100%;
+					height: 100%;
+					overflow: hidden;
+				}
+
+				.video-fixed {
+					position: absolute;
+					top: 0;
+					left: 0;
+					bottom: 0;
+					width: 100%;
+					height: 100%;
+					border-radius: 10rpx;
+					z-index: 96;
+				}
+
+				.play {
+					position: absolute;
+					top: 50%;
+					left: 50%;
+					transform: translate(-50%, -50%);
+					width: 30%;
+					z-index: 95;
+				}
+
+				.app_play {
+					position: absolute;
+					top: 50%;
+					left: 50%;
+					transform: translate(-50%, -50%);
+					width: 50rpx;
+					height: 50rpx;
+				}
+
+				.remove {
+					position: absolute;
+					top: 0;
+					right: 0;
+					background-color: #373737;
+					height: 50rpx;
+					width: 50rpx;
+					border-bottom-left-radius: 200rpx;
+					z-index: 97;
+
+					.image {
+						width: 20rpx;
+						height: 20rpx;
+						position: absolute;
+						right: 12rpx;
+						top: 12rpx;
+					}
+				}
+			}
+
+			.add-image {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				border: 2rpx dashed #ccc;
+
+				&:active {
+					opacity: 0.8;
+				}
+
+				image {
+					width: 40%;
+				}
+			}
+		}
+
+		.mask {
+			background-color: #000;
+			position: fixed;
+			top: 0;
+			right: 0;
+			bottom: 0;
+			left: 0;
+			z-index: 99;
+
+			.block {
+				padding: 0 30rpx;
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				transform: translate(-50%, -50%);
+				width: 100%;
+
+				video {
+					width: 100%;
+					height: 78vh;
+				}
+			}
+
+			._root {
+				width: 60rpx;
+				height: 60rpx;
+				position: absolute;
+				left: 40rpx;
+				top: 5vh
+			}
+		}
+	}
+</style>

+ 240 - 0
xiaochengxu/uni_modules/cl-upload/readme.md

@@ -0,0 +1,240 @@
+### cl-upload 上传组件
+
+> 支持手动自动上传,样式调整,参数配置,预览,删除等功能
+
+
+> `注意:每次上传都需要回调函数接收参数并且添加到与组件绑定的数组中以保持数据一致,这样做是因为组件不知道服务端返回的数据格式,也可以在组件中修改promise格式 一劳永逸`
+
+### 注意事项
+1. ratio 图片比例属性部分手机不支持,可选用height属性代替
+2. 自定义播放按钮部分平台有兼容性问题,可选择性关闭
+3. 开启压缩图片返回的临时路径没有尾缀,官方api的问题。真机上没问题,也可以在上传的时候手动添加尾缀
+4. **视频地址必须`https`, http可能导致无法显示封面图**
+5. 如果没条件用`https`那就配置`cloudType: other`
+
+### H5体验地址
+![image](https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/eff364bc-65f7-47e0-ae4b-7d5f19b9f094.png)
+
+#### list数据格式
+
+1. 数组格式
+```
+['地址1','地址2']
+```
+2. JSON格式
+```
+[
+    {
+        path: '地址1',
+        // 其他信息
+    },
+    {
+        path: '地址2',
+        // 其他信息
+    },
+]
+```
+
+#### 基础使用
+
+```
+<cl-upload v-model="list" action="请求地址" @onSuccess="onSuccess"></cl-upload>
+
+methods: {
+    /**
+	 * 自动上传返回的是一张图片信息
+	 * 手动上传返回的是已选中所有图片或者视频信息
+	 * */ 
+	onSuccess(reslut) {
+		// 把服务端返回的图片地址添加到list中与组件数据同步
+		this.list.push(reslut.url)
+	},
+}
+```
+### uniCloud上传
+> 一句代码实现上传,就是这么简单
+
+```
+<cl-upload v-model="list" action="uniCloud"></cl-upload>
+```
+
+### 自定义样式
+> 通过 listStyle 控制每行数量、比例、行间距、列间距等常用样式
+
+```
+<cl-upload v-model="list" :listStyle="{
+	columns: 2,
+	columnGap: '20rpx',
+	rowGap:'20rpx',
+	padding:'10rpx',
+	height:'300rpx',
+	radius:'20rpx'
+}"></cl-upload>
+```
+
+### 预览模式
+> 关闭显示添加按钮和删除按钮
+```
+<cl-upload v-model="list" :add="false" :remove="false"></cl-upload>
+```
+
+### 手动上传
+
+> 通过 autoUpload 关闭掉自动上传,提交的时候通过 refs 主动调用组件上传方法,返回本次提交成功服务器返回数据
+
+```
+<cl-upload 
+	ref="upload2" 
+	v-model="list2" 
+	:autoUpload="false"></cl-upload>
+	
+<button @tap="submit">手动提交</button>
+
+methods: {
+    submit() {
+    	/**
+    	 * 主动调用组件上传方法
+    	 * */ 
+    	this.$refs.upload2.submit().then(reslut=>{
+    		console.log(reslut); // 本次提交成功服务器返回数据
+    		
+    		// 上传第三方服务器需要手动同步数据
+    		// 上传uniCloud则不需要
+    		const imgUrls = reslut.list.map(imgInfo=> imgInfo.url);
+    		this.list2 = [...this.list2, ...imgUrls]
+    	})
+    },
+}
+```
+
+### 配置删除前和上传前钩子 `1.3.0`
+```
+/ **
+* 开启删除前钩子 useBeforeDelete
+* 开启上传前钩子 useBeforeUpload
+*/
+<cl-upload v-model="list" 
+    useBeforeDelete 
+    useBeforeUpload
+    @beforeDelete="beforeDelete"
+    @beforeUpload="beforeUpload"></cl-upload>
+    
+
+methods: {
+    /**
+	 * 删除前钩子
+	 * @param {Object} item 当前删除的图片或者视频信息
+	 * @param {Number} index 当前删除的图片或视频索引
+	 * @param {Function} next 调用此函数继续执行组件删除逻辑
+	 * */ 
+	beforeDelete(item, index, next) {
+		uni.showModal({
+			title: '提示信息',
+			content: '确定要删除这个文件嘛?',
+			success: res => {
+				if (res.confirm) {
+					// 模拟服务器接口
+					setTimeout(() => {
+						next();
+					}, 1000);
+				}
+			}
+		});
+	},
+	/**
+	 * 上传前钩子
+	 * @param {Object} tempFile 当前上传文件信息
+	 * @param {Function} next 调用此函数继续执行组件上传逻辑
+	 * */
+	beforeUpload(tempFile, next) {
+		// 自己的上传逻辑
+		// 如果不需要走组件的上传逻辑就不用调用next(), 但是上传完要同步到list
+	}
+}
+```
+
+## API 
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+| --- | --- | --- | --- | --- |
+| action | 上传地址 | String |-| uniCloud |
+| cloudType | 存储云类型(各个云服务截取封面方式不同。 other选项是video保底手段,部分平台有兼容性问题) | String |oss| 阿里云:oss  七牛云:vframe   腾讯云:process  其他:other |
+| headers | 设置上传的请求头部 | Object | - |-  |
+| data | 上传时附带的额外参数 | Object | - | - |
+| fileName| 标识符,即后端接口参数名 | String | file | - |
+| fileType | 文件类型 | String | all | 'image', 'video', 'all' |
+| imageFormData | 上传图片参数 | Object | - | - |
+| videoFromData | 上传视频参数 | Object  | - | - |
+| listStyle | 列表样式 |Object  | - | - |
+| isPreviewImage | 是否开启预览图片 | Boolean | true |false  |
+| remove | 是否显示删除按钮 | Boolean | true |false  |
+| add | 是否添加按钮 | Boolean | true |false  |
+| max | 最多显示数量 | Number | 9 | -  |
+| maxVideo | 视频最大上传数量 | Number | 不限制 |  - |
+| deleteTitle| 删除提示弹窗标题 | String | 提示 |  - |
+| deleteText| 删除提示弹窗文案 | String | 您确认要删除吗? | - |
+| loading| 是否显示加载 | Boolean | true | - |
+| loadingText| 加载文案 | String | 正在上传中... | - |
+| useBeforeDelete `1.3.0`| 是否开启删除前钩子  | Boolean | false  | true |
+| useBeforeUpload  `1.3.0`| 是否开启上传前钩子 | Boolean | false  | true |
+| addImg`1.3.6`| 添加按钮图片 | String | - |  - |
+| playImg`1.3.6`| 播放按钮图片 | String | - |  - |
+| deleteImg`1.3.6`| 删除按钮图片 | String | - |  - |
+| closeImg`1.3.6`| 关闭视频按钮图片 | String | - |  - |
+
+#### imageFormData
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+| --- | --- | --- | --- | --- |
+| count | 最多可以选择的图片张数 | number |9| - |
+| sizeType | original 原图,compressed 压缩图 | array | 默认二者都有 |-  |
+| sourceType | 相册或者相机 | array |  ['camera ', 'album'] | ['camera ', 'album']  |
+| compress | 是否开启图片压缩 | Boolean | false | true  |
+| quality | 压缩质量 | number | 80 | -  |
+| size | 图片大小 | number | - | 单位MB |
+
+#### videoFromData
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+| --- | --- | --- | --- | --- |
+| maxDuration | 拍摄视频最长拍摄时间 | number |60| 最多60秒 |
+| camera | 前摄像头后摄像头 | array | - |-  |
+| compressed | 是否压缩所选的视频源文件。 | Boolean | true |-  |
+| sourceType | 相册或者相机 | array |  ['camera ', 'album'] | ['camera ', 'album']  |
+| size | 视频大小 | number | - | 单位MB |
+
+#### listStyle
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+| --- | --- | --- | --- | --- |
+| columns | 每行数量 | number |4| - |
+| columnGap | 行间距 | string | '40rpx' |-  |
+| rowGap | 列间距 | string | '40rpx' |-  |
+| padding | 列表内边距 | string | '0 0rpx' |-  |
+| ratio | 图片比例 | string | '1/1' | 低版本手机不支持,可以选择height属性  |
+| height | 图片高度 | string | '140rpx' |-  |
+| radius | 图片圆角 | string | '6rpx' |-  |
+
+#### Events
+
+| 事件名 | 说明 | 回调参数 |
+| --- | --- | --- |
+| onSuccess | 上传成功 | data: 服务器返回数据 |
+| onError | 上传失败 | error:错误信息 |
+| onImage | 点击图片 | item: 图片信息 index: 列表索引 |
+| onVideo | 点击视频 | item: 视频信息 index: 列表索引 |
+| onProgress | 上传进程 | onProgress参数说明|
+| onVideoMax | 触发视频最大数量限制 | maxVideo, fileLength|
+| onImageSize | 触发图片最大尺寸限制 | 图片信息 |
+| beforeDelete`1.3.0` | 删除前钩子 | item: 文件信息 index:文件索引 next:继续执行删除逻辑 |
+| beforeUpload `1.3.0`| 上传前钩子 | tempFile: 文件信息 next:继续执行删除逻辑 |
+
+#### onProgress参数说明
+| 事件名 | 说明 |
+| --- | --- |
+| progress | 上传进度百分比 |
+| totalBytesSent | 已经上传的数据长度 |
+| totalBytesExpectedToSend | 预期需要上传的数据总长度 |
+
+
+### [遇到问题或者讨论 uniapp 加入QQ群  553291781](https://jq.qq.com/?_wv=1027&k=5UkMN1QX)