uniapp开发app解决视频层级太高的问题(subNvue方法)

app和h5他们不一样,video map等原生标签高于小程序和app的webview渲染引擎,所以会在所有层级上面

问题:视频层级太高

解决办法:

cover-view 和 原生子窗体subNvue

我们这用的就是subNvue

解决示例:

关键代码:

pages.json

company_footer.nvue

javascript 复制代码
<template>
	<div class="footer-box">
		<div class="footer fl-row">
			<div class="btn" @click="isSave">
				<text class="btn-text">保 存</text>
			</div>
		</div>
		<div class="footer-height" :style="{height: iStatusBarBottom}"></div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				iStatusBarBottom: +uni.getSystemInfoSync().safeAreaInsets.bottom + 'px'
			}
		},
		methods: {
			isSave() {
				uni.$emit('isSave')
			}
		}
	}
</script>

<style>
	.footer-box {
		display: flex;
		justify-content: flex-end;
		background: #ffffff;
	}

	.fl-row {
		flex-direction: row;
	}

	.footer {
		height: 120upx;
		display: flex;
		justify-content: center;
		align-items: center;
		padding: 20rpx 15rpx;
		background: #ffffff;
		box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.08);
	}

	.client-footer .basics-footer {
		position: relative;
		color: black;
	}

	.bar-btn {
		flex: 1;
		height: 75rpx;
		font-size: 30rpx;
		text-align: center;
		line-height: 75rpx;
	}

	.btn {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 720rpx;
		height: 80rpx;
		line-height: 80rpx;
		background: #4a6fff;
		color: #fff;
		text-align: center;
		font-size: 32rpx;
		font-weight: bold;
		border-radius: 12rpx;
		box-shadow: 0 8rpx 16rpx rgba(47, 88, 255, 0.35);
	}
	.btn-text {
		font-size: 32rpx;
		font-weight: bold;
		color: #fff;
		text-align: center;
	}

	.footer-height {
		background-color: white;
	}
</style>

关键 ~ 使用subNvue的edit.vue:

先注释在非app阶段不执行这个,因为subNvue只能在app使用

然后在pages.json那写就行了,不需要在这个页面再写什么

javascript 复制代码
		<!-- #ifndef APP-PLUS -->
		<view class="btnFixed btn">
			<button @click="save">保 存</button>
		</view>
		<!-- #endif -->
		
		// js
		subNvue: null,

		onLoad(option) {
			// #ifdef APP-PLUS
			this.footerShow()
			uni.$on('isSave', () => {
				this.save();
			});
			// #endif
		},
		
		methods: {
			footerShow() {
				this.subNvue = uni.getSubNVueById("companyFooter"); //获取
				this.subNvue.show(); // 显示
			},
		},

全部完整参考代码:

