Kaynağa Gözat

Signed-off-by: liuboyan <632697560@qq.com>
bug

liuboyan 4 yıl önce
ebeveyn
işleme
54926c0f89

+ 11 - 1
config/api.js

@@ -1,6 +1,12 @@
-const API_BASE = 'http://jihengcc.cn:17080/ypdApi'
+const API_BASE = 'http://39.100.230.190:8019/ypdApi'
 
 module.exports = {
+	//  微信授权登录
+	wxLogin: API_BASE + '/v2/coach/login/wxLogin',
+	//  账号密码登录
+	loginByPassword: API_BASE + '/v2/coach/login/login',
+	
+	
 	//  提交补课表单
 	submitExtraLessonsForm: API_BASE + '/v2/sale/class/extraLessonsSubmit',
 	//  获取补课学员列表
@@ -23,6 +29,8 @@ module.exports = {
 	getSignStudentList: API_BASE + '/v2/sale/class/getSignStudentList',
 	//  获取非本课程学员列表
 	getOtherSignStudentList: API_BASE + '/v2/sale/class/getOtherSignStudentList',
+	//  提交签到表单
+	submitSignForm: API_BASE + '/v2/sale/class/signSubmit',
 	
 	
 	//  获取消息通知列表
@@ -61,6 +69,8 @@ module.exports = {
 	getSignRankingList: API_BASE + '/v2/coach/center/getSignRankingList',
 	
 	
+	//  获取服务器端图片
+	getServerImg: API_BASE + '/download/icon/',
 	//  上传地址
 	uploadFile: API_BASE + '/v2/common/file/uploadOne',
 }

+ 8 - 7
manifest.json

@@ -1,6 +1,6 @@
 {
     "name" : "星火纵横教练端",
-    "appid" : "__UNI__ECFC1F2",
+    "appid" : "__UNI__BB6457B",
     "description" : "星火纵横教练端",
     "versionName" : "1.0.0",
     "versionCode" : "100",
@@ -49,15 +49,15 @@
                 "ad" : {},
                 "oauth" : {
                     "weixin" : {
-                        "appid" : "wxc6ab95e70160a2ee",
-                        "appsecret" : "a1eee335af123ba2f7621e6687ddb33b",
+                        "appid" : "wxeb29362c7f2e9e48",
+                        "appsecret" : "ff637286fcda117a3e5598df9338ad82",
                         "UniversalLinks" : ""
                     }
                 },
                 "payment" : {
                     "weixin" : {
                         "__platform__" : [ "android" ],
-                        "appid" : "wxc6ab95e70160a2ee",
+                        "appid" : "wxeb29362c7f2e9e48",
                         "UniversalLinks" : ""
                     }
                 }
@@ -105,9 +105,10 @@
     /* 快应用特有相关 */
     "mp-weixin" : {
         /* 小程序特有相关 */
-        "appid" : "wx0c8f390063292c1f",
+        "appid" : "wxeb29362c7f2e9e48",
         "setting" : {
-            "urlCheck" : true
+            "urlCheck" : false,
+            "minified" : false
         },
         "optimization" : {
             "subPackages" : true
@@ -125,7 +126,7 @@
             "mode" : "history",
             "base" : "./"
         },
-        "title" : "cereshop",
+        "title" : "星火纵横教练端",
         "domain" : "39.100.102.234",
         "devServer" : {
             "port" : 8080

+ 11 - 1
pages.json

@@ -18,6 +18,11 @@
 		"style": {
 			"navigationBarTitleText": "个人"
 		}
+	}, {
+		"path": "pages/login/index",
+		"style": {
+			"navigationStyle": "custom"
+		}
 	}],
 	"subPackages": [{
 		"root": "pagesClass",
@@ -51,11 +56,16 @@
 			"style": {
 				"navigationBarTitleText": "成长历程"
 			}
-		},  {
+		}, {
 			"path": "signForm",
 			"style": {
 				"navigationBarTitleText": "签到"
 			}
+		}, {
+			"path": "signStudentList",
+			"style": {
+				"navigationBarTitleText": "选择学员"
+			}
 		}, {
 			"path": "timetable",
 			"style": {

+ 14 - 2
pages/index/index.vue

@@ -45,11 +45,23 @@
 				]
 			}
 		},
-		onLoad() {},
-		onReady() {},
+		onLoad() {
+			// uni.setStorage({
+			// 	key: 'token',
+			// 	data: 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDT0FDSDo4OSJ9.7DPbdHQIkhpCXE2Tf6eHkdl0Y69C6kbRwntVdKm4DXu1_LlP1ue_2t4LKW0CYLm-enBRzBvIxkgvYjZSoKhqSA'
+			// })
+			if (!uni.getStorageSync('token')) {
+				uni.navigateTo({
+					url: '/pages/login/index'
+				});
+			}
+		},
 		methods: {
 			//  跳转
 			goToHandle(item) {
+				uni.removeStorageSync({
+					key: 'extraLessonsUserList'
+				})
 				uni.navigateTo({
 					url: '/pagesClass/' + item.path
 				});

+ 171 - 10
pages/login/index.vue

@@ -1,30 +1,191 @@
 <template>
 	<view class="content">
-		login
+		<u-image :src="logo" mode="aspectFit" width="208rpx" height="272rpx" class="logo"></u-image>
+		<u-card :show-head="false" :show-foot="false" padding="40" margin="40rpx 40rpx" borderRadius="40" box-shadow="0 0 8px rgba(0, 0, 0, 0.2)"
+		 class="card-box">
+			<view slot="body">
+				<u-cell-group :border="false">
+					<u-cell-item icon="account" icon-size="46" :icon-style="iconStyle" :arrow="false">
+						<u-input v-model="account" />
+					</u-cell-item>
+					<u-cell-item icon="lock" icon-size="46" :icon-style="iconStyle" :arrow="false">
+						<u-input v-model="password" type="password" :password-icon="true" />
+					</u-cell-item>
+				</u-cell-group>
+				<u-button type="warning" :ripple="true" :custom-style="{...customStyle, margin: '10px 0'}" @click="login()">登录</u-button>
+				<u-button type="warning" :ripple="true" :custom-style="{...customStyle, margin: '10px 0'}" @click="login1()">app微信授权登录</u-button>
+				<u-button type="warning" :ripple="true" :custom-style="{...customStyle, backgroundColor: '#07c160'}" open-type="getUserInfo"
+				 @getuserinfo="getUserinfoWechat" withCredentials="true">微信一键登录</u-button>
+			</view>
+		</u-card>
 		<u-top-tips ref="uTips"></u-top-tips>
 	</view>
 </template>
 
 <script>
+	import {
+		mapGetters
+	} from 'vuex'
 	const NET = require('@/utils/request')
 	const API = require('@/config/api')
 	export default {
+		computed: {
+			...mapGetters([
+				'customStyle',
+			])
+		},
 		data() {
-			return {}
+			return {
+				logo: API.getServerImg + 'logo.png',
+				account: '',
+				password: '',
+				iconStyle: {
+					color: '#999999',
+					marginRight: '10px'
+				}
+			}
 		},
 		onLoad() {},
-		methods: {}
+		methods: {
+			login() {
+				if (!this.account || !this.password) {
+					this.$refs.uTips.show({
+						title: '请输入账号密码',
+						type: 'warning',
+					})
+					return
+				}
+				NET.request(API.loginByPassword, {
+					phone: this.account,
+					pwd: this.password,
+				}, 'POST').then(res => {
+					uni.setStorage({
+						key: 'token',
+						data: res.data.token
+					})
+					uni.setStorage({
+						key: 'userData',
+						data: {
+							headImage: res.data.url,
+							userId: res.data.id,
+							nickName: res.data.nickName,
+							userName: res.data.username,
+							phone: res.data.phone,
+						}
+					})
+					uni.reLaunch({
+						url: '/pages/index/index'
+					})
+				}).catch(error => {
+					this.$refs.uTips.show({
+						title: '登录失败',
+						type: 'warning',
+					})
+				})
+			},
+			login1() {
+				let that = this
+				uni.login({
+					provider: 'weixin',
+					success: function(loginRes) {
+						uni.getUserInfo({
+							provider: 'weixin',
+							success: function(infoRes) {
+							}
+						});
+					}
+				});
+			},
+			getUserinfoWechat(data) {
+				uni.getProvider({
+					service: 'oauth',
+					success: (res) => {
+						if (~res.provider.indexOf('weixin')) {
+							uni.login({
+								provider: 'weixin',
+								success: (res2) => {
+									let wxLoginData = {
+										code: res2.code,
+										encryptedData: data.detail.encryptedData,
+										iv: data.detail.iv,
+										// shareParams: uni.getStorageSync('shareParams')
+									}
+									uni.setStorage({
+										key: 'wxLoginData',
+										data: wxLoginData
+									})
+									this.getUserInfo(wxLoginData)
+								},
+								fail: () => {
+									this.$refs.uTips.show({
+										title: '微信登录授权失败',
+										type: 'warning',
+									})
+								}
+							})
+						} else {
+							this.$refs.uTips.show({
+								title: '请先安装微信或升级版本',
+								type: 'warning',
+							})
+						}
+					}
+				});
+			},
+			// 发请求获取个人数据
+			getUserInfo(wxLoginData) {
+				// console.log(wxLoginData)
+				// return
+				NET.request(API.wxLogin, wxLoginData, 'POST').then(res => {
+					uni.setStorage({
+						key: 'token',
+						data: res.data.token
+					})
+					uni.setStorage({
+						key: 'userData',
+						data: {
+							headImage: res.data.url,
+							userId: res.data.id,
+							nickName: res.data.nickName,
+							userName: res.data.username,
+							phone: res.data.phone,
+						}
+					})
+					uni.reLaunch({
+						url: '/pages/index/index'
+					})
+				}).catch(error => {
+					this.$refs.uTips.show({
+						title: error.message,
+						type: 'warning',
+					})
+				})
+			},
+		}
 	}
 </script>
 
-<style lang='scss'>
-	page {
-		width: 100%;
-		height: 100%;
-	}
-
+<style lang="scss" scoped>
 	.content {
 		width: 100%;
-		height: 100%;
+		height: 100vh;
+		padding: 480rpx 0 60px 0;
+		float: left;
+
+		.logo {
+			top: 140rpx;
+			left: 50%;
+			transform: translateX(-50%);
+			position: absolute;
+		}
+
+		.card-box {
+			/deep/.u-cell {
+				border: 1px solid #eeeeee;
+				border-radius: 8px;
+				padding: 8px 10px;
+				margin-bottom: 16px;
+			}
+		}
 	}
 </style>

+ 4 - 4
pages/news/index.vue

@@ -114,17 +114,17 @@
 			//  获取列表数据
 			getTableList(index, refresh) {
 				NET.request(API.getMessageList, {
-					status: index,
+					type: index,
 					page: this.tabList[index].pageIndex,
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.list)
-					this.tabList[index].isOver = res.data.list.length != 10
+					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.row)
+					this.tabList[index].isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})

+ 28 - 20
pages/user/index.vue

@@ -2,16 +2,16 @@
 	<view class="content">
 		<view class="user-info">
 			<view class="user-data">
-				<view class="user-name">自行车</view>
-				<view class="user-phone">234567865</view>
+				<view class="user-name">{{userData.nickName}}</view>
+				<view class="user-phone">{{userData.phone}}</view>
 			</view>
 			<view class="user-img">
-				<u-avatar :src="src" size="160"></u-avatar>
+				<u-avatar :src="userData.headImage" size="160"></u-avatar>
 			</view>
 		</view>
 		<view class="user-handle" v-for="(item, index) in handleList" :key="index" @click="goToHandle(item)">
 			<view class="handle-icon">
-				<view class="iconfont" :class="item.icon"></view>
+				<u-image :src="item.icon" mode="aspectFit" width="28px" height="28px"></u-image>
 			</view>
 			<view class="handle-label">{{item.label}}</view>
 			<view class="handle-arrow">
@@ -28,41 +28,51 @@
 	export default {
 		data() {
 			return {
-				src: '',
+				userData: {
+					headImage: '',
+					userId: '',
+					nickName: '',
+					userName: '',
+					phone: '',
+				},
 				handleList: [{
 						label: '我的评价',
 						path: 'evaluateList',
-						icon: 'iconpingjia'
+						icon: API.getServerImg + 'pingjia.png'
 					},
 					{
 						label: '补课记录',
 						path: 'extraLessonsList',
-						icon: 'icondingdan'
+						icon: API.getServerImg + 'dingdanguanli.png'
 					},
 					{
 						label: '请假记录',
 						path: 'leaveLessonsList',
-						icon: 'iconqingjiajilu'
+						icon: API.getServerImg + 'qingjia.png'
 					},
 					{
 						label: '续费率/签到率排行',
 						path: 'ratioRank',
-						icon: 'iconpaihang'
+						icon: API.getServerImg + 'paihang.png'
 					},
 					{
 						label: '续费提醒',
 						path: 'renewList',
-						icon: 'iconxufeitixing'
+						icon: API.getServerImg + 'xufeitixing.png'
 					},
 					{
 						label: '签到提醒',
 						path: 'signList',
-						icon: 'iconqiandaotixing'
+						icon: API.getServerImg + 'xiaoxi.png'
 					},
 				]
 			}
 		},
-		onLoad() {},
+		onLoad() {
+			if (uni.getStorageSync('userData')) {
+				this.userData = uni.getStorageSync('userData')
+			}
+		},
 		methods: {
 			//  跳转我的各列表界面
 			goToHandle(site) {
@@ -135,24 +145,22 @@
 			float: left;
 			padding: 0 15px;
 			margin-bottom: 10px;
+			display: flex;
+			align-items: center;
 
 			.handle-icon {
 				width: 40px;
 				height: 40px;
-				float: left;
 				text-align: center;
 				line-height: 40px;
-
-				.iconfont {
-					font-size: 28px;
-				}
+				display: flex;
+				align-items: center;
 			}
 
 			.handle-label {
-				width: calc(100% - 70px);
 				height: 40px;
+				flex: 1;
 				margin-left: 10px;
-				float: left;
 				line-height: 40px;
 				font-size: 15px;
 			}
@@ -162,4 +170,4 @@
 			}
 		}
 	}
-</style>
+</style>

+ 3 - 2
pagesClass/addClassForm.vue

@@ -150,7 +150,7 @@
 					second: true
 				},
 				venueShow: false,
-				venueList: [{name:'删除删除删除删除'},{name:'删除删除删除删除'},{name:'删除删除删除删除'},{name:'删除删除删除删除'},{name:'删除删除删除删除'},],
+				venueList: [],
 				options: [{
 					text: '删除',
 					style: {
@@ -167,7 +167,7 @@
 				this.venueList = this.res.data.row
 			}).catch(error => {
 				this.$refs.uTips.show({
-					title: error.data.msg,
+					title: error.message,
 					type: 'warning',
 				})
 			})
@@ -263,6 +263,7 @@
 </style>
 <style lang="scss" scoped>
 	@import "@/static/css/themes.scss";
+
 	.content {
 		width: 100%;
 		float: left;

+ 9 - 32
pagesClass/classDetail.vue

@@ -60,31 +60,7 @@
 					classStartHours: '',
 					residue: '',
 					address: '',
-					studentSignList: [{
-							"name": "班级近",
-							"state": 0,
-						},
-						{
-							"name": "班级近",
-							"state": 0,
-						},
-						{
-							"name": "班级近",
-							"state": 1,
-						},
-						{
-							"name": "班级近",
-							"state": 1,
-						},
-						{
-							"name": "班级近",
-							"state": 0,
-						},
-						{
-							"name": "班级近",
-							"state": 0,
-						},
-					],
+					studentSignList: [],
 					showList: [],
 				},
 				cardStyle: {
@@ -113,7 +89,7 @@
 					uni.stopPullDownRefresh();
 				}).catch(error => {
 					this.$refs.uTips.show({
-						title: '获取班级详情失败',
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -121,14 +97,12 @@
 			//  跳转学生详情
 			goToStudentInfo(item) {
 				uni.navigateTo({
-					url: '/pagesMain/studentInfo?type=1&id=' + item.id
+					url: '/pagesMain/studentInfo?type=1&id=' + item.id + '&classId=' + this.classId
 				});
 			},
 			//  跳转签到表单
 			goToSignForm() {
-				uni.removeStorageSync({
-					key: 'signUserList'
-				})
+				uni.removeStorageSync('signUserList')
 				uni.navigateTo({
 					url: '/pagesClass/signForm?id=' + this.classId + '&status=' + this.classInfo.signStatus
 				});
@@ -156,6 +130,10 @@
 		.class-info-text {
 			color: #999999;
 			line-height: 18px;
+
+			u-icon {
+				margin-right: 4px;
+			}
 		}
 
 		.class-student-col {
@@ -186,8 +164,7 @@
 		}
 
 		.class-show-box {
-			width: calc(100% + 32px);
-			margin: 0 -16px;
+			width: 100%;
 			float: left;
 
 			.class-show-card {

+ 3 - 19
pagesClass/classList.vue

@@ -52,23 +52,7 @@
 					status: 3,
 					isOver: false,
 					pageIndex: 1,
-					tableList: [{
-							"address": "请假到等",
-							"className": "",
-							"name": "请假签到等",
-							"residue": 8,
-							"classStartDate": "2020-01-01",
-							"classStartHours": "19:20"
-						},
-						{
-							"address": "请假到等",
-							"className": "",
-							"name": "请假签到等",
-							"residue": 8,
-							"classStartDate": "2020-01-01",
-							"classStartHours": "19:20"
-						},
-					],
+					tableList: [],
 				}, {
 					name: '已开班',
 					status: 1,
@@ -96,7 +80,7 @@
 			this.tabList.forEach((site, index) => {
 				site.isOver = false
 				site.pageIndex = 1
-				// site.tableList = []
+				site.tableList = []
 				this.getTableList(index)
 			})
 		},
@@ -150,7 +134,7 @@
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})

+ 34 - 13
pagesClass/courseForm.vue

@@ -4,8 +4,9 @@
 			<u-form-item label="记录内容" prop="content" required>
 				<u-input v-model="form.content" placeholder="请输入记录内容" type="textarea" auto-height />
 			</u-form-item>
-			<u-form-item label="上传附件" prop="estimateContent" required required label-position="top">
-				<u-upload :action="uploadAaction" :head="uploadHeader" max-count="5" :multiple="false" :file-list="fileList"></u-upload>
+			<u-form-item label="上传附件" label-position="top">
+				<u-upload max-count="5" :multiple="false" :action="uploadUrl" :header="uploadHeader" @on-success="uploadSuccess"
+				 @on-error="uploadError" @on-remove="uploadRemove"></u-upload>
 			</u-form-item>
 		</u-form>
 		<view class="handle-fix-box">
@@ -30,19 +31,16 @@
 		data() {
 			return {
 				id: '',
+				classId: '',
 				form: {
-					level: 0,
 					content: '',
+					fileId: [],
+				},
+				uploadUrl: API.uploadFile,
+				uploadHeader: {
+					Authorization: uni.getStorageSync('token')
 				},
-				uploadAaction: '',
-				uploadHeader: {},
-				fileList: [],
 				rules: {
-					level: [{
-						required: true,
-						message: '请选择评价星级',
-						trigger: 'change'
-					}],
 					content: [{
 						required: true,
 						message: '请输入评价内容',
@@ -53,17 +51,39 @@
 		},
 		onLoad(options) {
 			this.id = options.id
+			this.classId = options.classId
 		},
 		onReady() {
 			this.$refs.form.setRules(this.rules);
 		},
 		methods: {
+			//  文件上传成功回调
+			uploadSuccess(res, index, lists, name) {
+				this.form.fileId.push(res.data.id)
+				this.$refs.uTips.show({
+					title: '文件上传成功',
+					type: 'success',
+				})
+				return true
+			},
+			//  文件上传失败回调
+			uploadError(res, index, lists, name) {
+				this.$refs.uTips.show({
+					title: '文件上传失败',
+					type: 'warning',
+				})
+			},
+			//  移除文件回调
+			uploadRemove(index, lists, name) {
+				this.form.fileId.splice(index, 1)
+			},
 			//  提交表单
 			submitForm() {
 				this.$refs.form.validate(valid => {
 					if (valid) {
 						NET.request(API.submitCourseForm, {
-							id: this.id,
+							studentId: this.id,
+							id: this.classId,
 							...this.form
 						}, 'POST').then(res => {
 							this.$refs.uTips.show({
@@ -75,7 +95,7 @@
 							}, 1000)
 						}).catch(error => {
 							this.$refs.uTips.show({
-								title: '提交失败',
+								title: error.message,
 								type: 'warning',
 							})
 						})
@@ -95,6 +115,7 @@
 </style>
 <style lang="scss" scoped>
 	@import "@/static/css/themes.scss";
+
 	.content {
 		width: 100%;
 		float: left;

+ 1 - 4
pagesClass/extraLessonsForm.vue

@@ -82,9 +82,6 @@
 								}
 							})
 						}, 'POST').then(res => {
-							uni.removeStorageSync({
-								key: 'extraLessonsUserList'
-							})
 							this.$refs.uTips.show({
 								title: '提交成功',
 								type: 'success',
@@ -94,7 +91,7 @@
 							}, 1000)
 						}).catch(error => {
 							this.$refs.uTips.show({
-								title: '提交失败',
+								title: error.message,
 								type: 'warning',
 							})
 						})

+ 5 - 5
pagesClass/extraLessonsStudentList.vue

@@ -77,19 +77,19 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					res.data.list.forEach(site => site.checked = false)
-					this.tableList = this.tableList.concat(res.data.list)
-					this.isOver = res.data.list.length != 10
+					res.data.row.forEach(site => site.checked = false)
+					this.tableList = this.tableList.concat(res.data.row)
+					this.isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
 			},
 			//  变更状态
-			changeStatus(item) { 
+			changeStatus(item) {
 				item.checked = !item.checked
 			},
 			//  选择学员

+ 103 - 47
pagesClass/signForm.vue

@@ -2,17 +2,21 @@
 	<view class="content">
 		<u-card :title="'总数(' + studentList.length + ')人'" title-size="32" margin="0px 0px 10px 0px" :head-style="cardStyle">
 			<u-grid :col="3" slot="body" :border="false">
-				<u-grid-item v-for="(item, index) in studentList" :key="index" :custom-style="gridCustomStyle">
-					<u-avatar :src="item.url" mode="circle" size="128" :show-level="item.state == 1 || item.checked" level-icon="checkbox-mark"
-					 :level-bg-color="mainColor" v-if="item.hasHead == 1" @click="setSignCheck(item)"></u-avatar>
-					<u-upload :custom-btn="true" :max-count="1" @on-success="uploadSuccess" @on-error="uploadError" v-else>
-						<view slot="addBtn" class="slot-btn">
-							<u-icon name="camera-fill" size="60" color="#999999"></u-icon>
-						</view>
-					</u-upload>
-					<view class="grid-text">{{item.name}}</view>
+				<u-grid-item v-for="(item, index) in studentList" :key="index" :custom-style="gridCustomStyle" @click.native.stop="setSignCheck(item)">
+					<view class="avatar-box">
+						<u-avatar :src="item.url" mode="circle" size="160" v-if="item.hasHead == 1"></u-avatar>
+						<u-upload :custom-btn="true" :max-count="1" :index="index" :multiple="false" :show-progress="false"
+						 :preview-full-image="false" :action="uploadUrl" :header="uploadHeader" @on-success="uploadSuccess" @on-error="uploadError"
+						 @on-remove="uploadRemove" @on-preview-error="uploadPreview" v-else>
+							<view slot="addBtn" class="slot-btn">
+								<u-icon name="camera-fill" size="60" color="#999999"></u-icon>
+							</view>
+						</u-upload>
+						<u-icon name="checkmark-circle-fill" v-if="item.state == 1 || item.checked" class="check-icon"></u-icon>
+						<view class="grid-text">{{item.name}}</view>
+					</view>
 				</u-grid-item>
-				<u-grid-item :custom-style="gridCustomStyle" @click="goToSelectStudent">
+				<u-grid-item :custom-style="gridCustomStyle" @click="goToSelectStudent()">
 					<view class="slot-btn">
 						<u-icon name="plus" size="60" color="#999999"></u-icon>
 					</view>
@@ -20,7 +24,10 @@
 			</u-grid>
 		</u-card>
 		<view class="handle-fix-box">
-			<u-button type="warning" shape="circle" :ripple="true" :custom-style="customStyle" :disabled="status != 0" @click="goToSignForm()">{{status == 1 ? '签到确认' : '今日已签到'}}</u-button>
+			<u-button type="warning" shape="circle" :ripple="true" :custom-style="customStyle" @click="submitForm()">
+				{{status == 1 ? '签到确认' : '今日已签到'}}
+				 <!-- :disabled="status != 0" -->
+			</u-button>
 		</view>
 		<u-top-tips ref="uTips"></u-top-tips>
 	</view>
@@ -43,37 +50,11 @@
 			return {
 				classId: '',
 				status: '',
-				studentList: [{
-					"hasHead": 1,
-					"name": "即预约",
-					"state": 0,
-					"checked": false,
-					"url": ""
-				}, {
-					"hasHead": 1,
-					"name": "即预约",
-					"state": 0,
-					"checked": false,
-					"url": ""
-				}, {
-					"hasHead": 1,
-					"name": "即预约",
-					"state": 1,
-					"checked": false,
-					"url": ""
-				}, {
-					"hasHead": 0,
-					"name": "即预约",
-					"state": 0,
-					"checked": false,
-					"url": ""
-				}, {
-					"hasHead": 1,
-					"name": "即预约",
-					"state": 1,
-					"checked": false,
-					"url": ""
-				}, ],
+				uploadUrl: API.uploadFile,
+				uploadHeader: {
+					Authorization: uni.getStorageSync('token')
+				},
+				studentList: [],
 				cardStyle: {
 					fontWeight: 'bold'
 				},
@@ -96,16 +77,18 @@
 					id: this.classId
 				}, 'POST').then(res => {
 					res.data.forEach(site => site.checked = false)
-					this.studentList = res.data.concat(uni.getStorageSync('extraLessonsUserList'))
+					this.studentList = res.data.concat(uni.getStorageSync('signUserList').length ? uni.getStorageSync('signUserList') : [])
 				}).catch(error => {
 					this.$refs.uTips.show({
-						title: '获取本课程学员列表失败',
+						title: error.message,
 						type: 'warning',
 					})
 				})
 			},
 			//  文件上传成功回调
 			uploadSuccess(res, index, lists, name) {
+				this.studentList[name].url = res.data.url
+				this.studentList[name].fileId = res.data.id
 				this.$refs.uTips.show({
 					title: '文件上传成功',
 					type: 'success',
@@ -119,6 +102,17 @@
 					type: 'warning',
 				})
 			},
+			//  移除文件回调
+			uploadRemove(index, lists, name) {
+				this.studentList[name].url = ''
+				this.studentList[name].fileId = ''
+			},
+			//  预览文件回调
+			uploadPreview(url, lists, name) {
+				if (this.status == 0) {
+					this.studentList[name].checked = this.studentList[name].checked ? false : true
+				}
+			},
 			//  设置是否签到
 			setSignCheck(item) {
 				if (this.status == 0) {
@@ -137,6 +131,37 @@
 					url: '/pagesMember/signForm?id=' + this.classId
 				});
 			},
+			//  提交表单
+			submitForm() {
+				NET.request(API.submitSignForm, {
+					classId: this.classId,
+					signStudentIdList: this.studentList.filter(site => site.state == 1 || site.checked).map(site => {
+						if (site.fileId) {
+							return {
+								id: site.id,
+								fileId: site.fileId,
+							}
+						} else {
+							return {
+								id: site.id,
+							}
+						}
+					})
+				}, 'POST').then(res => {
+					this.$refs.uTips.show({
+						title: '签到成功',
+						type: 'success',
+					})
+					setTimeout(() => {
+						uni.navigateBack()
+					}, 1000)
+				}).catch(error => {
+					this.$refs.uTips.show({
+						title: error.message,
+						type: 'warning',
+					})
+				})
+			},
 		},
 	}
 </script>
@@ -156,19 +181,50 @@
 		float: left;
 		padding-bottom: 60px;
 
+		/deep/.u-list-item {
+			width: 160rpx !important;
+			height: 160rpx !important;
+			border-radius: 50% !important;
+			margin: 0 !important;
+		}
+
 		/deep/.u-avatar__level {
 			left: 50%;
 			transform: translateX(-50%);
 			bottom: -6px !important;
 		}
 
+		/deep/.u-upload {
+			height: 160rpx !important;
+		}
+
+		.check-icon {
+			color: $mainColor;
+			font-size: 20px;
+			color: #ff6e3e;
+			font-size: 16px;
+			position: absolute;
+			top: 136rpx;
+			left: 50%;
+			transform: translateX(-50%);
+			background: #ffffff;
+			border-radius: 50%;
+		}
+
+		.avatar-box {
+			height: 200rpx;
+			position: relative;
+		}
+
 		.grid-text {
-			line-height: 18px;
+			line-height: 40rpx;
+			text-align: center;
+			margin-top: 10rpx;
 		}
 
 		.slot-btn {
-			width: 128rpx;
-			height: 128rpx;
+			width: 160rpx;
+			height: 160rpx;
 			border-radius: 50%;
 			border: 1px solid #999999;
 			display: flex;

+ 14 - 12
pagesClass/signStudentList.vue

@@ -77,32 +77,34 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					res.data.list.forEach(site => site.checked = false)
-					this.tableList = this.tableList.concat(res.data.list)
-					this.isOver = res.data.list.length != 10
+					res.data.row.forEach(site => site.checked = false)
+					this.tableList = this.tableList.concat(res.data.row)
+					this.isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
 			},
 			//  变更状态
-			changeStatus(item) { 
+			changeStatus(item) {
 				item.checked = !item.checked
 			},
 			//  选择学员
 			selectStudent() {
 				uni.setStorage({
 					key: 'signUserList',
-					data: this.tableList.filter(site => site.checked).map(site=>{return {
-						id: site.id,
-						name: site.name,
-						url: site.url,
-						hasUrl: site.hasUrl,
-						checked: false,
-					}})
+					data: this.tableList.filter(site => site.checked).map(site => {
+						return {
+							id: site.id,
+							name: site.name,
+							url: site.url,
+							hasUrl: site.hasUrl,
+							checked: false,
+						}
+					})
 				})
 				uni.navigateBack()
 			}

+ 1 - 1
pagesClass/timetable.vue

@@ -33,7 +33,7 @@
 				})
 			}).catch(error => {
 				this.$refs.uTips.show({
-					title: error.data.msg,
+					title: error.message,
 					type: 'warning',
 				})
 			})

+ 2 - 1
pagesMain/communicateForm.vue

@@ -65,7 +65,7 @@
 							}, 1000)
 						}).catch(error => {
 							this.$refs.uTips.show({
-								title: '提交失败',
+								title: error.message,
 								type: 'warning',
 							})
 						})
