XSLu08042 1 日 前
コミット
6d51a9b03d

+ 5 - 1
api/course.js

@@ -96,4 +96,8 @@ export function getWxConfig(data) {
  }
  export function complaintRecord(data, type) {
  	return request('/app/user/complaint/record', data, 'POST','application/json;charset=UTF-8','https://h5api.his.cdwjyyh.com');
- }
+ }
+ 
+ export function uploadOSS(data, type) {
+ 	return request('/app/common/uploadOSS', data, 'POST','application/json;charset=UTF-8','https://h5api.his.cdwjyyh.com');
+ }

+ 12 - 0
pages.json

@@ -1378,6 +1378,18 @@
 						}
 				    }
 				    
+				},{
+				    "path" : "webview",
+				    "style" :                                                                                    
+				    {
+				        "navigationBarTitleText": "授权登录",
+				        "scrollIndicator": "none",
+						"app-plus": {
+							"bounce": "none",
+							"softinputMode": "adjustResize"
+						}
+				    }
+				    
 				}
 			]
 		}

+ 4 - 3
pages_course/components/ques.vue

@@ -10,7 +10,7 @@
 				<text>{{item.title}}</text>
 			</view>
 			<view :class="isAnswer(item,option.name) ?'ques-option ques-option-active':'ques-option'"
-				v-for="(option,idx) in item.questionOption" :key="idx" @click="handleAnswer(item,option)">
+				v-for="(option,idx) in item.questionOption" :key="idx" @click="handleAnswer(item,option,index)">
 				<view>
 					{{numberToLetter(idx)}}.
 				</view>
@@ -51,10 +51,11 @@
 				let letter = String.fromCharCode(letterCode);
 				return letter;
 			},