javascript 复制代码
// edit.vue
<template>
	<view class="add myPageBott">
		<view class="pageBox">
			<view class="form-section">
				<view class="form-group">
					<view class="form-item">
						<view class="form-label">
							<text class="required">*</text>
							<text>生产基地</text>
						</view>
						<!-- <view class="form-input">
							<input v-model="form.prodBaseName" type="text" placeholder="请输入生产基地名称" maxlength="20" />
						</view> -->
						<view class="form-input picker-input">
							<view class="picker" @click="pickerShow2 = true">
								<text v-if="form.prodBaseName" class="selected">{{ form.prodBaseName }}</text>
								<text v-else class="placeholder">请选择生产基地</text>
								<text class="iconfont icon-right arrow"></text>
							</view>
						</view>
					</view>
					<view class="form-item">
						<view class="form-label">
							<text class="required">*</text>
							<text>企业名称</text>
						</view>
						<view class="form-input">
							<input v-model="form.companyName" type="text" placeholder="请输入企业名称" maxlength="20" />
						</view>
					</view>
					<view class="form-item">
						<view class="form-label">
							<text class="required">*</text>
							<text>养殖方式</text>
						</view>
						<view class="form-input picker-input">
							<view class="picker" @click="pickerShow1 = true">
								<text v-if="form.breedMethod" class="selected">{{ form.breedMethod }}</text>
								<text v-else class="placeholder">请选择品种</text>
								<text class="iconfont icon-right arrow"></text>
							</view>
						</view>
					</view>
					<view class="form-item">
						<view class="form-label">
							<text class="required">*</text>
							<text>负责人</text>
						</view>
						<view class="form-input">
							<input v-model="form.rsName" type="text" placeholder="请输入负责人" maxlength="20" />
						</view>
					</view>
					<view class="form-item">
						<view class="form-label">
							<text class="required">*</text>
							<text>联系方式</text>
						</view>
						<view class="form-input">
							<input v-model="form.rsPhone" type="number" placeholder="请输入联系方式" maxlength="20" />
						</view>
					</view>
					<view class="form-item">
						<view class="form-label">
							<text>生产地址</text>
						</view>
						<view class="form-input">
							<textarea v-model="form.address" placeholder="请输入生产地址" maxlength="200" auto-height />
						</view>
					</view>
				</view>
			</view>

			<view class="form-section">
				<view class="section-title">
					<text>简介照片</text>
					<text>资质照片、企业门口等</text>
				</view>
				<view class="box">
					<view class="imgList">
						<view class="nullFile2" @click="chooseFileEven">
							<text class="iconfont icon-add"></text>
							<text>点击上传</text>
						</view>
						<view class="item" v-for="(item, index) in fileList1" :key="index">
							<view class="imgBox">
								<image :src="item" mode="aspectFill" @click="previewImage(fileList1, index)" />
								<view class="del" @click="delImage(item, index)">
									<image src="/static/img/del.png" mode="scaleToFill"></image>
								</view>
								<view class="num">
									<text>{{ index + 1 }}</text>
								</view>
							</view>
						</view>
					</view>
				</view>
			</view>

			<view class="form-section">
				<view class="section-title">
					<text>视频</text>
					<text>非必需</text>
				</view>
				<view class="box">
					<view class="upload">
						<view class="fileData" v-if="form.video">
							<video :src="form.video">
								<cover-view class="del" @click="form.video = ''">
									<cover-image src="/static/img/del.png" mode="scaleToFill"></cover-image>
								</cover-view>
							</video>
						</view>
						<view class="nullFile" v-else @click="chooseVideoEven">
							<text class="iconfont icon-add"></text>
							<text>点击上传</text>
						</view>
					</view>
				</view>
			</view>
		</view>

		<!-- #ifndef APP-PLUS -->
		<view class="btnFixed btn">
			<button @click="save">保 存</button>
		</view>
		<!-- #endif -->

		<u-picker :show="pickerShow1" :columns="breedMethodList" keyName="name" :closeOnClickOverlay="true"
			@close="closePicker" @cancel="closePicker" @confirm="confirmPicker1"></u-picker>
		<u-picker :show="pickerShow2" :columns="prodBaseList" keyName="name" :closeOnClickOverlay="true"
			@close="closePicker" @cancel="closePicker" @confirm="confirmPicker2"></u-picker>
	</view>
</template>