@@ -85,6 +85,7 @@
 </style>
 <style lang="scss" scoped>
 	@import "@/static/css/themes.scss";
+
 	.content {
 		width: 100%;
 		float: left;

+ 1 - 1
pagesMain/evaluateDetail.vue

@@ -58,7 +58,7 @@
 					uni.stopPullDownRefresh();
 				}).catch(error => {
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})

+ 5 - 3
pagesMain/evaluateList.vue

@@ -85,12 +85,12 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tableList = this.tableList.concat(res.data.list)
-					this.isOver = res.data.list.length != 10
+					this.tableList = this.tableList.concat(res.data.row)
+					this.isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -121,6 +121,8 @@
 		.scroll-box {
 			width: 100%;
 			height: 100vh;
+			box-sizing: border-box;
+			padding-bottom: 10px;
 
 			.class-card {
 

+ 6 - 4
pagesMain/extraLessonsList.vue

@@ -7,7 +7,7 @@
 			 class="card-container" @click="goToDetail(item)">
 				<view class="card-content" slot="head" style="display: flex;">
 					<view class="card-img">
-						<u-avatar :src="url" size="100"></u-avatar>
+						<u-avatar :src="item.url" size="100"></u-avatar>
 					</view>
 					<view>
 						<view class="card-title">{{item.name}}</view>
@@ -80,12 +80,12 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tableList = this.tableList.concat(res.data.list)
-					this.isOver = res.data.list.length != 10
+					this.tableList = this.tableList.concat(res.data.row)
+					this.isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -116,6 +116,8 @@
 		.scroll-box {
 			width: 100%;
 			height: 100vh;
+			box-sizing: border-box;
+			padding-bottom: 10px;
 
 			.card-container {
 				.card-content {

+ 9 - 7
pagesMain/leaveLessonsList.vue

@@ -7,16 +7,16 @@
 			 class="card-container" @click="goToDetail(item)">
 				<view class="card-content" slot="head" style="display: flex;">
 					<view class="card-img">
-						<u-avatar :src="url" size="100"></u-avatar>
+						<u-avatar :src="item.url" size="100"></u-avatar>
 					</view>
 					<view>
-						<view class="card-title">{{item.name}}</view>
+						<view class="card-title">{{item.studentName}}</view>
 						<view class="card-info-text">手机号:{{item.phone}}</view>
-						<view class="card-info-text">地址:{{item.address}}</view>
+						<view class="card-info-text">地址:{{item.className}}</view>
 					</view>
 				</view>
 				<view class="card-content" slot="body">
-					<view class="card-info-text">{{item.remark}}</view>
+					<view class="card-info-text">{{item.leaveReason}}</view>
 				</view>
 				<view class="card-content" slot="foot" style="padding-top: 0;">
 					<view class="card-info-text" style="text-align: right;">{{item.time}}</view>
@@ -80,12 +80,12 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tableList = this.tableList.concat(res.data.list)
-					this.isOver = res.data.list.length != 10
+					this.tableList = this.tableList.concat(res.data.row)
+					this.isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -116,6 +116,8 @@
 		.scroll-box {
 			width: 100%;
 			height: 100vh;
+			box-sizing: border-box;
+			padding-bottom: 10px;
 
 			.card-container {
 				.card-content {

+ 33 - 47
pagesMain/ratioRank.vue

@@ -13,12 +13,16 @@
 					</view>
 					<view class="rank-box">
 						<u-line-progress :active-color="mainColor" :percent="item.percent" height="40"></u-line-progress>
-						<text class="rank-text" v-if="index1 == 1">签到人次:{{item.signCount}}</text>
+						<text class="rank-text" v-if="index1 == 1">签到人次:{{item.signCount}}</text>
 					</view>
 					<u-card :head-border-bottom="false" :foot-border-top="false" padding="0px" margin="0" borderRadius="0" v-for="(site, index2) in item.tableList"
 					 :key="index2" class="class-card" @click="goToStudent(site)">
 						<view class="class-content" slot="body">
-							<view class="class-info-sort">{{index2}}</view>
+							<view class="info-sort">
+								<u-image :src="index2 == 0 ? iconPath1 : (index2 == 1 ? iconPath2 : iconPath3)" mode="aspectFit" width="24px"
+								 height="24px" v-if="index2 < 3"></u-image>
+								<text v-else>{{index2 + 1}}</text>
+							</view>
 							<view class="class-info-text">{{site.name}}&nbsp;&nbsp;{{index1 == 1 ? site.residue + '课时' :''}}</view>
 							<view class="class-info-img">
 								<u-avatar :src="site.url" size="100"></u-avatar>
@@ -49,6 +53,9 @@
 		},
 		data() {
 			return {
+				iconPath1: API.getServerImg + 'jinpai.png',
+				iconPath2: API.getServerImg + 'yinpai.png',
+				iconPath3: API.getServerImg + 'tongpai.png',
 				triggered: false,
 				tabList: [{
 					name: '续费率',
@@ -56,49 +63,16 @@
 					pageIndex: 1,
 					year: '',
 					month: '',
-					percent: 20,
+					percent: 0,
 					signCount: '',
-					tableList: [
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-									{
-										"name": "续费率",
-										"url": ""
-									},
-					],
+					tableList: [],
 				}, {
 					name: '签到率',
 					isOver: false,
 					pageIndex: 1,
 					year: '',
 					month: '',
-					percent: '',
+					percent: 0,
 					signCount: '',
 					tableList: [],
 				}],
@@ -179,12 +153,12 @@
 					size: 20,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.list)
-					this.tabList[index].isOver = res.data.list.length != 20
+					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.row)
+					this.tabList[index].isOver = res.data.row.length != 20
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -194,11 +168,11 @@
 				NET.request(index == 0 ? API.getRenewPercent : API.getSignPercent, {
 					date: this.tabList[index].year + '-' + this.tabList[index].month,
 				}, 'POST').then(res => {
-					this.tabList[index].percent = res.data.percent
-					this.tabList[index].signCount = res.data.signCount
+					this.tabList[index].percent = parseFloat(res.data.percent)
+					this.tabList[index].signCount = JSON.stringify(res.data.signCount)
 				}).catch(error => {
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -237,6 +211,7 @@
 						justify-content: space-between;
 						align-items: center;
 						padding: 10px 15px;
+						background-color: #FFFFFF;
 
 						.filter-date {
 							text-align: center;
@@ -253,11 +228,13 @@
 						justify-content: space-between;
 						align-items: center;
 						padding: 0 15px;
+						background-color: #FFFFFF;
 
 						u-line-progress {
 							flex: 1;
 						}
-						.rank-text{
+
+						.rank-text {
 							padding-left: 10px;
 							font-size: 11px;
 							color: $mainColor;
@@ -272,8 +249,12 @@
 							display: flex;
 							align-items: center;
 
-							.class-info-sort {
-								width: 50px;
+							.info-sort {
+								width: 34px;
+								display: flex;
+								align-items: center;
+								justify-content: center;
+								margin-right: 5px;
 							}
 
 							.class-info-img {
@@ -299,5 +280,10 @@
 				}
 			}
 		}
+
+		u-divider {
+			padding: 10px 0;
+			display: flex;
+		}
 	}
 </style>

+ 6 - 5
pagesMain/renewList.vue

@@ -1,7 +1,6 @@
 <template>
 	<view class="content">
-		<u-subsection mode="subsection" :active-color="mainColor" :list="tabList" :current="current"
-		 @change="tabsChange"></u-subsection>
+		<u-subsection mode="subsection" :active-color="mainColor" :list="tabList" :current="current" @change="tabsChange"></u-subsection>
 		<swiper :current="swiperCurrent" @animationfinish="animationfinish" class="swiper-box">
 			<swiper-item class="swiper-item" v-for="(item, index1) in tabList" :key="index1">
 				<scroll-view scroll-y class="scroll-box" @scrolltolower="handleLoadMore" :refresher-enabled="true"
@@ -101,12 +100,12 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.list)
-					this.tabList[index].isOver = res.data.list.length != 10
+					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.row)
+					this.tabList[index].isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -144,6 +143,8 @@
 				.scroll-box {
 					width: 100%;
 					height: calc(100vh - 34px);
+					box-sizing: border-box;
+					padding-bottom: 10px;
 
 					.class-card {
 						border-bottom: 1px solid #cccccc;

+ 6 - 27
pagesMain/signList.vue

@@ -46,30 +46,7 @@
 					name: '5次未签到',
 					isOver: false,
 					pageIndex: 1,
-					tableList: [
-
-						{
-							"id": 0,
-							"name": "没有更多了",
-							"residue": 0,
-							"type": 0,
-							"url": ""
-						},
-						{
-							"id": 0,
-							"name": "没有更多了",
-							"residue": 0,
-							"type": 0,
-							"url": ""
-						},
-						{
-							"id": 0,
-							"name": "没有更多了",
-							"residue": 0,
-							"type": 0,
-							"url": ""
-						},
-					],
+					tableList: [],
 				}, {
 					name: '10次以上未签到',
 					isOver: false,
@@ -124,12 +101,12 @@
 					size: 10,
 				}, 'POST').then(res => {
 					this.triggered = false
-					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.list)
-					this.tabList[index].isOver = res.data.list.length != 10
+					this.tabList[index].tableList = this.tabList[index].tableList.concat(res.data.row)
+					this.tabList[index].isOver = res.data.row.length != 10
 				}).catch(error => {
 					this.triggered = false
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -167,6 +144,8 @@
 				.scroll-box {
 					width: 100%;
 					height: calc(100vh - 34px);
+					box-sizing: border-box;
+					padding-bottom: 10px;
 
 					.class-card {
 						border-bottom: 1px solid #cccccc;

+ 26 - 46
pagesMain/studentInfo.vue

@@ -6,11 +6,11 @@
 			<u-cell-group>
 				<u-cell-item :arrow="false" :title-style="{width: '100%'}">
 					<view slot="title" style="display: flex;justify-content: center;">
-						<u-avatar :src="studentInfo.studentImg" size="140"></u-avatar>
+						<u-avatar :src="studentInfo.studentImg || studentInfo.url" size="140"></u-avatar>
 					</view>
 				</u-cell-item>
-				<u-cell-item title="学生姓名" :value="studentInfo.studentName" :arrow="false"></u-cell-item>
-				<u-cell-item title="家长姓名" :value="studentInfo.fatherName" :arrow="false"></u-cell-item>
+				<u-cell-item title="学生姓名" :value="studentInfo.studentName || studentInfo.name" :arrow="false"></u-cell-item>
+				<u-cell-item title="家长姓名" :value="studentInfo.fatherName || studentInfo.parentName" :arrow="false"></u-cell-item>
 				<u-cell-item title="学生性别" :value="studentInfo.sex" :arrow="false"></u-cell-item>
 				<u-cell-item title="学生年龄" :value="studentInfo.age" :arrow="false"></u-cell-item>
 				<u-cell-item title="手机号码" :value="studentInfo.phone" :arrow="false"></u-cell-item>
@@ -43,6 +43,7 @@
 		data() {
 			return {
 				id: '',
+				classId: '',
 				type: '',
 				studentInfo: {
 					studentImg: '',
@@ -50,42 +51,18 @@
 					fatherName: '',
 					sex: '',
 					age: '',
-					phone: '1234567543',
+					phone: '',
 				},
 				triggered: false,
 				isOver: false,
 				pageIndex: 1,
-				tableList: [{
-						coachName: '填写沟通记录',
-						time: '2020-10-10',
-						content: '填写沟通记录填写沟通记录填写沟通记录'
-					},
-					{
-						coachName: '填写沟通记录',
-						time: '2020-10-10',
-						content: '填写沟通记录填写沟通记录填写沟通记录'
-					},
-					{
-						coachName: '填写沟通记录',
-						time: '2020-10-10',
-						content: '填写沟通记录填写沟通记录填写沟通记录'
-					},
-					{
-						coachName: '填写沟通记录',
-						time: '2020-10-10',
-						content: '填写沟通记录填写沟通记录填写沟通记录'
-					},
-					{
-						coachName: '填写沟通记录',
-						time: '2020-10-10',
-						content: '填写沟通记录填写沟通记录填写沟通记录'
-					},
-				]
+				tableList: []
 			}
 		},
 		onLoad(options) {
 			this.id = options.id
 			this.type = options.type
+			this.classId = options.classId
 			this.initialize()
 		},
 		onShow() {
@@ -103,7 +80,7 @@
 					this.tableList = res.data.growList
 				}).catch(error => {
 					this.$refs.uTips.show({
-						title: error.data.msg,
+						title: error.message,
 						type: 'warning',
 					})
 				})
@@ -130,27 +107,29 @@
 			},
 			//  获取列表数据
 			getTableList() {
-				NET.request(this.type == 2 ? API.getRenewCommunicateList : API.getSignCommunicateList, {
-					id: this.id,
-					page: this.pageIndex,
-					size: 10,
-				}, 'POST').then(res => {
-					this.triggered = false
-					this.tableList = this.tableList.concat(res.data.list)
-					this.isOver = res.data.list.length != 10
-				}).catch(error => {
-					this.triggered = false
-					this.$refs.uTips.show({
-						title: error.data.msg,
-						type: 'warning',
+				if(this.type != 1){
+					NET.request(this.type == 2 ? API.getRenewCommunicateList : API.getSignCommunicateList, {
+						id: this.id,
+						page: this.pageIndex,
+						size: 10,
+					}, 'POST').then(res => {
+						this.triggered = false
+						this.tableList = this.tableList.concat(res.data.list)
+						this.isOver = res.data.list.length != 10
+					}).catch(error => {
+						this.triggered = false
+						this.$refs.uTips.show({
+							title: error.message,
+							type: 'warning',
+						})
 					})
-				})
+				}
 			},
 			//  跳转沟通记录与成长经历表单
 			goToRecordForm() {
 				if (this.type == 1) {
 					uni.navigateTo({
-						url: '/pagesClass/courseForm?id=' + this.id
+						url: '/pagesClass/courseForm?id=' + this.id + '&classId=' + this.classId
 					});
 				} else {
 					uni.navigateTo({
@@ -171,6 +150,7 @@
 </style>
 <style lang="scss" scoped>
 	@import "@/static/css/themes.scss";
+
 	.content {
 		width: 100%;
 		float: left;

+ 5 - 19
store/index.js

@@ -1,4 +1,3 @@
-//引入vue和vuex
 import Vue from 'vue'
 import Vuex from 'vuex'
 Vue.use(Vuex)
@@ -8,6 +7,7 @@ const store = new Vuex.Store({ //  全局变量定义
 		mainColor: '#ff6e3e', //  主色
 		customStyle: { //  底部悬浮按钮样式
 			height: '40px',
+			fontSize: '14px',
 			backgroundColor: '#ff6e3e'
 		},
 		handleCustomStyle: { //  卡片主要操作按钮样式
@@ -23,24 +23,10 @@ const store = new Vuex.Store({ //  全局变量定义
 		},
 	},
 	mutations: {
-		// 登录
-		login(state, provider) {
-			state.userLoginFlag = true;
-			state.userInfo = provider
-			uni.setStorage({ // 异步缓存用户信息
-				key: "userinfo",
-				data: provider
-			})
-			console.log(state.userInfo)
-		},
-		// 退出
-		logout(state) {
-			state.userLoginFlag = false;
-			state.userInfo = {};
-			uni.removeStorage({ // 清除用户信息
-				key: "userinfo"
-			})
-		}
+		//  例子
+		test(state, provider) {
+			//  state.mainColor = '';
+		},
 	},
 	getters: {
 		mainColor: state => {

+ 22 - 9
utils/request.js

@@ -1,11 +1,19 @@
+let requestTimes = 0;
 const request = (url, data, method = 'GET') => {
 	return new Promise((resolve, reject) => {
 		let header = {
 			'Content-Type': 'application/json',
 		}
 		if (uni.getStorageSync('token')) {
-			header.token = 'Bearer ' + uni.getStorageSync('token')
+			header.Authorization = uni.getStorageSync('token')
 		}
+		if (requestTimes === 0) {
+			uni.showLoading({
+				title: '加载中',
+				mask: true,
+			});
+		}
+		requestTimes++;
 		uni.request({
 			url: url,
 			data: data,
@@ -14,29 +22,34 @@ const request = (url, data, method = 'GET') => {
 			success: res => {
 				if (res.statusCode == 200) {
 					let data = res.data
-					if (data.code == 40001) {
+					if (data.status == 30001) {
+						uni.removeStorageSync('token')
 						uni.navigateTo({
-							url: '/pages/index/login'
+							url: '/pages/login/index'
 						});
 						return false
 					}
-					if (data.code == 0 || data.code == 200) {
+					if (data.status == 10000) {
 						resolve(res.data)
 					} else {
-						reject(res)
+						reject(res.data)
 					}
 				} else {
-					reject(res)
+					reject(res.data)
 				}
 			},
 			fail: res => {
-				reject(res)
+				reject(res.data)
+			},
+			complete: () => {
+				requestTimes--;
+				if (requestTimes === 0) {
+					uni.hideLoading();
+				}
 			}
 		})
 	});
 }
-
-
 module.exports = {
 	request: request
 }

+ 581 - 593
uview-ui/components/u-upload/u-upload.vue

@@ -1,48 +1,27 @@
 <template>
 	<view class="u-upload" v-if="!disabled">
-		<view
-			v-if="showUploadList"
-			class="u-list-item u-preview-wrap"
-			v-for="(item, index) in lists"
-			:key="index"
-			:style="{
+		<view v-if="showUploadList" class="u-list-item u-preview-wrap" v-for="(item, index) in lists" :key="index" :style="{
 				width: $u.addUnit(width),
 				height: $u.addUnit(height)
-			}"
-		>
-			<view
-				v-if="deletable"
-				class="u-delete-icon"
-				@tap.stop="deleteItem(index)"
-				:style="{
+			}">
+			<view v-if="deletable" class="u-delete-icon" @tap.stop="deleteItem(index)" :style="{
 					background: delBgColor
-				}"
-			>
+				}">
 				<u-icon class="u-icon" :name="delIcon" size="20" :color="delColor"></u-icon>
 			</view>
-			<u-line-progress
-				v-if="showProgress && item.progress > 0 && !item.error"
-				:show-percent="false"
-				height="16"
-				class="u-progress"
-				:percent="item.progress"
-			></u-line-progress>
+			<u-line-progress v-if="showProgress && item.progress > 0 && !item.error" :show-percent="false" height="16" class="u-progress"
+			 :percent="item.progress"></u-line-progress>
 			<view @tap.stop="retry(index)" v-if="item.error" class="u-error-btn">点击重试</view>
-			<image @tap.stop="doPreviewImage(item.url || item.path, index)" class="u-preview-image" v-if="!item.isImage" :src="item.url || item.path" :mode="imageMode"></image>
+			<image @tap.stop="doPreviewImage(item.url || item.path, index)" class="u-preview-image" v-if="!item.isImage" :src="item.url || item.path"
+			 :mode="imageMode"></image>
 		</view>
 		<slot name="file" :file="lists"></slot>
-		<view style="display: inline-block;" @tap="selectFile" v-if="maxCount > lists.length">
+		<view style="display: inline-block;" @tap.stop="selectFile" v-if="maxCount > lists.length">
 			<slot name="addBtn"></slot>
-			<view
-				v-if="!customBtn"
-				class="u-list-item u-add-wrap"
-				hover-class="u-add-wrap__hover"
-				hover-stay-time="150"
-				:style="{
+			<view v-if="!customBtn" class="u-list-item u-add-wrap" hover-class="u-add-wrap__hover" hover-stay-time="150" :style="{
 					width: $u.addUnit(width),
 					height: $u.addUnit(height)
-				}"
-			>
+				}">
 				<u-icon name="plus" class="u-add-btn" size="40"></u-icon>
 				<view class="u-add-tips">{{ uploadText }}</view>
 			</view>
@@ -51,602 +30,611 @@
 </template>
 
 <script>
-/**
- * upload 图片上传
- * @description 该组件用于上传图片场景
- * @tutorial https://www.uviewui.com/components/upload.html
- * @property {String} action 服务器上传地址
- * @property {String Number} max-count 最大选择图片的数量(默认99)
- * @property {Boolean} custom-btn 如果需要自定义选择图片的按钮,设置为true(默认false)
- * @property {Boolean} show-progress 是否显示进度条(默认true)
- * @property {Boolean} disabled 是否启用(显示/移仓)组件(默认false)
- * @property {String} image-mode 预览图片等显示模式,可选值为uni的image的mode属性值(默认aspectFill)
- * @property {String} del-icon 右上角删除图标名称,只能为uView内置图标
- * @property {String} del-bg-color 右上角关闭按钮的背景颜色
- * @property {String | Number} index 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件
- * @property {String} del-color 右上角关闭按钮图标的颜色
- * @property {Object} header 上传携带的头信息,对象形式
- * @property {Object} form-data 上传额外携带的参数
- * @property {String} name 上传文件的字段名,供后端获取使用(默认file)
- * @property {Array<String>} size-type original 原图,compressed 压缩图,默认二者都有(默认['original', 'compressed'])
- * @property {Array<String>} source-type 选择图片的来源,album-从相册选图,camera-使用相机,默认二者都有(默认['album', 'camera'])
- * @property {Boolean} preview-full-image	是否可以通过uni.previewImage预览已选择的图片(默认true)
- * @property {Boolean} multiple	是否开启图片多选,部分安卓机型不支持(默认true)
- * @property {Boolean} deletable 是否显示删除图片的按钮(默认true)
- * @property {String Number} max-size 选择单个文件的最大大小,单位B(byte),默认不限制(默认Number.MAX_VALUE)
- * @property {Array<Object>} file-list 默认显示的图片列表,数组元素为对象,必须提供url属性
- * @property {Boolean} upload-text 选择图片按钮的提示文字(默认“选择图片”)
- * @property {Boolean} auto-upload 选择完图片是否自动上传,见上方说明(默认true)
- * @property {Boolean} show-tips 特殊情况下是否自动提示toast,见上方说明(默认true)
- * @property {Boolean} show-upload-list 是否显示组件内部的图片预览(默认true)
- * @event {Function} on-oversize 图片大小超出最大允许大小
- * @event {Function} on-preview 全屏预览图片时触发
- * @event {Function} on-remove 移除图片时触发
- * @event {Function} on-success 图片上传成功时触发
- * @event {Function} on-change 图片上传后,无论成功或者失败都会触发
- * @event {Function} on-error 图片上传失败时触发
- * @event {Function} on-progress 图片上传过程中的进度变化过程触发
- * @event {Function} on-uploaded 所有图片上传完毕触发
- * @event {Function} on-choose-complete 每次选择图片后触发,只是让外部可以得知每次选择后,内部的文件列表
- * @example <u-upload :action="action" :file-list="fileList" ></u-upload>
- */
-export default {
-	name: 'u-upload',
-	props: {
-		//是否显示组件自带的图片预览功能
-		showUploadList: {
-			type: Boolean,
-			default: true
-		},
-		// 后端地址
-		action: {
-			type: String,
-			default: ''
-		},
-		// 最大上传数量
-		maxCount: {
-			type: [String, Number],
-			default: 52
-		},
-		//  是否显示进度条
-		showProgress: {
-			type: Boolean,
-			default: true
-		},
-		// 是否启用
-		disabled: {
-			type: Boolean,
-			default: false
-		},
-		// 预览上传的图片时的裁剪模式,和image组件mode属性一致
-		imageMode: {
-			type: String,
-			default: 'aspectFill'
-		},
-		// 头部信息
-		header: {
-			type: Object,
-			default() {
-				return {};
-			}
-		},
-		// 额外携带的参数
-		formData: {
-			type: Object,
-			default() {
-				return {};
-			}
-		},
-		// 上传的文件字段名
-		name: {
-			type: String,
-			default: 'file'
-		},
-		// 所选的图片的尺寸, 可选值为original compressed
-		sizeType: {
-			type: Array,
-			default() {
-				return ['original', 'compressed'];
-			}
-		},
-		sourceType: {
-			type: Array,
-			default() {
-				return ['album', 'camera'];
-			}
-		},
-		// 是否在点击预览图后展示全屏图片预览
-		previewFullImage: {
-			type: Boolean,
-			default: true
-		},
-		// 是否开启图片多选,部分安卓机型不支持
-		multiple: {
-			type: Boolean,
-			default: true
-		},
-		// 是否展示删除按钮
-		deletable: {
-			type: Boolean,
-			default: true
-		},
-		// 文件大小限制,单位为byte
-		maxSize: {
-			type: [String, Number],
-			default: Number.MAX_VALUE
-		},
-		// 显示已上传的文件列表
-		fileList: {
-			type: Array,
-			default() {
-				return [];
-			}
-		},
-		// 上传区域的提示文字
-		uploadText: {
-			type: String,
-			default: '选择图片'
-		},
-		// 是否自动上传
-		autoUpload: {
-			type: Boolean,
-			default: true
-		},
-		// 是否显示toast消息提示
-		showTips: {
-			type: Boolean,
-			default: true
-		},
-		// 是否通过slot自定义传入选择图标的按钮
-		customBtn: {
-			type: Boolean,
-			default: false
-		},
-		// 内部预览图片区域和选择图片按钮的区域宽度
-		width: {
-			type: [String, Number],
-			default: 200
-		},
-		// 内部预览图片区域和选择图片按钮的区域高度
-		height: {
-			type: [String, Number],
-			default: 200
-		},
-		// 右上角关闭按钮的背景颜色
-		delBgColor: {
-			type: String,
-			default: '#fa3534'
-		},
-		// 右上角关闭按钮的叉号图标的颜色
-		delColor: {
-			type: String,
-			default: '#ffffff'
-		},
-		// 右上角删除图标名称,只能为uView内置图标
-		delIcon: {
-			type: String,
-			default: 'close'
-		},
-		// 如果上传后的返回值为json字符串,是否自动转json
-		toJson: {
-			type: Boolean,
-			default: true
-		},
-		// 上传前的钩子,每个文件上传前都会执行
-		beforeUpload: {
-			type: Function,
-			default: null
-		},
-		// 移除文件前的钩子
-		beforeRemove: {
-			type: Function,
-			default: null
-		},
-		// 允许上传的图片后缀
-		limitType:{
-			type: Array,
-			default() {
-				return ['png', 'jpg', 'jpeg', 'webp', 'gif'];
-			}
-		},
-		// 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件
-		index: {
-			type: [Number, String],
-			default: ''
-		}
-	},
-	mounted() {},
-	data() {
-		return {
-			lists: [],
-			isInCount: true,
-			uploading: false
-		};
-	},
-	watch: {
-		fileList: {
-			immediate: true,
-			handler(val) {
-				val.map(value => {
-					// 首先检查内部是否已经添加过这张图片,因为外部绑定了一个对象给fileList的话(对象引用),进行修改外部fileList
-					// 时,会触发watch,导致重新把原来的图片再次添加到this.lists
-					// 数组的some方法意思是,只要数组元素有任意一个元素条件符合,就返回true,而另一个数组的every方法的意思是数组所有元素都符合条件才返回true
-					let tmp = this.lists.some(val => {
-						return val.url == value.url;
-					})
-					// 如果内部没有这个图片(tmp为false),则添加到内部
-					!tmp && this.lists.push({ url: value.url, error: false, progress: 100 });
-				});
+	/**
+	 * upload 图片上传
+	 * @description 该组件用于上传图片场景
+	 * @tutorial https://www.uviewui.com/components/upload.html
+	 * @property {String} action 服务器上传地址
+	 * @property {String Number} max-count 最大选择图片的数量(默认99)
+	 * @property {Boolean} custom-btn 如果需要自定义选择图片的按钮,设置为true(默认false)
+	 * @property {Boolean} show-progress 是否显示进度条(默认true)
+	 * @property {Boolean} disabled 是否启用(显示/移仓)组件(默认false)
+	 * @property {String} image-mode 预览图片等显示模式,可选值为uni的image的mode属性值(默认aspectFill)
+	 * @property {String} del-icon 右上角删除图标名称,只能为uView内置图标
+	 * @property {String} del-bg-color 右上角关闭按钮的背景颜色
+	 * @property {String | Number} index 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件
+	 * @property {String} del-color 右上角关闭按钮图标的颜色
+	 * @property {Object} header 上传携带的头信息,对象形式
+	 * @property {Object} form-data 上传额外携带的参数
+	 * @property {String} name 上传文件的字段名,供后端获取使用(默认file)
+	 * @property {Array<String>} size-type original 原图,compressed 压缩图,默认二者都有(默认['original', 'compressed'])
+	 * @property {Array<String>} source-type 选择图片的来源,album-从相册选图,camera-使用相机,默认二者都有(默认['album', 'camera'])
+	 * @property {Boolean} preview-full-image	是否可以通过uni.previewImage预览已选择的图片(默认true)
+	 * @property {Boolean} multiple	是否开启图片多选,部分安卓机型不支持(默认true)
+	 * @property {Boolean} deletable 是否显示删除图片的按钮(默认true)
+	 * @property {String Number} max-size 选择单个文件的最大大小,单位B(byte),默认不限制(默认Number.MAX_VALUE)
+	 * @property {Array<Object>} file-list 默认显示的图片列表,数组元素为对象,必须提供url属性
+	 * @property {Boolean} upload-text 选择图片按钮的提示文字(默认“选择图片”)
+	 * @property {Boolean} auto-upload 选择完图片是否自动上传,见上方说明(默认true)
+	 * @property {Boolean} show-tips 特殊情况下是否自动提示toast,见上方说明(默认true)
+	 * @property {Boolean} show-upload-list 是否显示组件内部的图片预览(默认true)
+	 * @event {Function} on-oversize 图片大小超出最大允许大小
+	 * @event {Function} on-preview 全屏预览图片时触发
+	 * @event {Function} on-remove 移除图片时触发
+	 * @event {Function} on-success 图片上传成功时触发
+	 * @event {Function} on-change 图片上传后,无论成功或者失败都会触发
+	 * @event {Function} on-error 图片上传失败时触发
+	 * @event {Function} on-progress 图片上传过程中的进度变化过程触发
+	 * @event {Function} on-uploaded 所有图片上传完毕触发
+	 * @event {Function} on-choose-complete 每次选择图片后触发,只是让外部可以得知每次选择后,内部的文件列表
+	 * @example <u-upload :action="action" :file-list="fileList" ></u-upload>
+	 */
+	export default {
+		name: 'u-upload',
+		props: {
+			//是否显示组件自带的图片预览功能
+			showUploadList: {
+				type: Boolean,
+				default: true
+			},
+			// 后端地址
+			action: {
+				type: String,
+				default: ''
+			},
+			// 最大上传数量
+			maxCount: {
+				type: [String, Number],
+				default: 52
+			},
+			//  是否显示进度条
+			showProgress: {
+				type: Boolean,
+				default: true
+			},
+			// 是否启用
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			// 预览上传的图片时的裁剪模式,和image组件mode属性一致
+			imageMode: {
+				type: String,
+				default: 'aspectFill'
+			},
+			// 头部信息
+			header: {
+				type: Object,
+				default () {
+					return {};
+				}
+			},
+			// 额外携带的参数
+			formData: {
+				type: Object,
+				default () {
+					return {};
+				}
+			},
+			// 上传的文件字段名
+			name: {
+				type: String,
+				default: 'file'
+			},
+			// 所选的图片的尺寸, 可选值为original compressed
+			sizeType: {
+				type: Array,
+				default () {
+					return ['original', 'compressed'];
+				}
+			},
+			sourceType: {
+				type: Array,
+				default () {
+					return ['album', 'camera'];
+				}
+			},
+			// 是否在点击预览图后展示全屏图片预览
+			previewFullImage: {
+				type: Boolean,
+				default: true
+			},
+			// 是否开启图片多选,部分安卓机型不支持
+			multiple: {
+				type: Boolean,
+				default: true
+			},
+			// 是否展示删除按钮
+			deletable: {
+				type: Boolean,
+				default: true
+			},
+			// 文件大小限制,单位为byte
+			maxSize: {
+				type: [String, Number],
+				default: Number.MAX_VALUE
+			},
+			// 显示已上传的文件列表
+			fileList: {
+				type: Array,
+				default () {
+					return [];
+				}
+			},
+			// 上传区域的提示文字
+			uploadText: {
+				type: String,
+				default: '选择图片'
+			},
+			// 是否自动上传
+			autoUpload: {
+				type: Boolean,
+				default: true
+			},
+			// 是否显示toast消息提示
+			showTips: {
+				type: Boolean,
+				default: true
+			},
+			// 是否通过slot自定义传入选择图标的按钮
+			customBtn: {
+				type: Boolean,
+				default: false
+			},
+			// 内部预览图片区域和选择图片按钮的区域宽度
+			width: {
+				type: [String, Number],
+				default: 200
+			},
+			// 内部预览图片区域和选择图片按钮的区域高度
+			height: {
+				type: [String, Number],
+				default: 200
+			},
+			// 右上角关闭按钮的背景颜色
+			delBgColor: {
+				type: String,
+				default: '#fa3534'
+			},
+			// 右上角关闭按钮的叉号图标的颜色
+			delColor: {
+				type: String,
+				default: '#ffffff'
+			},
+			// 右上角删除图标名称,只能为uView内置图标
+			delIcon: {
+				type: String,
+				default: 'close'
+			},
+			// 如果上传后的返回值为json字符串,是否自动转json
+			toJson: {
+				type: Boolean,
+				default: true
+			},
+			// 上传前的钩子,每个文件上传前都会执行
+			beforeUpload: {
+				type: Function,
+				default: null
+			},
+			// 移除文件前的钩子
+			beforeRemove: {
+				type: Function,
+				default: null
+			},
+			// 允许上传的图片后缀
+			limitType: {
+				type: Array,
+				default () {
+					return ['png', 'jpg', 'jpeg', 'webp', 'gif'];
+				}
+			},
+			// 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件
+			index: {
+				type: [Number, String],
+				default: ''
 			}
 		},
-		// 监听lists的变化,发出事件
-		lists(n) {
-			this.$emit('on-list-change', n, this.index);
-		}
-	},
-	methods: {
-		// 清除列表
-		clear() {
-			this.lists = [];
-		},
-		// 重新上传队列中上传失败的所有文件
-		reUpload() {
-			this.uploadFile();
-		},
-		// 选择图片
-		selectFile() {
-			if (this.disabled) return;
-			const { name = '', maxCount, multiple, maxSize, sizeType, lists, camera, compressed, maxDuration, sourceType } = this;
-			let chooseFile = null;
-			const newMaxCount = maxCount - lists.length;
-			// 设置为只选择图片的时候使用 chooseImage 来实现
-			chooseFile = new Promise((resolve, reject) => {
-				uni.chooseImage({
-					count: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1,
-					sourceType: sourceType,
-					sizeType,
-					success: resolve,
-					fail: reject
-				});
-			});
-			chooseFile
-				.then(res => {
-					let file = null;
-					let listOldLength = this.lists.length;
-					res.tempFiles.map((val, index) => {
-						// 检查文件后缀是否允许,如果不在this.limitType内,就会返回false
-						if(!this.checkFileExt(val)) return ;
-						
-						// 如果是非多选,index大于等于1或者超出最大限制数量时,不处理
-						if (!multiple && index >= 1) return;
-						if (val.size > maxSize) {
-							this.$emit('on-oversize', val, this.lists, this.index);
-							this.showToast('超出允许的文件大小');
-						} else {
-							if (maxCount <= lists.length) {
-								this.$emit('on-exceed', val, this.lists, this.index);
-								this.showToast('超出最大允许的文件个数');
-								return;
-							}
-							lists.push({
-								url: val.path,
-								progress: 0,
+		mounted() {},
+		data() {
+			return {
+				lists: [],
+				isInCount: true,
+				uploading: false
+			};
+		},
+		watch: {
+			fileList: {
+				immediate: true,
+				handler(val) {
+					val.map(value => {
+						// 首先检查内部是否已经添加过这张图片,因为外部绑定了一个对象给fileList的话(对象引用),进行修改外部fileList
+						// 时,会触发watch,导致重新把原来的图片再次添加到this.lists
+						// 数组的some方法意思是,只要数组元素有任意一个元素条件符合,就返回true,而另一个数组的every方法的意思是数组所有元素都符合条件才返回true
+						let tmp = this.lists.some(val => {
+								return val.url == value.url;
+							})
+							// 如果内部没有这个图片(tmp为false),则添加到内部
+							!tmp && this.lists.push({
+								url: value.url,
 								error: false,
-								file: val
+								progress: 100
 							});
-						}
 					});
-					// 每次图片选择完,抛出一个事件,并将当前内部选择的图片数组抛出去
-					this.$emit('on-choose-complete', this.lists, this.index);
-					if (this.autoUpload) this.uploadFile(listOldLength);
-				})
-				.catch(error => {
-					this.$emit('on-choose-fail', error);
-				});
-		},
-		// 提示用户消息
-		showToast(message, force = false) {
-			if (this.showTips || force) {
-				uni.showToast({
-					title: message,
-					icon: 'none'
-				});
+				}
+			},
+			// 监听lists的变化,发出事件
+			lists(n) {
+				this.$emit('on-list-change', n, this.index);
 			}
 		},
-		// 该方法供用户通过ref调用,手动上传
-		upload() {
-			this.uploadFile();
-		},
-		// 对失败的图片重新上传
-		retry(index) {
-			this.lists[index].progress = 0;
-			this.lists[index].error = false;
-			this.lists[index].response = null;
-			uni.showLoading({
-				title: '重新上传'
-			});
-			this.uploadFile(index);
-		},
-		// 上传图片
-		async uploadFile(index = 0) {
-			if (this.disabled) return;
-			if (this.uploading) return;
-			// 全部上传完成
-			if (index >= this.lists.length) {
-				this.$emit('on-uploaded', this.lists, this.index);
-				return;
-			}
-			// 检查是否是已上传或者正在上传中
-			if (this.lists[index].progress == 100) {
-				if (this.autoUpload == false) this.uploadFile(index + 1);
-				return;
-			}
-			// 执行before-upload钩子
-			if(this.beforeUpload && typeof(this.beforeUpload) === 'function') {
-				// 执行回调,同时传入索引和文件列表当作参数
-				// 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
-				// 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
-				// 因为upload组件可能会被嵌套在其他组件内,比如u-form,这时this.$parent其实为u-form的this,
-				// 非页面的this,所以这里需要往上历遍,一直寻找到最顶端的$parent,这里用了this.$u.$parent.call(this)
-				// 明白意思即可,无需纠结this.$u.$parent.call(this)的细节
-				let beforeResponse = this.beforeUpload.bind(this.$u.$parent.call(this))(index, this.lists);
-				// 判断是否返回了promise
-				if (!!beforeResponse && typeof beforeResponse.then === 'function') {
-					await beforeResponse.then(res => {
-						// promise返回成功,不进行动作,继续上传
-					}).catch(err => {
-						// 进入catch回调的话,继续下一张
-						return this.uploadFile(index + 1);
+		methods: {
+			// 清除列表
+			clear() {
+				this.lists = [];
+			},
+			// 重新上传队列中上传失败的所有文件
+			reUpload() {
+				this.uploadFile();
+			},
+			// 选择图片
+			selectFile() {
+				if (this.disabled) return;
+				const {
+					name = '', maxCount, multiple, maxSize, sizeType, lists, camera, compressed, maxDuration, sourceType
+				} = this;
+				let chooseFile = null;
+				const newMaxCount = maxCount - lists.length;
+				// 设置为只选择图片的时候使用 chooseImage 来实现
+				chooseFile = new Promise((resolve, reject) => {
+					uni.chooseImage({
+						count: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1,
+						sourceType: sourceType,
+						sizeType,
+						success: resolve,
+						fail: reject
+					});
+				});
+				chooseFile
+					.then(res => {
+						let file = null;
+						let listOldLength = this.lists.length;
+						res.tempFiles.map((val, index) => {
+							// 检查文件后缀是否允许,如果不在this.limitType内,就会返回false
+							if (!this.checkFileExt(val)) return;
+
+							// 如果是非多选,index大于等于1或者超出最大限制数量时,不处理
+							if (!multiple && index >= 1) return;
+							if (val.size > maxSize) {
+								this.$emit('on-oversize', val, this.lists, this.index);
+								this.showToast('超出允许的文件大小');
+							} else {
+								if (maxCount <= lists.length) {
+									this.$emit('on-exceed', val, this.lists, this.index);
+									this.showToast('超出最大允许的文件个数');
+									return;
+								}
+								lists.push({
+									url: val.path,
+									progress: 0,
+									error: false,
+									file: val
+								});
+							}
+						});
+						// 每次图片选择完,抛出一个事件,并将当前内部选择的图片数组抛出去
+						this.$emit('on-choose-complete', this.lists, this.index);
+						if (this.autoUpload) this.uploadFile(listOldLength);
 					})
-				} else if(beforeResponse === false) {
-					// 如果返回false,继续下一张图片的上传
-					return this.uploadFile(index + 1);
-				} else {
-					// 此处为返回"true"的情形,这里不写代码,就跳过此处,继续执行当前的上传逻辑
+					.catch(error => {
+						this.$emit('on-choose-fail', error);
+					});
+			},
+			// 提示用户消息
+			showToast(message, force = false) {
+				if (this.showTips || force) {
+					uni.showToast({
+						title: message,
+						icon: 'none'
+					});
 				}
-			}
-			// 检查上传地址
-			if (!this.action) {
-				this.showToast('请配置上传地址', true);
-				return;
-			}
-			this.lists[index].error = false;
-			this.uploading = true;
-			// 创建上传对象
-			const task = uni.uploadFile({
-				url: this.action,
-				filePath: this.lists[index].url,
-				name: this.name,
-				formData: this.formData,
-				header: this.header,
-				success: res => {
-					// 判断是否json字符串,将其转为json格式
-					let data = this.toJson && this.$u.test.jsonString(res.data) ? JSON.parse(res.data) : res.data;
-					if (![200, 201, 204].includes(res.statusCode)) {
-						this.uploadError(index, data);
+			},
+			// 该方法供用户通过ref调用,手动上传
+			upload() {
+				this.uploadFile();
+			},
+			// 对失败的图片重新上传
+			retry(index) {
+				this.lists[index].progress = 0;
+				this.lists[index].error = false;
+				this.lists[index].response = null;
+				uni.showLoading({
+					title: '重新上传'
+				});
+				this.uploadFile(index);
+			},
+			// 上传图片
+			async uploadFile(index = 0) {
+				if (this.disabled) return;
+				if (this.uploading) return;
+				// 全部上传完成
+				if (index >= this.lists.length) {
+					this.$emit('on-uploaded', this.lists, this.index);
+					return;
+				}
+				// 检查是否是已上传或者正在上传中
+				if (this.lists[index].progress == 100) {
+					if (this.autoUpload == false) this.uploadFile(index + 1);
+					return;
+				}
+				// 执行before-upload钩子
+				if (this.beforeUpload && typeof(this.beforeUpload) === 'function') {
+					// 执行回调,同时传入索引和文件列表当作参数
+					// 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
+					// 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
+					// 因为upload组件可能会被嵌套在其他组件内,比如u-form,这时this.$parent其实为u-form的this,
+					// 非页面的this,所以这里需要往上历遍,一直寻找到最顶端的$parent,这里用了this.$u.$parent.call(this)
+					// 明白意思即可,无需纠结this.$u.$parent.call(this)的细节
+					let beforeResponse = this.beforeUpload.bind(this.$u.$parent.call(this))(index, this.lists);
+					// 判断是否返回了promise
+					if (!!beforeResponse && typeof beforeResponse.then === 'function') {
+						await beforeResponse.then(res => {
+							// promise返回成功,不进行动作,继续上传
+						}).catch(err => {
+							// 进入catch回调的话,继续下一张
+							return this.uploadFile(index + 1);
+						})
+					} else if (beforeResponse === false) {
+						// 如果返回false,继续下一张图片的上传
+						return this.uploadFile(index + 1);
 					} else {
-						// 上传成功
-						this.lists[index].response = data;
-						this.lists[index].progress = 100;
-						this.lists[index].error = false;
-						this.$emit('on-success', data, index, this.lists, this.index);
+						// 此处为返回"true"的情形,这里不写代码,就跳过此处,继续执行当前的上传逻辑
 					}
-				},
-				fail: e => {
-					this.uploadError(index, e);
-				},
-				complete: res => {
-					uni.hideLoading();
-					this.uploading = false;
-					this.uploadFile(index + 1);
-					this.$emit('on-change', res, index, this.lists, this.index);
 				}
-			});
-			task.onProgressUpdate(res => {
-				if (res.progress > 0) {
-					this.lists[index].progress = res.progress;
-					this.$emit('on-progress', res, index, this.lists, this.index);
+				// 检查上传地址
+				if (!this.action) {
+					this.showToast('请配置上传地址', true);
+					return;
 				}
-			});
-		},
-		// 上传失败
-		uploadError(index, err) {
-			this.lists[index].progress = 0;
-			this.lists[index].error = true;
-			this.lists[index].response = null;
-			this.$emit('on-error', err, index, this.lists, this.index);
-			this.showToast('上传失败,请重试');
-		},
-		// 删除一个图片
-		deleteItem(index) {
-			uni.showModal({
-				title: '提示',
-				content: '您确定要删除此项吗?',
-				success: async (res) => {
-					if (res.confirm) {
-						// 先检查是否有定义before-remove移除前钩子
-						// 执行before-remove钩子
-						if(this.beforeRemove && typeof(this.beforeRemove) === 'function') {
-							// 此处钩子执行 原理同before-remove参数,见上方注释
-							let beforeResponse = this.beforeRemove.bind(this.$u.$parent.call(this))(index, this.lists);
-							// 判断是否返回了promise
-							if (!!beforeResponse && typeof beforeResponse.then === 'function') {
-								await beforeResponse.then(res => {
-									// promise返回成功,不进行动作,继续上传
-									this.handlerDeleteItem(index);
-								}).catch(err => {
-									// 如果进入promise的reject,终止删除操作
+				this.lists[index].error = false;
+				this.uploading = true;
+				// 创建上传对象
+				const task = uni.uploadFile({
+					url: this.action,
+					filePath: this.lists[index].url,
+					name: this.name,
+					formData: this.formData,
+					header: this.header,
+					success: res => {
+						// 判断是否json字符串,将其转为json格式
+						let data = this.toJson && this.$u.test.jsonString(res.data) ? JSON.parse(res.data) : res.data;
+						if (![200, 201, 204].includes(res.statusCode)) {
+							this.uploadError(index, data);
+						} else {
+							// 上传成功
+							this.lists[index].response = data;
+							this.lists[index].progress = 100;
+							this.lists[index].error = false;
+							this.$emit('on-success', data, index, this.lists, this.index);
+						}
+					},
+					fail: e => {
+						this.uploadError(index, e);
+					},
+					complete: res => {
+						uni.hideLoading();
+						this.uploading = false;
+						this.uploadFile(index + 1);
+						this.$emit('on-change', res, index, this.lists, this.index);
+					}
+				});
+				task.onProgressUpdate(res => {
+					if (res.progress > 0) {
+						this.lists[index].progress = res.progress;
+						this.$emit('on-progress', res, index, this.lists, this.index);
+					}
+				});
+			},
+			// 上传失败
+			uploadError(index, err) {
+				this.lists[index].progress = 0;
+				this.lists[index].error = true;
+				this.lists[index].response = null;
+				this.$emit('on-error', err, index, this.lists, this.index);
+				this.showToast('上传失败,请重试');
+			},
+			// 删除一个图片
+			deleteItem(index) {
+				uni.showModal({
+					title: '提示',
+					content: '您确定要删除此项吗?',
+					success: async (res) => {
+						if (res.confirm) {
+							// 先检查是否有定义before-remove移除前钩子
+							// 执行before-remove钩子
+							if (this.beforeRemove && typeof(this.beforeRemove) === 'function') {
+								// 此处钩子执行 原理同before-remove参数,见上方注释
+								let beforeResponse = this.beforeRemove.bind(this.$u.$parent.call(this))(index, this.lists);
+								// 判断是否返回了promise
+								if (!!beforeResponse && typeof beforeResponse.then === 'function') {
+									await beforeResponse.then(res => {
+										// promise返回成功,不进行动作,继续上传
+										this.handlerDeleteItem(index);
+									}).catch(err => {
+										// 如果进入promise的reject,终止删除操作
+										this.showToast('已终止移除');
+									})
+								} else if (beforeResponse === false) {
+									// 返回false,终止删除
 									this.showToast('已终止移除');
-								})
-							} else if(beforeResponse === false) {
-								// 返回false,终止删除
-								this.showToast('已终止移除');
+								} else {
+									// 如果返回true,执行删除操作
+									this.handlerDeleteItem(index);
+								}
 							} else {
-								// 如果返回true,执行删除操作
+								// 如果不存在before-remove钩子,
 								this.handlerDeleteItem(index);
 							}
-						} else {
-							// 如果不存在before-remove钩子,
-							this.handlerDeleteItem(index);
 						}
 					}
+				});
+			},
+			// 执行移除图片的动作,上方代码只是判断是否可以移除
+			handlerDeleteItem(index) {
+				// 如果文件正在上传中,终止上传任务,进度在0 < progress < 100则意味着正在上传
+				if (this.lists[index].process < 100 && this.lists[index].process > 0) {
+					typeof this.lists[index].uploadTask != 'undefined' && this.lists[index].uploadTask.abort();
 				}
-			});
-		},
-		// 执行移除图片的动作,上方代码只是判断是否可以移除
-		handlerDeleteItem(index) {
-			// 如果文件正在上传中,终止上传任务,进度在0 < progress < 100则意味着正在上传
-			if (this.lists[index].process < 100 && this.lists[index].process > 0) {
-				typeof this.lists[index].uploadTask != 'undefined' && this.lists[index].uploadTask.abort();
-			}
-			this.lists.splice(index, 1);
-			this.$forceUpdate();
-			this.$emit('on-remove', index, this.lists, this.index);
-			this.showToast('移除成功');
-		},
-		// 用户通过ref手动的形式,移除一张图片
-		remove(index) {
-			// 判断索引的合法范围
-			if (index >= 0 && index < this.lists.length) {
 				this.lists.splice(index, 1);
-				this.$emit('on-list-change', this.lists, this.index);
-			}
-		},
-		// 预览图片
-		doPreviewImage(url, index) {
-			if (!this.previewFullImage) return;
-			const images = this.lists.map(item => item.url || item.path);
-			uni.previewImage({
-				urls: images,
-				current: url,
-				success: () => {
-					this.$emit('on-preview', url, this.lists, this.index);
-				},
-				fail: () => {
-					uni.showToast({
-						title: '预览图片失败',
-						icon: 'none'
-					});
+				this.$forceUpdate();
+				this.$emit('on-remove', index, this.lists, this.index);
+				this.showToast('移除成功');
+			},
+			// 用户通过ref手动的形式,移除一张图片
+			remove(index) {
+				// 判断索引的合法范围
+				if (index >= 0 && index < this.lists.length) {
+					this.lists.splice(index, 1);
+					this.$emit('on-list-change', this.lists, this.index);
 				}
-			});
-		},
-		// 判断文件后缀是否允许
-		checkFileExt(file) {
-			// 检查是否在允许的后缀中
-			let noArrowExt = false;
-			// 获取后缀名
-			let fileExt = '';
-			const reg = /.+\./;
-			// 如果是H5,需要从name中判断
-			// #ifdef H5
-			fileExt = file.name.replace(reg, "").toLowerCase();
-			// #endif
-			// 非H5,需要从path中读取后缀
-			// #ifndef H5
-			fileExt = file.path.replace(reg, "").toLowerCase();
-			// #endif
-			// 使用数组的some方法,只要符合limitType中的一个,就返回true
-			noArrowExt = this.limitType.some(ext => {
-				// 转为小写
-				return ext.toLowerCase() === fileExt;
-			})
-			if(!noArrowExt) this.showToast(`不允许选择${fileExt}格式的文件`);
-			return noArrowExt;
+			},
+			// 预览图片
+			doPreviewImage(url, index) {
+				if (!this.previewFullImage) {
+					this.$emit('on-preview-error', url, this.lists, this.index);
+					return;
+				}
+				const images = this.lists.map(item => item.url || item.path);
+				uni.previewImage({
+					urls: images,
+					current: url,
+					success: () => {
+						this.$emit('on-preview', url, this.lists, this.index);
+					},
+					fail: () => {
+						uni.showToast({
+							title: '预览图片失败',
+							icon: 'none'
+						});
+					}
+				});
+			},
+			// 判断文件后缀是否允许
+			checkFileExt(file) {
+				// 检查是否在允许的后缀中
+				let noArrowExt = false;
+				// 获取后缀名
+				let fileExt = '';
+				const reg = /.+\./;
+				// 如果是H5,需要从name中判断
+				// #ifdef H5
+				fileExt = file.name.replace(reg, "").toLowerCase();
+				// #endif
+				// 非H5,需要从path中读取后缀
+				// #ifndef H5
+				fileExt = file.path.replace(reg, "").toLowerCase();
+				// #endif
+				// 使用数组的some方法,只要符合limitType中的一个,就返回true
+				noArrowExt = this.limitType.some(ext => {
+					// 转为小写
+					return ext.toLowerCase() === fileExt;
+				})
+				if (!noArrowExt) this.showToast(`不允许选择${fileExt}格式的文件`);
+				return noArrowExt;
+			}
 		}
-	}
-};
+	};
 </script>
 
 <style lang="scss" scoped>
-@import '../../libs/css/style.components.scss';
+	@import '../../libs/css/style.components.scss';
 
-.u-upload {
-	@include vue-flex;
-	flex-wrap: wrap;
-	align-items: center;
-}
+	.u-upload {
+		@include vue-flex;
+		flex-wrap: wrap;
+		align-items: center;
+	}
 
-.u-list-item {
-	width: 200rpx;
-	height: 200rpx;
-	overflow: hidden;
-	margin: 10rpx;
-	background: rgb(244, 245, 246);
-	position: relative;
-	border-radius: 10rpx;
-	/* #ifndef APP-NVUE */
-	display: flex;
-	/* #endif */
-	align-items: center;
-	justify-content: center;
-}
+	.u-list-item {
+		width: 200rpx;
+		height: 200rpx;
+		overflow: hidden;
+		margin: 10rpx;
+		background: rgb(244, 245, 246);
+		position: relative;
+		border-radius: 10rpx;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+	}
 
-.u-preview-wrap {
-	border: 1px solid rgb(235, 236, 238);
-}
+	.u-preview-wrap {
+		border: 1px solid rgb(235, 236, 238);
+	}
 
-.u-add-wrap {
-	flex-direction: column;
-	color: $u-content-color;
-	font-size: 26rpx;
-}
+	.u-add-wrap {
+		flex-direction: column;
+		color: $u-content-color;
+		font-size: 26rpx;
+	}
 
-.u-add-tips {
-	margin-top: 20rpx;
-	line-height: 40rpx;
-}
+	.u-add-tips {
+		margin-top: 20rpx;
+		line-height: 40rpx;
+	}
 
-.u-add-wrap__hover {
-	background-color: rgb(235, 236, 238);
-}
+	.u-add-wrap__hover {
+		background-color: rgb(235, 236, 238);
+	}
 
-.u-preview-image {
-	display: block;
-	width: 100%;
-	height: 100%;
-	border-radius: 10rpx;
-}
+	.u-preview-image {
+		display: block;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
+	}
 
-.u-delete-icon {
-	position: absolute;
-	top: 10rpx;
-	right: 10rpx;
-	z-index: 10;
-	background-color: $u-type-error;
-	border-radius: 100rpx;
-	width: 44rpx;
-	height: 44rpx;
-	@include vue-flex;
-	align-items: center;
-	justify-content: center;
-}
+	.u-delete-icon {
+		position: absolute;
+		top: 10rpx;
+		right: 10rpx;
+		z-index: 10;
+		background-color: $u-type-error;
+		border-radius: 100rpx;
+		width: 44rpx;
+		height: 44rpx;
+		@include vue-flex;
+		align-items: center;
+		justify-content: center;
+	}
 
-.u-icon {
-	@include vue-flex;
-	align-items: center;
-	justify-content: center;
-}
+	.u-icon {
+		@include vue-flex;
+		align-items: center;
+		justify-content: center;
+	}
 
-.u-progress {
-	position: absolute;
-	bottom: 10rpx;
-	left: 8rpx;
-	right: 8rpx;
-	z-index: 9;
-	width: auto;
-}
+	.u-progress {
+		position: absolute;
+		bottom: 10rpx;
+		left: 8rpx;
+		right: 8rpx;
+		z-index: 9;
+		width: auto;
+	}
 
-.u-error-btn {
-	color: #ffffff;
-	background-color: $u-type-error;
-	font-size: 20rpx;
-	padding: 4px 0;
-	text-align: center;
-	position: absolute;
-	bottom: 0;
-	left: 0;
-	right: 0;
-	z-index: 9;
-	line-height: 1;
-}
+	.u-error-btn {
+		color: #ffffff;
+		background-color: $u-type-error;
+		font-size: 20rpx;
+		padding: 4px 0;
+		text-align: center;
+		position: absolute;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		z-index: 9;
+		line-height: 1;
+	}
 </style>