-			handleAnswer(item, option) {
+			handleAnswer(item, option,index) {
 				const param = {
 					item,
-					option
+					option,
+					index
 				}
 				this.$emit("handleAnswer", param)
 			}

+ 302 - 33
pages_course/feedback.vue

@@ -5,38 +5,96 @@
 			<view class="header-title" :style="{height:menuButtonH+'px',lineHeight:menuButtonH+'px'}">投诉反馈</view>
 		</view>
 		<view class="container" :style="{paddingTop: `calc(88rpx + ${statusBarHeight}px)`}">
-			<view class="list-item title">{{pageIndex==0 ? '请选择反馈类型':'请选择'}}</view>
-			<view class="list-item" v-for="(item, index) in feedbackItems" :key="index" @click="handleClick(item,index)">
-				{{ item.complaintTypeName }}
+			<view class="formbox" v-if="isLastChild==1">
+				<view class="formbox-title">{{ text }}</view>
+				<view class="form">
+					<u-form labelPosition="top" labelWidth='auto' :model="formdata" :rules="rules" ref="uForm" errorType="toast">
+						<u-form-item label="" prop="complaintContent">
+							<u--textarea v-model="formdata.complaintContent" border="none" :clearable="true" placeholder="请填写反馈内容" count maxlength='200'></u--textarea>
+						</u-form-item>
+						<view class="box">
+							<u-form-item label="图片(最多9张)">
+								<view class="imgitem">
+									<u-upload
+										:fileList="fileList1"
+										@afterRead="afterRead"
+										@delete="deletePic"
+										name="1"
+										:maxCount="9"
+									></u-upload>
+								</view>
+							</u-form-item>
+						</view>
+					</u-form>
+				</view>
+				<view class="footer-btn">
+					<button class="submit-btn" @click="submit">提交</button>
+					<!-- <button class="submit-btn back-btn" @click="goBack">返回</button> -->
+				</view>
 			</view>
-			<view class="list-item" @click="goBack">
-				返回上一层
+			<view class="container" v-else>
+				<view class="list-item title">请选择反馈类型</view>
+				<view class="list-item" v-for="(item, index) in feedbackItems" :key="index"
+					@click="handleClick(item,index)">
+					<view>{{ item.complaintTypeName }}</view>
+					<uni-icons type="right" size="20" color="rgba(0,0,0,.3)" v-if="isLastChild==0"></uni-icons>
+				</view>
+				<view class="list-item" v-if="pageIndex!=0&&isLastChild==0" @click="goBack">
+					返回上一层
+				</view>
 			</view>
 		</view>
 	</view>
 </template>
 
 <script>
-	import{ getTypeTree, complaintRecord } from "@/api/course.js"
+	import{ getTypeTree, complaintRecord,loginByMp } from "@/api/course.js"
 	export default {
 		data() {
 			return {
 				statusBarHeight: uni.getSystemInfoSync().statusBarHeight,
 				menuButtonH: 45,
 				pageIndex: 0,
-				list:[],
+				list: [],
 				feedbackItems: [],
-				userId:'',
+				userId: '',
 				courseId: '',
-				videoId:''
+				videoId: '',
+				formdata: {
+					complaintContent: ""
+				},
+				rules: {
+					complaintContent:[{
+						required: true,
+						message: '投诉反馈内容不能为空',
+						trigger: ["change", "blur"]
+					}]
+				},
+				text: '',
+				templateId: 0,
+				user: {},
+				isLastChild: 0,
+				isLogin: false,
+				fileList1: [],
 			};
 		},
 		onLoad(option) {
 			this.userId = option.userId || ''
 			this.courseId = option.courseId || ''
 			this.videoId = option.videoId || ''
-			this.getMenuButton()
-			this.getList()
+			this.$isLoginCourse().then(
+				res => {
+					if(res){
+						this.isLogin = true
+						this.getMenuButton()
+						this.getList()
+					} else{
+						this.isLogin = false
+						this.goLogin()
+					}
+				},
+				rej => {}
+			);
 		},
 		methods: {
 			getMenuButton(){
@@ -45,32 +103,83 @@
 			},
 			goBack() {
 				// 返回上一层逻辑
-				if(this.pageIndex == 0){
+				if (this.pageIndex == 0) {
 					uni.navigateBack();
-				}else {
-					this.pageIndex = 0
-					this.feedbackItems = this.list
+				} else {
+					this.pageIndex--;
+					this.formdata = {
+						complaintContent: ""
+					}
+					if (this.isLastChild == 1) {
+						this.isLastChild = 0
+					} else {
+						if (this.pageIndex == 0) {
+							this.feedbackItems = this.list
+							this.templateId = 0
+						} else {
+							const list = this.findGrandparentOrAllData(this.list, this.templateId)
+							this.feedbackItems = list.childrenType
+							this.templateId = list.complaintTypeId
+						}
+					}
+				
+				}
+			},
+			findGrandparentOrAllData(data, targetId) {
+				// 递归函数,用于查找目标节点的父级节点
+				function findParent(node, targetId) {
+					if (!node || !node.childrenType) return null;
+			
+					for (let child of node.childrenType) {
+						if (child.complaintTypeId === targetId) {
+							return node; // 找到目标节点的父级节点
+						}
+			
+						const result = findParent(child, targetId); // 递归查找子节点
+						if (result) return result;
+					}
+			
+					return null;
 				}
+			
+				// 遍历顶层节点,查找目标节点的父级和祖父级节点
+				for (let root of data) {
+					if (root.complaintTypeId === targetId) {
+						return data; // 如果目标节点是顶层节点,返回所有数据
+					}
+			
+					const parent = findParent(root, targetId); // 查找目标节点的父级节点
+					if (parent) {
+						const grandparent = findParent(root, parent.complaintTypeId); // 查找父级节点的父级节点
+						return grandparent || data; // 如果找到祖父节点返回祖父节点,否则返回所有数据
+					}
+				}
+			
+				return data; // 如果没有找到目标节点,返回所有数据
 			},
 			handleClick(item,index) {
-				if(this.pageIndex ==0) {
-					this.feedbackItems = this.list[index].childrenType || [];
-					this.pageIndex = 1;
-				} else if(this.pageIndex ==1){
-					const param = {
-						userId: this.userId,
-						complaintTypeId: item.complaintTypeId,
-						complaintContent: item.complaintContent,
-						courseId: this.courseId,
-						videoId: this.videoId,
+				if (this.isLastChild == 1) return
+				if (this.pageIndex >= 0) {
+					this.pageIndex++
+					let children = this.feedbackItems[index].childrenType || [];
+					this.templateId = this.feedbackItems[index].complaintTypeId
+					this.formdata = {
+						complaintContent: ""
+					}
+					this.text = this.feedbackItems[index].complaintTypeName
+					if (children.length > 0) {
+						this.isLastChild = 0
+						this.feedbackItems = children
+						this.templateId = this.feedbackItems[0].complaintTypeId
+					} else {
+						this.isLastChild = 1
+						this.formdata = {
+							complaintContent: ""
+						}
+						setTimeout(() => {
+							this.$refs.uForm.setRules(this.rules)
+						}, 200)
 					}
-					complaintRecord(param).then(res=>{
-						uni.showModal({
-							title: '',
-							content: '我们已收到您的反馈,谢谢',
-							showCancel: false
-						});
-					})
 				}
 			},
 			getList(){
@@ -81,12 +190,172 @@
 						this.feedbackItems = this.list
 					}
 				})
-			}
+			},
+			submit() {
+				if(this.fileList1.some(item=>item.status == 'uploading')) {
+					uni.showToast({
+						title: '等待图片上传中',
+						icon: 'none'
+					})
+					return
+				}
+				var images=[];
+				this.fileList1.forEach(function(element) {
+					images.push(element.url)
+				});
+				this.$refs.uForm.validate().then(res => {
+					if (res) {
+						const param = {
+							userId: this.userId,
+							complaintTypeId: this.templateId,
+							complaintContent: this.formdata.complaintContent,
+							courseId: this.courseId,
+							videoId: this.videoId,
+							complaintUrl: images.toString()
+						}
+						complaintRecord(param).then(res=>{
+							uni.showModal({
+								title: '',
+								content: '我们已收到您的反馈,谢谢',
+								showCancel: false
+							});
+						})
+					}
+				})
+			},
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: 'https://h5api.his.cdwjyyh.com/app/common/uploadOSS', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							setTimeout(() => {
+								console.log(JSON.parse(res.data).url)
+								resolve(JSON.parse(res.data).url)
+							}, 1000)
+						}
+					});
+				})
+			},
+			goLogin() {
+				this.$getProvider().then(provider=>{
+					console.log('当前的环境商',provider)
+					if (!provider) {
+					  reject()
+					}
+					uni.login({
+						provider: provider,
+						success: async loginRes => {
+							console.log(loginRes)
+							uni.getUserInfo({
+							   provider: provider,
+							   success: (infoRes)=> {
+								    uni.showToast({
+										title: '处理中...',
+										icon: 'loading'
+								    });
+									loginByMp({code: loginRes.code,encryptedData:infoRes.encryptedData,iv:infoRes.iv}).then(res=>{
+										 uni.hideLoading();
+										 if (res.code == 200) {
+											uni.setStorageSync('AppTokenmini_RTCourse', res.token);
+											uni.setStorageSync('userInfo', JSON.stringify(res.user));
+											this.userId = res.user.userId || ''
+											this.isLogin = true
+											this.getMenuButton()
+											this.getList()
+										 } else {
+											uni.showToast({
+												title: res.msg,
+												icon: 'none'
+											});
+										 }
+									 }).catch(err=>{
+										uni.hideLoading();
+										uni.showToast({
+											icon:'none',
+											title: "登录失败,请重新登录",
+										});
+									});
+							   }
+							});
+						}
+					})
+				}).catch(err => {})
+			},
 		}
 	};
 </script>
 
 <style scoped lang="scss">