<script>
	import {
		getProdBaseApi,
		addProdBaseApi,
		updateProdBaseApi
	} from "@/api/trace";
	import {
		getProdBaseListApi
	} from "@/api/index.js"
	import {
		headerUploads,
		videoFile
	} from "@/utils/tools.js";
	export default {
		data() {
			return {
				pickerShow1: false,
				pickerShow2: false,
				fileList1: [],
				breedMethodList: [
					[{
							id: 1,
							name: '散养'
						},
						{
							id: 2,
							name: '放养'
						},
					]
				],
				prodBaseList: [
					[]
				],
				form: {},
				subNvue: null,
			};
		},
		onLoad(option) {
			// #ifdef APP-PLUS
			this.footerShow()
			uni.$on('isSave', () => {
				this.save();
			});
			// #endif
			this.reset()
			this.GetProdBaseList()
			if (option.prodBaseId) {
				this.form.prodBaseId = option.prodBaseId;
				uni.setNavigationBarTitle({
					title: "编辑生产基地"
				});
				this.getDetail(option.prodBaseId)
			} else {
				this.form.breedMethod = this.breedMethodList[0][0].name
			}
		},
		methods: {
			footerShow() {
				this.subNvue = uni.getSubNVueById("companyFooter"); //获取
				this.subNvue.show(); // 显示
			},
			previewImage(urls, current) {
				if (urls && urls.length > 0) {
					uni.previewImage({
						urls,
						current,
						longPressActions: {
							itemList: ["发送给朋友", "保存图片", "收藏"],
						},
					});
				}
			},
			delImage(item, index) {
				this.fileList1.splice(index, 1);
			},
			async chooseFileEven() {
				let count = 10 - this.fileList1.length;
				if (count > 0) {
					let list = (await headerUploads(10 - this.fileList1.length)) || [];
					this.fileList1 = [...this.fileList1, ...list];
				} else {
					this.$toast({
						title: "超过10张图片,请先删除部分再上传",
					});
				}
			},
			async chooseVideoEven() {
				let res = await videoFile();
				this.form.video = res.url;
			},
			closePicker() {
				this.pickerShow1 = false
				this.pickerShow2 = false
			},
			confirmPicker1(e) {
				this.form.breedMethod = e.value[0].name
				this.closePicker()
			},
			confirmPicker2(e) {
				this.form.prodBaseName = e.value[0].name
				this.closePicker()
			},
			// 获取详情
			getDetail(prodBaseId) {
				getProdBaseApi(prodBaseId).then(response => {
					this.form = response.data
					this.fileList1 = this.form.photos ? this.form.photos.split(';') : []
				})
			},
			GetProdBaseList() {
				getProdBaseListApi({}).then(res => {
					this.prodBaseList = [res.data]
				})
			},
			// 表单重置
			reset() {
				this.form = {
					prodBaseId: null,
					companyName: null,
					prodBaseName: null,
					address: null,
					rsName: null,
					rsPhone: null,
					breedMethod: null,
					photos: null,
					video: null,
					createBy: null,
					createMan: null,
					createTime: null,
					updateBy: null,
					updateTime: null,
				}
			},
			// 保存
			save() {
				// 表单验证
				if (!this.form.companyName) {
					uni.showToast({
						title: "请输入企业名称",
						icon: "none",
					});
					return;
				}
				if (!this.form.prodBaseName) {
					uni.showToast({
						title: "请输入生产基地名称",
						icon: "none",
					});
					return;
				}
				if (!this.form.breedMethod) {
					uni.showToast({
						title: "请输入养殖方式",
						icon: "none",
					});
					return;
				}
				if (!this.form.rsName) {
					uni.showToast({
						title: "请输入负责人",
						icon: "none",
					});
					return;
				}
				if (!this.form.rsPhone) {
					uni.showToast({
						title: "请输入联系方式",
						icon: "none",
					});
					return;
				}
				// if (!this.form.address) {
				// 	uni.showToast({
				// 		title: "请输入生产地址",
				// 		icon: "none",
				// 	});
				// 	return;
				// }
				// if(this.fileList1 && this.fileList1.length > 0) {
				// 	this.form.photos = this.fileList1.join(';')
				// } else {
				// 	uni.showToast({
				// 		title: "请输入简介照片",
				// 		icon: "none",
				// 	});
				// 	return;
				// }

				if (this.form.prodBaseId != null) {
					updateProdBaseApi(this.form).then(response => {
						this.$toast({
							title: '修改成功'
						})
						uni.navigateBack()
					})
				} else {
					addProdBaseApi(this.form).then(response => {
						this.$toast({
							title: '新增成功'
						})
						uni.navigateBack()
					})
				}

			},
		},
	};
</script>

