flx:优化
This commit is contained in:
parent
c8dfacb2b1
commit
999e00c2e6
@ -6,27 +6,62 @@
|
||||
</view>
|
||||
|
||||
<!-- APP平台使用原生video组件 -->
|
||||
<video id="video" v-else :hidden="showMask" :src="src" :poster="poster" :controls="true" :autoplay="autoplay"
|
||||
:muted="muted" :loop="loop" :enable-play-gesture="true" :enable-progress-gesture="true"
|
||||
:show-fullscreen-btn="true" :show-play-btn="true" :show-center-play-btn="true" :show-loading="true"
|
||||
:object-fit="objectFit" class="video-player" @loadstart="onLoadStart" @loadedmetadata="onLoadedMetadata"
|
||||
@canplay="onCanPlay" @play="onPlay" @pause="onPause" @ended="onEnded" @error="onError"
|
||||
@timeupdate="onTimeUpdate" @fullscreenchange="onFullscreenChange">
|
||||
<cover-view @click="videoClick" class="maskleft"></cover-view>
|
||||
<cover-view @click="videoClick" class="maskright"></cover-view>
|
||||
<video v-else id="video" :hidden="showMask || error" :src="currentVideoSrc || src" :poster="poster"
|
||||
:controls="true" :autoplay="autoplay" :muted="muted" :loop="loop" :enable-play-gesture="true"
|
||||
:enable-progress-gesture="true" :show-fullscreen-btn="true" :show-play-btn="true"
|
||||
:show-center-play-btn="true" :show-loading="false" :show-error-msg="false" :object-fit="objectFit"
|
||||
class="video-player" @loadstart="onLoadStart" @loadedmetadata="onLoadedMetadata" @canplay="onCanPlay"
|
||||
@play="onPlay" @pause="onPause" @error="onVideoError" @ended="onEnded" @timeupdate="onTimeUpdate"
|
||||
@fullscreenchange="onFullscreenChange" @click="onVideoClick">
|
||||
<cover-view @click="videoClick" v-if="!error" class="maskleft"></cover-view>
|
||||
</video>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="loading" class="loading-overlay">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<!-- 加载状态 - APP平台使用cover-view覆盖 -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<cover-view v-if="loading" class="loading-overlay">
|
||||
<cover-image src="@/static/iscImage/loading.gif" class="loading-spinner"></cover-image>
|
||||
<!-- <cover-view class="loading-text">加载中...</cover-view> -->
|
||||
</cover-view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<view v-if="error" class="error-overlay">
|
||||
<text class="error-text">{{ errorMessage }}</text>
|
||||
<view class="retry-btn" @click="retry">重试</view>
|
||||
<!-- 加载状态 - H5平台使用普通view -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="loading" class="loading-overlay">
|
||||
<image src="@/static/iscImage/loading.gif" class="loading-spinner"></image>
|
||||
<!-- <text class="loading-text">加载中...</text> -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 错误状态 - APP平台使用cover-view覆盖 -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<cover-view v-if="error" class="error-overlay" @click="videoClick">
|
||||
<cover-view class="error-icon" v-if="customErrorIcon">
|
||||
<cover-image :src="customErrorIcon" class="error-icon-img"></cover-image>
|
||||
</cover-view>
|
||||
<cover-view class="error-text" @click="videoClick">{{ displayErrorMessage }}</cover-view>
|
||||
<cover-view v-if="showRetryBtn" class="retry-btn">
|
||||
<cover-image @click="retry" src="@/static/iscImage/reload.png" class="error-icon-img"></cover-image>
|
||||
<cover-view @click="retry">{{ retryBtnText }}</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 错误状态 - H5平台使用普通view -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="error" class="error-overlay">
|
||||
<!-- 自定义错误插槽 -->
|
||||
<slot name="error" :error="errorInfo" :retry="retry">
|
||||
<view class="error-icon" v-if="customErrorIcon">
|
||||
<image :src="customErrorIcon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="error-text">{{ displayErrorMessage }}</text>
|
||||
<view v-if="showRetryBtn" class="retry-btn" @click="retry">
|
||||
<image src="@/static/iscImage/reload.png" class="error-icon-img"></image>
|
||||
<text>{{ retryBtnText }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -89,6 +124,31 @@
|
||||
playerIndex: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
// 自定义错误消息
|
||||
// errorMessage: {
|
||||
// type: String,
|
||||
// default: ''
|
||||
// },
|
||||
// 自定义错误处理函数
|
||||
errorHandler: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
// 是否显示重试按钮
|
||||
showRetryBtn: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 重试按钮文字
|
||||
retryBtnText: {
|
||||
type: String,
|
||||
default: '重试'
|
||||
},
|
||||
// 自定义错误图标
|
||||
customErrorIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -96,7 +156,16 @@
|
||||
loading: false,
|
||||
error: false,
|
||||
errorMessage: '',
|
||||
player: null
|
||||
errorInfo: {
|
||||
code: null,
|
||||
message: '',
|
||||
type: '',
|
||||
details: null
|
||||
},
|
||||
player: null,
|
||||
loadTimeout: null, // 加载超时定时器
|
||||
loadStartTime: null, // 加载开始时间
|
||||
currentVideoSrc: '' // 当前视频源,用于重试时重新加载
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -108,6 +177,13 @@
|
||||
// #ifdef APP-PLUS
|
||||
return false;
|
||||
// #endif
|
||||
},
|
||||
// 显示的错误消息
|
||||
displayErrorMessage() {
|
||||
if (this.errorMessage) {
|
||||
return this.errorMessage;
|
||||
}
|
||||
return this.errorInfo.message || '视频加载失败';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -115,11 +191,17 @@
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroyPlayer();
|
||||
this.clearLoadTimeout();
|
||||
},
|
||||
watch: {
|
||||
src(newSrc) {
|
||||
if (this.player && this.isH5) {
|
||||
this.player.reloadUrl(newSrc);
|
||||
} else if (!this.isH5) {
|
||||
// APP平台src变化时重新初始化
|
||||
this.error = false;
|
||||
this.errorMessage = '';
|
||||
this.initPlayer();
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -130,11 +212,54 @@
|
||||
this.initH5Player();
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// APP平台使用原生video组件,无需额外初始化
|
||||
// #ifdef APP-PLUS
|
||||
// APP平台使用原生video组件,添加超时检测
|
||||
this.initAppPlayer();
|
||||
// #endif
|
||||
},
|
||||
|
||||
// APP平台初始化播放器
|
||||
initAppPlayer() {
|
||||
// #ifdef APP-PLUS
|
||||
// 清除之前的超时定时器
|
||||
this.clearLoadTimeout();
|
||||
|
||||
// 重置错误状态
|
||||
this.error = false;
|
||||
this.errorMessage = '';
|
||||
this.loading = true;
|
||||
this.loadStartTime = Date.now();
|
||||
|
||||
// 设置当前视频源
|
||||
this.currentVideoSrc = this.src;
|
||||
|
||||
// 设置加载超时检测(30秒)
|
||||
this.loadTimeout = setTimeout(() => {
|
||||
if (this.loading && this.error) {
|
||||
console.warn('视频加载超时');
|
||||
this.handleError('预览失败,请检查设备网络', {
|
||||
code: 10002,
|
||||
type: 'timeout',
|
||||
details: {
|
||||
timeout: 30000,
|
||||
loadStartTime: this.loadStartTime
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
}, 4000);
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 清除加载超时定时器
|
||||
clearLoadTimeout() {
|
||||
if (this.loadTimeout) {
|
||||
clearTimeout(this.loadTimeout);
|
||||
this.loadTimeout = null;
|
||||
}
|
||||
},
|
||||
|
||||
// H5平台初始化mui-player
|
||||
initH5Player() {
|
||||
// #ifdef H5
|
||||
@ -235,28 +360,92 @@
|
||||
retry() {
|
||||
this.error = false;
|
||||
this.errorMessage = '';
|
||||
this.errorInfo = {
|
||||
code: null,
|
||||
message: '',
|
||||
type: '',
|
||||
details: null
|
||||
};
|
||||
this.loading = true;
|
||||
// #ifdef APP-PLUS
|
||||
this.clearLoadTimeout();
|
||||
|
||||
// 重新初始化播放器
|
||||
// APP平台通过改变src来强制重新加载
|
||||
this.$nextTick(() => {
|
||||
// 先清空src
|
||||
this.currentVideoSrc = '';
|
||||
this.$emit('retryClick', this.src);
|
||||
// 延迟后重新设置src,触发重新加载
|
||||
// setTimeout(() => {
|
||||
// this.currentVideoSrc = this.src;
|
||||
// // 重新初始化播放器(设置超时检测等)
|
||||
// this.initAppPlayer();
|
||||
// }, 100);
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// H5平台重新初始化播放器
|
||||
this.$nextTick(() => {
|
||||
this.initPlayer();
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 处理错误
|
||||
handleError(message) {
|
||||
handleError(message, errorInfo = {}) {
|
||||
// 构建错误信息对象
|
||||
const errorData = {
|
||||
code: errorInfo.code || null,
|
||||
message: message || '视频加载失败',
|
||||
type: errorInfo.type || 'unknown',
|
||||
details: errorInfo.details || null,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
// 如果有自定义错误处理函数,先执行
|
||||
if (this.errorHandler && typeof this.errorHandler === 'function') {
|
||||
const customResult = this.errorHandler(errorData);
|
||||
// 如果返回 false,则不显示默认错误提示
|
||||
if (customResult === false) {
|
||||
return;
|
||||
}
|
||||
// 如果返回字符串,使用该字符串作为错误消息
|
||||
if (typeof customResult === 'string') {
|
||||
errorData.message = customResult;
|
||||
}
|
||||
}
|
||||
|
||||
this.error = true;
|
||||
this.errorMessage = message;
|
||||
this.errorInfo = errorData;
|
||||
this.errorMessage = errorData.message;
|
||||
this.loading = false;
|
||||
this.$emit('error', {
|
||||
message
|
||||
});
|
||||
|
||||
this.$emit('error', errorData);
|
||||
},
|
||||
|
||||
// 事件处理方法
|
||||
onLoadStart() {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
this.loadStartTime = Date.now();
|
||||
// #ifdef APP-PLUS
|
||||
// APP平台重新设置超时检测
|
||||
this.clearLoadTimeout();
|
||||
this.loadTimeout = setTimeout(() => {
|
||||
if (this.loading && !this.error) {
|
||||
console.warn('视频加载超时');
|
||||
this.handleError('视频加载超时,请检查网络连接', {
|
||||
code: 10002,
|
||||
type: 'timeout',
|
||||
details: {
|
||||
timeout: 30000,
|
||||
loadStartTime: this.loadStartTime
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 30000);
|
||||
// #endif
|
||||
this.$emit('loadstart');
|
||||
},
|
||||
|
||||
@ -266,12 +455,25 @@
|
||||
|
||||
onCanPlay() {
|
||||
this.loading = false;
|
||||
// #ifdef APP-PLUS
|
||||
// 清除超时定时器
|
||||
this.clearLoadTimeout();
|
||||
// #endif
|
||||
this.$emit('canplay');
|
||||
},
|
||||
|
||||
onPlay() {
|
||||
this.$emit('play');
|
||||
},
|
||||
|
||||
// 视频点击事件
|
||||
onVideoClick() {
|
||||
// 如果视频正在播放,触发点击事件
|
||||
if (!this.error && !this.loading) {
|
||||
this.videoClick();
|
||||
}
|
||||
},
|
||||
|
||||
videoClick() {
|
||||
this.$emit('playerClick', this.playerIndex);
|
||||
},
|
||||
@ -294,11 +496,140 @@
|
||||
onEnded() {
|
||||
this.$emit('ended');
|
||||
},
|
||||
|
||||
onVideoError(event) {
|
||||
console.error('Video error:', event);
|
||||
this.error = true;
|
||||
this.handleError('预览失败,请检查设备网络');
|
||||
// #ifdef APP-PLUS
|
||||
// APP平台清除超时定时器
|
||||
this.clearLoadTimeout();
|
||||
// #endif
|
||||
},
|
||||
onError(event) {
|
||||
console.error('Video error:', event);
|
||||
this.handleError('视频播放出错');
|
||||
this.$emit('error', event);
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// APP平台清除超时定时器
|
||||
this.clearLoadTimeout();
|
||||
// #endif
|
||||
|
||||
// 获取详细的错误信息
|
||||
let errorCode = null;
|
||||
let errorType = 'unknown';
|
||||
let errorMessage = '视频播放出错';
|
||||
let errorDetails = event;
|
||||
|
||||
// 尝试从事件中获取错误代码和类型
|
||||
if (event && event.detail) {
|
||||
// uni-app video 组件的错误信息(APP平台)
|
||||
errorCode = event.detail.errCode || event.detail.code;
|
||||
errorType = this.getErrorType(errorCode);
|
||||
errorMessage = this.getErrorMessage(errorCode, event.detail.errMsg);
|
||||
errorDetails = {
|
||||
...event.detail,
|
||||
platform: 'APP'
|
||||
};
|
||||
|
||||
// APP平台尝试通过videoContext获取更多信息
|
||||
// #ifdef APP-PLUS
|
||||
try {
|
||||
const videoContext = uni.createVideoContext('video', this);
|
||||
if (videoContext) {
|
||||
errorDetails.videoContext = 'available';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('获取videoContext失败:', e);
|
||||
}
|
||||
// #endif
|
||||
} else if (event && event.target) {
|
||||
// H5 video 元素的错误信息
|
||||
const video = event.target;
|
||||
if (video.error) {
|
||||
errorCode = video.error.code;
|
||||
errorType = this.getErrorType(errorCode);
|
||||
errorMessage = this.getErrorMessage(errorCode, video.error.message);
|
||||
errorDetails = {
|
||||
code: video.error.code,
|
||||
message: video.error.message,
|
||||
networkState: video.networkState,
|
||||
readyState: video.readyState,
|
||||
platform: 'H5'
|
||||
};
|
||||
}
|
||||
} else if (event && event.code !== undefined) {
|
||||
// mui-player 或其他播放器的错误信息
|
||||
errorCode = event.code;
|
||||
errorType = this.getErrorType(errorCode);
|
||||
errorMessage = this.getErrorMessage(errorCode, event.message);
|
||||
errorDetails = {
|
||||
...event,
|
||||
platform: 'H5'
|
||||
};
|
||||
} else {
|
||||
// 未知错误,尝试从event中提取信息
|
||||
errorDetails = {
|
||||
...event,
|
||||
platform: this.isH5 ? 'H5' : 'APP'
|
||||
};
|
||||
}
|
||||
|
||||
this.handleError(errorMessage, {
|
||||
code: errorCode,
|
||||
type: errorType,
|
||||
details: errorDetails
|
||||
});
|
||||
},
|
||||
|
||||
// 根据错误代码获取错误类型
|
||||
getErrorType(code) {
|
||||
if (code === null || code === undefined) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// MediaError 错误代码 (H5)
|
||||
// 1 = MEDIA_ERR_ABORTED - 用户中止
|
||||
// 2 = MEDIA_ERR_NETWORK - 网络错误
|
||||
// 3 = MEDIA_ERR_DECODE - 解码错误
|
||||
// 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - 格式不支持
|
||||
|
||||
// uni-app video 错误代码
|
||||
// 10001 = 系统错误
|
||||
// 10002 = 网络错误
|
||||
// 10003 = 文件错误
|
||||
// 10004 = 格式错误
|
||||
|
||||
const codeMap = {
|
||||
1: 'aborted',
|
||||
2: 'network',
|
||||
3: 'decode',
|
||||
4: 'not_supported',
|
||||
10001: 'system',
|
||||
10002: 'network',
|
||||
10003: 'file',
|
||||
10004: 'format'
|
||||
};
|
||||
|
||||
return codeMap[code] || 'unknown';
|
||||
},
|
||||
|
||||
// 根据错误代码获取错误消息
|
||||
getErrorMessage(code, defaultMsg) {
|
||||
if (this.errorMessage) {
|
||||
return this.errorMessage;
|
||||
}
|
||||
|
||||
const messageMap = {
|
||||
1: '视频加载被中断',
|
||||
2: '网络连接失败,请检查网络设置',
|
||||
3: '视频解码失败,可能文件已损坏',
|
||||
4: '视频格式不支持',
|
||||
10001: '系统错误,请稍后重试',
|
||||
10002: '网络错误,请检查网络连接',
|
||||
10003: '文件错误,视频文件可能不存在',
|
||||
10004: '视频格式不支持'
|
||||
};
|
||||
|
||||
return messageMap[code] || defaultMsg || '视频播放出错';
|
||||
},
|
||||
|
||||
onTimeUpdate() {
|
||||
@ -351,27 +682,37 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
video {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/deep/ .uni-video-slots {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.maskleft {
|
||||
width: 30%;
|
||||
width: calc(100% - 2rpx);
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.maskright {
|
||||
width: 30%;
|
||||
height: 80%;
|
||||
}
|
||||
// .maskright {
|
||||
// width: 30%;
|
||||
// height: 80%;
|
||||
// }
|
||||
|
||||
.m3u8-player {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
width: calc(100% - 2rpx);
|
||||
height: calc(100% - 2rpx);
|
||||
// background: #000;
|
||||
z-index: 1;
|
||||
/* #ifdef APP-PLUS */
|
||||
// 防止在swiper中滑动时错位
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
will-change: transform;
|
||||
/* #endif */
|
||||
|
||||
.video-container {
|
||||
width: 100%;
|
||||
@ -388,14 +729,21 @@
|
||||
}
|
||||
|
||||
.video-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
width: calc(100% - 2rpx);
|
||||
height: calc(100% - 2rpx);
|
||||
// object-fit: contain;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
// z-index: 1;
|
||||
/* #ifdef APP-PLUS */
|
||||
// 防止在swiper中滑动时错位
|
||||
// transform: translateZ(0);
|
||||
// -webkit-transform: translateZ(0);
|
||||
// will-change: transform;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -405,16 +753,12 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
// background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 10;
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 4rpx solid #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
width: 500rpx;
|
||||
height: 500rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
@ -434,26 +778,139 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
// background: #000;
|
||||
z-index: 10;
|
||||
width: calc(100% - 2rpx);
|
||||
|
||||
.error-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 30rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
padding: 20rpx 40rpx;
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
font-size: 24rpx;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.error-icon-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* APP平台 cover-view 样式 */
|
||||
/* #ifdef APP-PLUS */
|
||||
cover-view.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// background: rgba(0, 0, 0, 0.5);
|
||||
width: 100%;
|
||||
z-index: 998;
|
||||
// 防止在swiper中滑动时错位
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
|
||||
cover-view.loading-spinner {
|
||||
width: 500rpx;
|
||||
height: 500rpx;
|
||||
}
|
||||
|
||||
cover-view.loading-text {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
cover-view.error-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// background: #000;
|
||||
z-index: 999;
|
||||
width: 99%;
|
||||
// 防止在swiper中滑动时错位
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
|
||||
cover-view.error-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
cover-image.error-icon-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
cover-view.error-text {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
text-align: center;
|
||||
width: 99%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
cover-view.retry-btn {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
|
||||
.error-icon-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
padding-top: 14rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
:circular="circular" :duration="duration" :autoplay="autoplay"
|
||||
:previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
|
||||
:next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'" :style="{
|
||||
height: height + 'rpx',
|
||||
height: swiperHeight + 'rpx',
|
||||
backgroundColor: bgColor
|
||||
}">
|
||||
<swiper-item class="u-swiper-item" v-for="(item, index) in list" :key="index">
|
||||
@ -22,16 +22,12 @@
|
||||
}, titleStyle]">
|
||||
{{ item.title }}
|
||||
</view> -->
|
||||
<view class="player-main" :class="{'player-main_2': item.length > 1}">
|
||||
<view class="player-m3u8" @click="videoClick(video)"
|
||||
:class="{'active_box': activeInfo.itemId == video.itemId}" v-for="(video, eIndex) in item"
|
||||
<view v-if="uCurrent == index" class="player-main" :class="{'player-main_2': item.length > 1}">
|
||||
<view class="player-m3u8" :class="{'active_box': activeInfo.itemId == video.itemId}" @click="videoClick(video)"
|
||||
v-for="(video, eIndex) in item"
|
||||
:key="eIndex">
|
||||
<my-player-m3u8 @playerClick="onPlayerClick" :ref="`player-${eIndex}`" :playerIndex="eIndex"
|
||||
:showMask="false" v-if="video.url" :src="video.url" :autoplay="true" />
|
||||
<video class="player-m3u8_1" v-else :src="video.url" :autoplay="true">
|
||||
<cover-view @click="videoClick(video)" class="maskleft"></cover-view>
|
||||
<cover-view @click="videoClick(video)" class="maskright"></cover-view>
|
||||
</video>
|
||||
<my-player-m3u8 @retryClick="retryClick" @playerClick="onPlayerClick" :ref="`player-${video.itemId}`" :playerIndex="eIndex"
|
||||
:showMask="false" :src="video.url" :autoplay="true" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -128,7 +124,7 @@
|
||||
// list的高度,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 250
|
||||
default: 578
|
||||
},
|
||||
// 指示器的位置,topLeft|topCenter|topRight|bottomLeft|bottomCenter|bottomRight
|
||||
indicatorPos: {
|
||||
@ -186,26 +182,43 @@
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
showDetail: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 如果外部的list发生变化,判断长度是否被修改,如果前后长度不一致,重置uCurrent值,避免溢出
|
||||
list(nVal, oVal) {
|
||||
if (nVal.length !== oVal.length) this.uCurrent = 0;
|
||||
if (nVal.length !== oVal.length && this.uCurrent >= nVal.length) {
|
||||
this.uCurrent = nVal.length > 0 ? nVal.length - 1 : 0;
|
||||
}
|
||||
this.updateSwiperHeight(this.uCurrent);
|
||||
},
|
||||
// 监听外部current的变化,实时修改内部依赖于此测uCurrent值,如果更新了current,而不是更新uCurrent,
|
||||
// 就会错乱,因为指示器是依赖于uCurrent的
|
||||
current(n) {
|
||||
this.uCurrent = n;
|
||||
}
|
||||
this.updateSwiperHeight(n);
|
||||
},
|
||||
uCurrent() {
|
||||
console.log("我触发了1122")
|
||||
this.onPlayerClick(0)
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uCurrent: this.current, // 当前活跃的swiper-item的index
|
||||
activeInfo: {},
|
||||
currentSwiperHeight: Number(this.height) || 578
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
swiperHeight() {
|
||||
const fallback = Number(this.height) || 578
|
||||
return this.currentSwiperHeight || fallback
|
||||
},
|
||||
justifyContent() {
|
||||
if (this.indicatorPos == 'topLeft' || this.indicatorPos == 'bottomLeft') return 'flex-start';
|
||||
if (this.indicatorPos == 'topCenter' || this.indicatorPos == 'bottomCenter') return 'center';
|
||||
@ -230,7 +243,31 @@
|
||||
return Number(this.current);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.updateSwiperHeight(this.uCurrent);
|
||||
},
|
||||
methods: {
|
||||
getSlideHeightByItem(item = []) {
|
||||
const defaultHeight = Number(this.height) || 578;
|
||||
if (!item || item.length <= 1) {
|
||||
return defaultHeight;
|
||||
}
|
||||
const columns = 2;
|
||||
const rows = Math.ceil(item.length / columns);
|
||||
const multiHeight = rows * 276;
|
||||
return Math.max(multiHeight, defaultHeight);
|
||||
},
|
||||
updateSwiperHeight(index = 0) {
|
||||
let safeIndex = index;
|
||||
if (safeIndex >= this.list.length) {
|
||||
safeIndex = this.list.length - 1;
|
||||
}
|
||||
if (safeIndex < 0) {
|
||||
safeIndex = 0;
|
||||
}
|
||||
const slide = this.list[safeIndex] || [];
|
||||
this.currentSwiperHeight = this.getSlideHeightByItem(slide);
|
||||
},
|
||||
videoClick(row) {
|
||||
this.activeInfo = row;
|
||||
this.$emit('videoClick', row);
|
||||
@ -241,6 +278,8 @@
|
||||
change(e) {
|
||||
let current = e.detail.current;
|
||||
this.uCurrent = current;
|
||||
this.updateSwiperHeight(current);
|
||||
this.onPlayerClick(0)
|
||||
// 发出change事件,表示当前自动切换的index,从0开始
|
||||
this.$emit('change', current);
|
||||
},
|
||||
@ -252,12 +291,21 @@
|
||||
// #endif
|
||||
},
|
||||
onPlayerClick(eIndex) {
|
||||
const row = this.list[this.current][eIndex];
|
||||
const row = this.list[this.uCurrent][eIndex];
|
||||
console.log(row)
|
||||
this.videoClick(row);
|
||||
this.$nextTick(() => {
|
||||
this.$forceUpdate();
|
||||
})
|
||||
},
|
||||
retryClick(url) {
|
||||
// const row = this.list[this.uCurrent]
|
||||
console.log("触发了11", url)
|
||||
this.$emit('retryVideo', url, this.uCurrent)
|
||||
},
|
||||
stopVideo() {
|
||||
this.list[this.current].forEach((item, index) => {
|
||||
const video = this.$refs[`player-${index}`];
|
||||
this.list[this.uCurrent].forEach((item, index) => {
|
||||
const video = this.$refs[`player-${item.itemId}`];
|
||||
if (video) {
|
||||
console.log(video)
|
||||
video[0].emitPause();
|
||||
@ -271,37 +319,50 @@
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.player-main {
|
||||
width: 100%;
|
||||
width: calc(100%);
|
||||
/* #ifdef APP-PLUS */
|
||||
// 防止video组件在swiper中滑动时错位
|
||||
position: relative;
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
/* #endif */
|
||||
|
||||
.active_box {
|
||||
border: 2rpx solid rgb(255, 204, 0);
|
||||
position: relative;
|
||||
border: 2rpx solid rgb(255, 204, 0) !important;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.player-m3u8 {
|
||||
width: 100%;
|
||||
width: calc(100% - 2rpx);
|
||||
height: 578rpx;
|
||||
/* #ifdef APP-PLUS */
|
||||
// 防止video组件在swiper中滑动时错位
|
||||
position: relative;
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.player-m3u8_1 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: calc(100% - 2rpx);
|
||||
height: calc(100% - 2rpx);
|
||||
|
||||
/deep/ .uni-video-slots {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.maskleft {
|
||||
width: 30%;
|
||||
height: 80%;
|
||||
}
|
||||
// .maskleft {
|
||||
// width: 30%;
|
||||
// height: 80%;
|
||||
// }
|
||||
|
||||
.maskright {
|
||||
width: 30%;
|
||||
height: 80%;
|
||||
}
|
||||
// .maskright {
|
||||
// width: 30%;
|
||||
// height: 80%;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +371,7 @@
|
||||
flex-wrap: wrap;
|
||||
|
||||
.player-m3u8 {
|
||||
width: 50%;
|
||||
width: calc(50% - 2rpx);
|
||||
height: 276rpx;
|
||||
}
|
||||
}
|
||||
@ -415,6 +476,11 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* #ifdef APP-PLUS */
|
||||
// 防止video组件在swiper中滑动时错位
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.u-swiper-title {
|
||||
@ -432,5 +498,10 @@
|
||||
@include vue-flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
/* #ifdef APP-PLUS */
|
||||
// 防止video组件在swiper中滑动时错位
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
||||
2
main.js
2
main.js
@ -110,7 +110,7 @@ if (process.env.NODE_ENV === 'development') {
|
||||
// Vue.prototype.url_config = 'http://jxj.zhgdyun.com:1667/' // 测试环境h5
|
||||
// Vue.prototype.url_config = 'http://jxj.zhgdyun.com:21000/' // 潮州项目h5
|
||||
// Vue.prototype.url_config = 'http://gszhdz.crpower.com.cn:9809/' // 敦煌环境h5
|
||||
// Vue.prototype.url_config = 'https://gszhdz.crpower.com.cn:9807/' // 敦煌环境h5
|
||||
Vue.prototype.url_config = 'https://gszhdz.crpower.com.cn:9807/' // 敦煌环境h5
|
||||
// Vue.prototype.url_config = window.location.protocol + "//" + window.location.host + "/"
|
||||
|
||||
} else {
|
||||
|
||||
6619
pages.json
6619
pages.json
File diff suppressed because it is too large
Load Diff
@ -50,7 +50,7 @@
|
||||
<scroll-view scroll-y="true" class="content_main" :class="{'content_main2': pageType=='backend'}">
|
||||
<view class="main-box" v-if="workTicketList.length > 0">
|
||||
<u-collapse :accordion="false">
|
||||
<u-collapse-item :title="item.date" v-for="(item, index) in workTicketList" :key="item.id"
|
||||
<u-collapse-item :title="item.date" v-for="(item, index) in workTicketList" :key="item.date"
|
||||
:open="true">
|
||||
<scroll-view @scrolltolower="onScrollTolower(item)" class="collapse-scroll" scroll-y="true">
|
||||
<view class="item-box" @click="onNavigateToDetail(ele)"
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
searchsn: '',
|
||||
userInfo: null,
|
||||
mapData: [],
|
||||
headerName: '集团',
|
||||
headerName: '华润电力',
|
||||
pageType: 'video',
|
||||
treeIndex: 1, // 树形层级index
|
||||
};
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<view class="page-main">
|
||||
<headers :showBack="true">
|
||||
<view class="headerName">
|
||||
切换组织
|
||||
</view>
|
||||
</headers>
|
||||
<liu-folding-panel :dataList="deptList" @change="change"></liu-folding-panel>
|
||||
</view>
|
||||
</template>
|
||||
@ -26,7 +31,7 @@
|
||||
},
|
||||
method: "POST",
|
||||
success: (res) => {
|
||||
this.deptList = res.result;
|
||||
this.deptList = res.result ? res.result : [];
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<!-- background-color: #f6f6f6; -->
|
||||
<view style="height: 100%; background-color: #fff;">
|
||||
<view class="fixedheader" style="position: fixed; top: 0; left: 0; width: 100%; z-index: 2;">
|
||||
<headers :showBack="false">
|
||||
<headers :showBack="backType == 'backend' ? true : false">
|
||||
<view class="headerName">
|
||||
消息中心
|
||||
</view>
|
||||
@ -63,7 +63,8 @@
|
||||
checkedTab: 0,
|
||||
accountType: 1,
|
||||
searchName: '',
|
||||
centerContentList: ''
|
||||
centerContentList: '',
|
||||
backType: "",
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -79,9 +80,10 @@
|
||||
console.log('this.mobileTopHeight',this.mobileTopHeight)
|
||||
this.loadData()
|
||||
},
|
||||
onLoad() {
|
||||
onLoad(opts) {
|
||||
var userInfo = JSON.parse(uni.getStorageSync('userInfo'))
|
||||
this.accountType = userInfo.accountType
|
||||
this.accountType = userInfo.accountType;
|
||||
this.backType = opts.type;
|
||||
},
|
||||
methods: {
|
||||
//去指定页
|
||||
|
||||
@ -201,7 +201,7 @@
|
||||
},
|
||||
toMessageCenter() {
|
||||
uni.navigateTo({
|
||||
url: '../messageCenter/messageCenter'
|
||||
url: `../messageCenter/messageCenter?type=backend`
|
||||
})
|
||||
},
|
||||
toLegalTerms() {
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
</headers>
|
||||
</view>
|
||||
<view class="content" :style="{paddingTop: mobileTopHeight + 44 + 'px'}">
|
||||
<SwiperVideo v-if="!showDetail" ref="swiperVideoRef" @videoClick="videoClick" :height="800"
|
||||
:list="videoList" mode="number" :autoplay="false" />
|
||||
<SwiperVideo v-show="!showDetail" ref="swiperVideoRef" @videoClick="videoClick" :height="800"
|
||||
:list="videoList" mode="number" @retryVideo="onRetryClick" :autoplay="false" />
|
||||
<scroll-view scroll-y="true"
|
||||
:style="{height: `calc(100% - ${mobileTopHeight + 44}px - 120rpx - 52rpx)`, top: `${mobileTopHeight + 44}px`}"
|
||||
v-show="showDetail" class="main-box">
|
||||
@ -209,6 +209,29 @@
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
onRetryClick(url, index) {
|
||||
console.log("触发了", url, index);
|
||||
const find = this.videoList[index].find(item => item.url == url);
|
||||
if (find) {
|
||||
console.log("触发了", find);
|
||||
var that = this
|
||||
var json = {
|
||||
itemId: find.itemId,
|
||||
streamType: find.defaultStreamType,
|
||||
type: "rtsp",
|
||||
}
|
||||
this.sendRequest({
|
||||
url: "xmgl/policeCameraItem/getVideoItemInfo",
|
||||
data: {
|
||||
...json,
|
||||
},
|
||||
method: "post",
|
||||
success(res) {
|
||||
find.url = res.result.videoInfo ? res.result.videoInfo.url : "";
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onVideoStop() {
|
||||
this.$refs.swiperVideoRef.stopVideo();
|
||||
},
|
||||
@ -256,6 +279,7 @@
|
||||
success(res) {
|
||||
that.list.push({
|
||||
itemId: res.result.videoInfo ? res.result.videoInfo.itemId : "",
|
||||
defaultStreamType: row.defaultStreamType ? row.defaultStreamType : "",
|
||||
url: res.result.videoInfo ? res.result.videoInfo.url : "",
|
||||
});
|
||||
}
|
||||
@ -550,9 +574,10 @@
|
||||
background-color: #f7f7f7;
|
||||
color: #272d45 !important;
|
||||
}
|
||||
|
||||
.gfx_active {
|
||||
background-color: #ff0000;
|
||||
color: #ffffff;
|
||||
background-color: #ff0000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.box-item1>view:last-child {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user