+	.container {
+		background-color: #fff;
+	}
+	.formbox-title {
+		padding-bottom: 30rpx;
+		border-bottom: 1px solid #f4f4f4;
+	}
+	.formbox {
+		border-top: 1px solid #f4f4f4;
+		padding: 30rpx;
+	}
+	.box {
+		padding-bottom: 24rpx;
+		border-top: 1px solid #f4f4f4;
+		.imgitem {
+			padding-top: 20rpx;
+		}
+	}
+	.footer-btn {
+		margin-top: 50rpx;
+	}
+	.submit-btn {
+		width: 50%;
+		height: 88rpx;
+		line-height: 88rpx;
+		text-align: center;
+		font-size: 30rpx;
+		font-family: PingFang SC;
+		color: #FFFFFF;
+		background: rgb(0,178,106);
+		border-radius: 16rpx;
+		border: 1rpx solid ;
+		margin-bottom: 30rpx;
+		&::after {
+			border: none;
+		}
+	}
+	.back-btn {
+		color: #bbb;
+		background: transparent;
+		border-radius: 16rpx;
+		border: 1rpx solid #999;
+	}
 	.header-nav {
 		height: 88rpx;
 		display: flex;

+ 34 - 22
pages_course/video.vue

@@ -1,6 +1,10 @@
 <template>
 	<view class="content">
 		<view class="header-nav" :style="{height: `calc(88rpx + ${statusBarHeight}px)`,paddingTop: statusBarHeight + 'px'}">
+			<view class="arrow-left-warning" @click="feedback">
+				<image src="/static/images/warning.png"></image>
+				<text>投诉</text>
+			</view>
 			<view class="header-title" :style="{width:menuButtonLeft + 'px',height:menuButtonH+'px',lineHeight:menuButtonH+'px'}">{{courseInfo.title}}</view>
 		</view>
 		<view class="video-box">
@@ -77,20 +81,20 @@
 					{{courseInfo.courseName}}
 				</view>
 				<!-- 投诉 -->
-				<view class="warning" @click="feedback" v-if="isLogin&&videoId">
+				<!-- <view class="warning" @click="feedback" v-if="isLogin&&videoId">
 					<image src="https://cos.his.cdwjyyh.com/fs/20250606/66a44bedde6c4e9d937f4bf866aace40.png"></image>
 					<text>投诉</text>
-				</view>
+				</view> -->
 			</view>
 			<view class="tabbox-bar" v-if="openCommentStatus==1">
 				<view class="tabbox">
 					<view :class="currentTab == nav.id ? 'tabbox-active':''" v-for="nav in navList" :key="nav.id" @click="handleTab(nav.id)">{{nav.name}}</view>
 				</view>
 				<!-- 投诉 -->
-				<view class="warning" @click="feedback">
+				<!-- <view class="warning" @click="feedback">
 					<image src="https://cos.his.cdwjyyh.com/fs/20250606/66a44bedde6c4e9d937f4bf866aace40.png"></image>
 					<text>投诉</text>
-				</view>
+				</view> -->
 			</view>
 		</view>
 		<scroll-view
@@ -411,6 +415,7 @@
 			}
 		},
 		onLoad(option) {
+			console.log("onLoad===",'')
 			this.code = option.code
 			this.urlOption = option.course ? JSON.parse(option.course) : {}
 			this.videoId = this.urlOption.videoId || ''
@@ -457,8 +462,6 @@
 			if (this.player) {
 				this.player.pause()
 			}
-			this.getFinishCourseVideo()
-			this.getInternetTraffic()
 			// if (this.interval != null) {
 			// 	clearInterval(this.interval)
 			// 	this.interval = null
@@ -469,8 +472,6 @@
 				clearInterval(this.interval)
 				this.interval = null
 			}