<style lang="scss">
	.add {
		background-color: #f5f6f8;
		min-height: 100vh;
		// #ifdef H5
		min-height: calc(100vh - 44px);
		// #endif

		.pageBox {
			padding: 20rpx 30rpx;
			padding-bottom: 40rpx;
		}

		// 表单区块
		.form-section {
			background-color: #fff;
			border-radius: 16rpx;
			overflow: hidden;
			box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
			margin-bottom: 20rpx;

			&:last-child {
				margin-bottom: 0;
			}

			.section-title {
				display: flex;
				align-items: center;
				justify-content: space-between;
				padding: 30rpx 30rpx 20rpx;
				border-bottom: 1rpx solid #f0f0f0;

				text {
					font-size: 32rpx;
					font-weight: bold;
					color: #333;

					&:nth-child(2) {
						font-size: 24rpx;
						color: #999;
					}
				}
			}

			.form-group {
				padding: 20rpx 30rpx 30rpx;
			}
		}

		// 表单项
		.form-item {
			display: flex;
			flex-direction: column;
			margin-bottom: 30rpx;

			&:last-child {
				margin-bottom: 0;
			}

			.form-label {
				display: flex;
				align-items: center;
				margin-bottom: 16rpx;
				font-size: 28rpx;
				color: #333;
				font-weight: 500;

				.required {
					color: #ff4949;
					margin-right: 4rpx;
					font-size: 30rpx;
				}
			}

			.form-input {
				position: relative;
				display: flex;
				align-items: center;
				width: 100%;
				padding: 20rpx 24rpx;
				background-color: #f5f7fa;
				border-radius: 12rpx;
				border: 2rpx solid transparent;
				transition: all 0.3s ease;

				input,
				textarea {
					flex: 1;
					width: 100%;
					font-size: 28rpx;
					color: #333;
					background: transparent;
				}

				textarea {
					min-height: 120rpx;
					line-height: 1.6;
				}

				.unit {
					margin-left: 16rpx;
					font-size: 28rpx;
					color: #999;
					white-space: nowrap;
				}

				.picker {
					flex: 1;
					display: flex;
					align-items: center;
					justify-content: space-between;

					.selected,
					.placeholder {
						font-size: 28rpx;
					}

					.selected {
						color: #333;
					}

					.placeholder {
						color: #c0c4cc;
					}

					.arrow {
						font-size: 24rpx;
						color: #c0c4cc;
						margin-left: 12rpx;
					}
				}

				&:focus-within {
					background-color: #fff;
					border-color: #2f58ff;
				}
			}
		}

		.box {
			padding: 20rpx 30rpx 30rpx;

			.imgList {
				display: flex;
				flex-wrap: wrap;
				gap: 20rpx;

				.item {
					position: relative;
					width: calc(33.333% - 14rpx);
					aspect-ratio: 1;

					.imgBox {
						position: relative;
						width: 100%;
						height: 100%;
						border-radius: 12rpx;
						overflow: hidden;
						box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);

						image {
							width: 100%;
							height: 100%;
						}

						.del {
							position: absolute;
							top: 8rpx;
							right: 8rpx;
							width: 48rpx;
							height: 48rpx;
							background: rgba(0, 0, 0, 0.6);
							border-radius: 50%;
							padding: 8rpx;
							cursor: pointer;
							transition: all 0.3s ease;
							z-index: 10;

							image {
								width: 100%;
								height: 100%;
							}

							&:active {
								transform: scale(0.9);
								background: rgba(0, 0, 0, 0.8);
							}
						}

						.num {
							position: absolute;
							bottom: 8rpx;
							right: 8rpx;
							padding: 4rpx 12rpx;
							background: rgba(0, 0, 0, 0.6);
							border-radius: 8rpx;
							font-size: 24rpx;
							color: #fff;
						}
					}
				}

				.nullFile2 {
					width: calc(33.333% - 14rpx);
					aspect-ratio: 1;
					display: flex;
					flex-direction: column;
					align-items: center;
					justify-content: center;
					background: linear-gradient(135deg, rgba(47, 88, 255, 0.05) 0%, rgba(90, 125, 255, 0.05) 100%);
					border: 2rpx dashed #2f58ff;
					border-radius: 12rpx;
					cursor: pointer;
					transition: all 0.3s ease;

					.icon-add {
						font-size: 60rpx;
						color: #2f58ff;
						margin-bottom: 12rpx;
					}

					text {
						font-size: 26rpx;
						color: #2f58ff;
					}

					&:active {
						background: linear-gradient(135deg, rgba(47, 88, 255, 0.1) 0%, rgba(90, 125, 255, 0.1) 100%);
						border-color: #5a7dff;
					}
				}
			}

			.upload {
				.fileData {
					position: relative;
					width: 100%;
					height: 400rpx;
					background: #f5f7fa;
					border-radius: 16rpx;
					overflow: hidden;

					video {
						width: 100%;
						height: 100%;
					}

					.del {
						position: absolute;
						z-index: 5;
						top: 16rpx;
						right: 16rpx;
						width: 56rpx;
						height: 56rpx;
						background: rgba(255, 71, 87, 0.9);
						border-radius: 50%;
						display: flex;
						align-items: center;
						justify-content: center;
						padding: 12rpx;
						box-shadow: 0 4rpx 12rpx rgba(255, 71, 87, 0.3);

						image {
							width: 100%;
							height: 100%;
						}
					}
				}

				.nullFile {
					width: 100%;
					height: 300rpx;
					background: linear-gradient(135deg,
							rgba(47, 88, 255, 0.05) 0%,
							rgba(90, 125, 255, 0.08) 100%);
					border: 2rpx dashed #2f58ff;
					border-radius: 16rpx;
					display: flex;
					flex-direction: column;
					align-items: center;
					justify-content: center;
					gap: 16rpx;
					transition: all 0.3s ease;

					&:active {
						transform: scale(0.95);
						background: rgba(47, 88, 255, 0.1);
					}

					.iconfont {
						font-size: 56rpx;
						color: #2f58ff;
					}

					text:last-child {
						font-size: 26rpx;
						color: #2f58ff;
					}
				}
			}
		}

		.btnFixed {
			position: fixed;
			bottom: 0;
			left: 0;
			right: 0;
			padding: 20rpx 15rpx;
			background: #ffffff;
			box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.08);
			z-index: 100;

			button {
				width: 100%;
				height: 80rpx;
				line-height: 80rpx;
				background: linear-gradient(135deg, #2f58ff 0%, #5a7dff 100%);
				color: #fff;
				font-size: 32rpx;
				font-weight: bold;
				letter-spacing: 4rpx;
				border-radius: 12rpx;
				border: none;
				box-shadow: 0 8rpx 16rpx rgba(47, 88, 255, 0.35);
				transition: all 0.3s ease;
				overflow: visible;

				&::after {
					border: none;
				}
			}
		}
	}
</style>
相关推荐
EasyCVR2 小时前
视频汇聚平台EasyCVR重塑安防与物联可视化,打造视频“万物互联”的智能枢纽
音视频
乘凉~2 小时前
【MoneyPrinterTurbo】一个利用AI大模型一键生成高清短视频的工具,开源免费,只要有电脑就能用
人工智能·电脑·音视频
Jinuss2 小时前
源码分析之React中useCallback和useMemo
前端·javascript·react.js
maxmaxma2 小时前
ROS2机器人少年创客营:Python第一课
前端·python·机器人
吃西瓜的年年2 小时前
react(二)useEffect 和 useRef
前端·react.js·前端框架
RDCJM2 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
LZQ <=小氣鬼=>2 小时前
React 插槽(Slot)
前端·javascript·react.js
前端老石人2 小时前
HTML 内容分组终极指南:从语义化标签到现代 Web 结构
前端·html
大转转FE2 小时前
转转前端周刊第191期: 淘宝闪购 AI Agent 的秒级响应记忆系统
前端·人工智能