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>