-			this.getFinishCourseVideo()
-			this.getInternetTraffic()
 			this.clearIntegral()
 			// #ifndef H5
 			uni.offKeyboardHeightChange(this.keyboardHeightChange);
@@ -564,6 +565,7 @@
 				this.clearIntegral()
 				this.isEnded = true
 				this.showAnswerTip = true
+				this.isFinish = 1
 				this.getFinishCourseVideo()
 			},
 			getWaiting() {
@@ -689,11 +691,7 @@
 							if(status != this.openCommentStatus) {
 								this.openCommentStatus = status
 							}
-							if(this.openCommentStatus==1) {
-								this.currentTab = 0
-							} else {
-								this.currentTab = 1
-							}
+							this.currentTab = 1
 							if(this.openCommentStatus!=2 || this.showDanmu!=1) {
 								this.activeDanmus = []
 							}
@@ -745,9 +743,9 @@
 				)
 			},
 			handleAnswer(val) {
-				let {item, option} = val
+				let {item, option,index} = val
 				let time = this.playTime
-				if(this.isEnded) {
+				if(this.isEnded||this.isFinish==1) {
 					time = this.duration
 				} else {
 					if(time < this.playDuration&&this.isFinish!=1) {
@@ -763,18 +761,18 @@
 					return
 				}
 
-				if (item.type == 1) {
+				if (this.quesList[index].type == 1) {
 					// 单选option
-					item.answer = option.name
-				} else if (item.type == 2) {
+					this.quesList[index].answer = option.name
+				} else if (this.quesList[index].type == 2) {
 					// 多选
-					let answer = item.answer ? item.answer.split(',') : []
+					let answer = this.quesList[index].answer ? this.quesList[index].answer.split(',') : []
 					if (answer.indexOf(option.name) === -1) {
 						answer.push(option.name)
-						item.answer = answer.join(',')
+						this.quesList[index].answer = answer.join(',')
 					} else {
 						answer.splice(answer.indexOf(option.name), 1)
-						item.answer = answer.join(',')
+						this.quesList[index].answer = answer.join(',')
 					}
 				}
 			},
@@ -815,7 +813,7 @@
 			// 答题
 			courseAnswer() {
 				let time = this.playTime
-				if (this.isEnded) {
+				if (this.isEnded || this.isFinish==1) {
 					time = this.duration
 				} else {
 					if (time < this.playDuration && this.isFinish != 1) {
@@ -2039,4 +2037,18 @@
 			transform: translateX(-100%);
 		}
 	}
+	.arrow-left-warning {
+		position: absolute;
+		left: 24rpx;
+		height: 88rpx;
+		overflow: hidden;
+		color: #888;
+		font-size: 24rpx;
+		@include u-flex(column, center, center);
+		image {
+			flex-shrink: 0;
+			height: 36rpx;
+			width: 36rpx;
+		}
+	}
 </style>

+ 162 - 0
pages_course/webview.vue

@@ -0,0 +1,162 @@
+<template>
+	<view class="container">
+		<!-- 加载提示 -->
+		<view class="loading" v-if="loading">
+			<text>加载中...</text>
+		</view>
+
+		<!-- web-view组件 -->
+		<web-view :src="webviewUrl" @message="handleMessage" @load="onLoads" @error="onError"></web-view>
+	</view>
+</template>
+
+<script>
+	// import {
+	// 	loginByMp
+	// } from '@/api/user'
+	export default {
+		data() {
+			return {
+				loading: true,
+				webviewUrl: 'https://h5.fbylive.com/weixinOauth',
+				userInfo: null
+			}
+		},
+		onLoad(options) {
+			console.log("webview",options)
+			uni.setStorageSync('webview', "webview");
+			if (options.code) {
+				// uni.$emit('usercode', {  code: options.code });
+				this.loginweixin(options.code)
+
+			}
+			// 生成带参的H5授权页面URL
+			// this.webviewUrl = this.generateAuthUrl()
+		},
+		methods: {
+			loginweixin(datas) {
+				var data = {
+					code: datas,
+				}
+				loginByMp(data).then(res => {
+						this.res = res
+						uni.hideLoading();
+						if (res.code == 200) {
+							console.log(res)
+							uni.hideLoading();
+							uni.showToast({
+								icon: 'none',
+								title: "成功获取用户信息",
+							});
+							uni.setStorageSync('userInfos', JSON.stringify(res.user));
+							this.userInfo = res.user;
+							setTimeout(() => {
+								uni.navigateBack({
+									delta: 1
+								});
+							}, 200)
+						} else {
+							uni.hideLoading();
+							uni.showToast({
+								title: res.msg || '获取用户信息失败',
+								icon: 'none'
+							})
+						}
+					},
+					err => {}
+				).catch(err => {
+					uni.hideLoading();
+					uni.showToast({
+						icon: 'none',
+						title: "授权登录失败,请重新登录",
+					});
+				});
+			},
+			// 生成授权页面URL,附带小程序传递的参数
+			generateAuthUrl() {
+				// 获取当前小程序的场景值,用于后续业务处理
+				const scene = uni.getLaunchOptionsSync().scene
+
+				// 这里替换为你的uniapp H5项目域名
+
+				// 拼接参数,可包含小程序特有的信息
+				const params = {
+					scene,
+					appid: 'wx961fadab9bcb792b', // 公众号AppID
+					redirect_uri: encodeURIComponent('https://h5.fbylive.com/weixinOauth'),
+					scope: 'snsapi_userinfo',
+					state: 'wechat_redirect'
+				}
+
+				// 微信公众号授权URL
+				return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${params.appid}&redirect_uri=${params.redirect_uri}&response_type=code&scope=${params.scope}&state=${params.state}#wechat_redirect`
+			},
+
+			// 处理web-view向小程序发送的消息
+			handleMessage(e) {
+				console.log('收到web-view消息:', e.detail)
+				console.log('收到web-view消息:', e)
+				// 获取H5页面传递过来的用户信息
+				if (e.detail && e.detail.type === 'user_info') {
+					this.userInfo = e.detail.data
+					this.token = e.detail.token
+					// 存储用户信息到本地
+					uni.setStorageSync('userInfo', this.userInfo)
+					uni.setStorageSync('TOKEN_WEXIN', this.userInfo)
+
+					// 返回上一页或跳转到首页
+					uni.showToast({
+						title: '登录成功',
+						icon: 'success'
+					})
+
+					setTimeout(() => {
+						uni.navigateBack()
+					}, 1500)
+				}
+			},
+
+			// web-view加载完成
+			onLoads() {
+				this.loading = false
+				console.log('web-view加载完成')
+			},
+
+			// web-view加载失败
+			onError(e) {
+				this.loading = false
+				console.error('web-view加载失败:', e)
+				uni.showToast({
+					title: '页面加载失败',
+					icon: 'none'
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+	.container {
+		width: 100%;
+		height: 100%;
+		position: relative;
+	}
+
+	.loading {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background-color: #fff;
+		z-index: 100;
+	}
+
+	web-view {
+		width: 100%;
+		height: 100%;
+	}
+</style>

BIN
static/images/warning.png