vr-shopxo-uniapp/pages/plugins/video/detail/detail.vue

1774 lines
92 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view :class="theme_view">
<view v-if="display_video_list.length > 0" class="content pr">
<!-- 搜索框 -->
<view v-if="!show_comment_modal" class="header-top" :style="top_content_style + menu_button_info">
<view id="search-height" class="flex-row align-c">
<!-- 支付宝小程序自带返回按钮,这里就不给返回按钮了,这里给留出一点空间就行 -->
<!-- #ifndef MP-ALIPAY -->
<view class="cp" @tap="handle_back">
<iconfont name="icon-arrow-left " size="36rpx" color="#333" class="mr-10"></iconfont>
</view>
<!-- #endif -->
<view class="wh-auto ht-auto" :style="header_padding_left">
<component-search propIsDisabled @disabledSearch="handle_search" />
</view>
</view>
</view>
<!-- 视频列表 -->
<swiper class="swiper-container" :key="'top-or-buttom-' + swiper_key" :style="swiperStyle" :duration="500" :vertical="true" :circular="close_circular ? false : true" :skip-hidden-item-layout="true" :current="current_index" easing-function="linear" @transition="on_transition" @change="handle_swiper_change">
<swiper-item v-for="(video_item, index) in display_video_list" :key="video_item.id">
<view class="video-container pr" @tap.stop="toggle_play_pause" @touchstart="handle_swiper_touch_start" @touchmove="handle_swiper_touch_move" @touchend="handle_swiper_touch_end">
<view class="video-bg" :style="!isEmpty(video_item.cover) ? 'background-image: url(' + video_item.cover + ')' : ''"></view>
<video class="video" :src="video_item.video_url" :poster="video_item.cover" :id="`video_${index}`" :loop="true" :show-fullscreen-btn="false" :show-center-play-btn="false" :show-play-btn="false" :controls="false" :show-mute-btn="true" object-fit="contain" @timeupdate="handle_time_update" @play="handle_play"></video>
<view v-if="paused && current_index == index" class="play-icon">
<view class="pr">
<view class="play-icon-bg"></view>
<view class="pa z-i play-icon-iconfont">
<iconfont name="icon-bofang" size="120rpx" propClass="" color="#4F3E35"></iconfont>
</view>
</view>
</view>
<template v-if="!show_comment_modal">
<!-- Right Action Bar -->
<view class="right-actions">
<view v-if="base_config_data && base_config_data.is_video_give_thumbs && base_config_data.is_video_give_thumbs == 1" class="action-item" :data-id="video_item.id" @tap.stop="handle_like">
<iconfont name="icon-givealike" :color="video_item.is_give_thumbs == 0 ? '#fff' : '#F4B73F'" size="60rpx" />
<text class="action-text">{{ video_item.give_thumbs_count }}</text>
</view>
<view v-if="base_config_data && base_config_data.is_video_comments_show && base_config_data.is_video_comments_show == 1" class="action-item" :data-id="video_item.id" @tap.stop="handle_comment">
<iconfont name="icon-comment" color="#fff" size="60rpx" />
<text class="action-text">{{ video_item.comments_count }}</text>
</view>
<view class="action-item" @tap.stop="handle_share">
<iconfont name="icon-share-solid" color="#fff" size="60rpx"></iconfont>
<text class="action-text">{{$t('common.share')}}</text>
</view>
</view>
<view v-if="!isEmpty(video_item.goods) && base_config_data && base_config_data.is_video_detail_show_goods && base_config_data.is_video_detail_show_goods == 1" class="product-card">
<view class="flex-col gap-10">
<view v-if="video_item.show_goods" class="flex-row align-c gap-10 product-card-item" :data-id="video_item.id" @tap.stop="handle_product_card_item">
<view class="product-image">
<image :src="video_item.goods.images" mode="aspectFill" class="product-image"></image>
</view>
<view class="flex-1 flex-col align-sb jc-c gap-10">
<text class="product-name text-line-1">{{ video_item.goods.title }}</text>
<text class="product-price">¥{{ video_item.goods.price }}</text>
</view>
<view class="product-close" :data-id="video_item.id" @tap.stop="product_close_event">
<iconfont name="icon-close" color="#999" size="30rpx"></iconfont>
</view>
</view>
<view class="product-button" :data-id="video_item.id" @tap.stop="handle_product_button">
<view class="product-button-left flex-row align-c gap-10">
<iconfont name="icon-cart-have" color="#F5C366" size="30rpx"></iconfont>
<text class="size-14 cr-f">{{$t('common.buy')}} {{$t('common.goods')}}</text>
</view>
<iconfont name="icon-angle-right" color="#fff" size="30rpx"></iconfont>
</view>
</view>
</view>
<!-- Progress Bar -->
<view class="progress-bar-container" v-if="current_index == index">
<slider class="progress-slider" :value="current_video_progress" :max="current_video_duration" @change.stop="handle_slider_change" @changing="handle_slider_changing" @tap.stop="handle_slider_change" block-size="14" activeColor="#FFFFFF" backgroundColor="rgba(255, 255, 255, 0.4)" />
<text class="time-display">{{ format_time(current_video_progress) }} / {{ format_time(current_video_duration) }}</text>
</view>
</template>
</view>
</swiper-item>
</swiper>
</view>
<template v-else>
<component-no-data :propStatus="data_list_loding_status" :propMsg="data_list_loding_msg"></component-no-data>
</template>
<!-- 评论弹窗 -->
<view v-if="show_comment_modal" class="comment-modal" @tap="close_comment_modal">
<view class="comment-content bottom-line-exclude-bottom" @tap.stop :style="commentContentStyle">
<view class="comment-header" data-type="header" @tap.stop @touchstart="handle_comment_touch_start" @touchmove="handle_comment_touch_move" @touchend="handle_comment_touch_end">
<text class="comment-count">{{$t('common.comment')}}</text>
<view class="close-btn" @tap="close_comment_modal">✕</view>
</view>
<view class="flex-1 flex-row oh" data-type="scroll" @tap.stop @touchstart="handle_comment_touch_start" @touchmove="handle_comment_touch_move" @touchend="handle_comment_touch_end">
<!-- 评论内容区域 -->
<scroll-view class="comment-list" scroll-y :scroll-top="comment_scroll_top" show-scrollbar="false" scroll-with-animation @scrolltolower="handle_comment_to_lower_scroll" @scroll="handle_comment_scroll">
<view class="comment-scroll">
<template v-if="active_comments.length > 0">
<view class="comment-item flex-col" v-for="(comment_item, index) in active_comments" :key="index">
<commentInfoComponent class="wh-auto ht-auto" :propComment="comment_item" :propId="comment_item.id" :propDropDownVisible="active_dropdown_id == comment_item.id" @comment_reply="comment_reply" @comment_like="comment_like" @toggle_dropdown="handle_toggle_dropdown" @dropdown_item_click="handle_dropdown_item_click"></commentInfoComponent>
<!-- 子评论 -->
<view class="sub-comment flex-col jc-c gap-10 mt-10">
<view v-if="comment_item.sub_comments && comment_item.sub_comments.length > 0 && comment_item.show_sub_comment" class="sub-comment-list flex-col jc-c">
<view class="sub-comment-item flex-row align-s gap-10" v-for="(sub_comment_item, sub_comment_index) in comment_item.sub_comments" :key="sub_comment_index">
<commentInfoComponent class="wh-auto ht-auto" :propComment="sub_comment_item" :propId="sub_comment_item.id" :propDropDownVisible="active_dropdown_id == sub_comment_item.id" @comment_reply="comment_reply" @comment_like="comment_like" @toggle_dropdown="handle_toggle_dropdown" @dropdown_item_click="handle_dropdown_item_click"></commentInfoComponent>
</view>
</view>
<template v-if="comment_item.comments_count > 0">
<template v-if="!comment_item.show_sub_comment">
<commentMoreComponent :propId="comment_item.id" :propIsLevel="1" :propText="'—— '+ $t('common.expand') + (comment_item.comments_count ? comment_item.comments_count || 0 : 0) + $t('ask-comments.ask-comments.ymmd24')" @comment_more_event="open_sub_comment"></commentMoreComponent>
</template>
<template v-else>
<template v-if="comment_item.show_sub_comment_loading">
<loading-component></loading-component>
</template>
<view v-else class="sub-comment-more flex-row align-c gap-10">
<template v-if="comment_item.page != null && comment_item.page < comment_item.page_total">
<commentMoreComponent :propId="comment_item.id" :propIsLevel="2" :propText="$t('common.expand')" @comment_more_event="open_sub_comment"></commentMoreComponent>
</template>
<commentMoreComponent :propId="comment_item.id" :propText="$t('common.retract')" propIconName="arrow-top" @comment_more_event="close_sub_comment"></commentMoreComponent>
</view>
</template>
</template>
</view>
</view>
<template v-if="comment_item_loading">
<component-loading></component-loading>
</template>
<template v-else>
<!-- 结尾 -->
<component-bottom-line :propStatus="goods_bottom_line_status"></component-bottom-line>
</template>
</template>
<template v-else>
<component-no-data :propMsg="$t('common.no_data')"></component-no-data>
</template>
</view>
</scroll-view>
</view>
<view v-if="base_config_data && base_config_data.is_video_comments_add && base_config_data.is_video_comments_add == 1" class="comment-input-container">
<view class="flex-col align-c gap-4">
<view v-if="!isEmpty(comments_reply_data)" class="comment-reply-content flex-row align-c jc-sb gap-10 wh-auto">
<text class="size-12 cr-f text-line-1">@{{ comments_reply_data.user.user_name_view }}:{{ comments_reply_data.content }}</text>
<view data-type="image" @tap="comment_data_delete">
<iconfont name="icon-close-line" size="24rpx" color="#fff"></iconfont>
</view>
</view>
<view class="flex-row align-s gap-10 wh-auto ht-auto">
<view class="flex-1 comment-input-content flex-col jc-c">
<view class="flex-row align-s gap-10 wh-auto ht-auto pr-16 box-border-box">
<!-- #ifdef H5 -->
<textarea :value="comment_input_value" class="comment-input wh-auto cr-black" placeholder-class="cr-grey" auto-height :show-confirm-bar="false" :maxlength="500" :adjust-position="false" :placeholder="input_placeholder" @focus="add_comment" @input="comment_input_event" />
<!-- #endif -->
<!-- #ifndef H5 -->
<view :class="'comment-input text-line-1 ' + (isEmpty(comment_input_value) ? 'cr-grey' : 'cr-black')" @tap="add_comment">{{ isEmpty(comment_input_value) ? input_placeholder : comment_input_value }}</view>
<!-- #endif -->
<view class="pt-8">
<component-upload :propMaxNum="propMaxNum" :propPathType="editor_path_type" propSlot propSingleCall propIsAllInfo @call-back="upload_images_event">
<iconfont name="icon-layout-module-single-images" size="40rpx" color="#999"></iconfont>
</component-upload>
</view>
</view>
<view v-if="form_images_list.length > 0" class="pr w h comment-input-img-container">
<view v-for="(item, index) in form_images_list" :key="index" class="comment-input-img pr">
<iconfont name="icon-close" size="10" color="#000" class="comment-input-img-close" :data-index="index" @tap="comment_input_img_close"></iconfont>
<image :src="item.url" :data-index="index" @tap="upload_show_event" mode="aspectFill" class="wh-auto ht-auto"></image>
</view>
</view>
</view>
<!-- #ifdef H5 -->
<view class="pt-4 flex-row align-c">
<button :disabled="isEmpty(comment_input_value)" size="mini" type="default" class="bg-main margin-0 cr-white" @tap="send_comment">{{$t('common.send')}}</button>
</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view v-if="!isEmpty(comment_input_value)" class="pt-4 flex-row align-c">
<button size="mini" type="default" class="margin-0 bg-main cr-white" @tap="send_comment">{{$t('common.send')}}</button>
</view>
<!-- #endif -->
</view>
</view>
</view>
</view>
</view>
<!-- 举报弹窗 -->
<component-popup :propShow="popup_report_status" propPosition="bottom" @onclose="popup_report_close_event">
<view class="report-content">
<!-- 顶部按钮区域 -->
<view class="report-header flex-row align-c jc-sb">
<view class="report-btn cr-6" @tap="popup_report_close_event">{{$t('common.cancel')}}</view>
<view class="report-title flex-1">{{$t('video-detail.video-detail.rfsdfg')}}</view>
<view class="report-btn cr-blue" @tap="submit_report">{{$t('common.confirm')}}</view>
</view>
<!-- 主要内容区域 -->
<view class="report-body">
<!-- 第一层:举报原因选择 -->
<view v-if="report_type_list.length > 0" class="report-section">
<view class="report-label">{{$t('video-detail.video-detail.rfsdfg')}}<text class="ml-10">*</text></view>
<view class="flex-row align-c gap-10 flex-wrap">
<view v-for="(mainItem, main_index) in report_type_list" :key="main_index" class="flex-row align-c" :data-index="main_index" @tap="select_main_reason">
<view class="flex-row align-c">
<radio :value="main_index.toString()" :checked="current_main_index === main_index" style="transform:scale(0.7)" />
<view class="flex-row align-c">{{mainItem.name}}</view>
</view>
</view>
</view>
</view>
<!-- 第二层:具体类型选择(当有主类别选中时显示) -->
<view class="report-section mt-20" v-if="current_main_index >= 0 && report_type_list[current_main_index]">
<view class="report-label">{{$t('video-detail.video-detail.fsdf33')}}<text class="ml-10">*</text></view>
<view class="flex-row align-c gap-10 flex-wrap">
<view v-for="(subItem, sub_index) in report_type_list[current_main_index].data" :key="sub_index" class="flex-row align-c" :data-index="sub_index" @tap="select_sub_reason">
<view class="flex-row align-c">
<radio :value="sub_index.toString()" :checked="current_sub_index === sub_index" style="transform:scale(0.7)" />
<view class="flex-row align-c">{{subItem}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
</component-popup>
<!-- 添加评论弹出框 -->
<view v-if="is_add_comment" class="keyboard-input br-top-shadow" :style="'width:100%;bottom:' + listener_height + 'px;'">
<view class="flex-col gap-10">
<view class="comment-input-content flex-col jc-c">
<view v-if="!isEmpty(comments_reply_data)" class="comment-reply-content flex-row align-c jc-sb gap-10">
<text class="size-12 cr-f text-line-1">@{{ comments_reply_data.user.user_name_view }}:{{ comments_reply_data.content }}</text>
<view data-type="image" @tap="comment_data_delete">
<iconfont name="icon-close-line" size="24rpx" color="#fff"></iconfont>
</view>
</view>
<view class="flex-row align-c gap-10 wh-auto ht-auto pr-16 box-border-box">
<textarea ref="commentInput" :value="comment_input_value" :focus="is_add_comment" class="comment-input wh-auto cr-black" placeholder-class="cr-grey" auto-height :show-confirm-bar="false" :maxlength="500" :adjust-position="false" :auto-blur="true" :placeholder="input_placeholder" @input="comment_input_event" @blur="() => is_add_comment = false" @confirm="send_comment" />
</view>
<view v-if="form_images_list.length > 0" class="pr w h comment-input-img-container">
<view v-for="(item, index) in form_images_list" :key="index" class="comment-input-img pr">
<iconfont name="icon-close" size="10" color="#000" class="comment-input-img-close" :data-index="index" @tap="comment_input_img_close"></iconfont>
<image :src="item.url" :data-index="index" @tap="upload_show_event" mode="aspectFill" class="wh-auto ht-auto"></image>
</view>
</view>
</view>
<view class="flex-row align-c jc-sb wh-auto">
<component-upload :propMaxNum="propMaxNum" :propPathType="editor_path_type" propSlot propSingleCall propIsAllInfo propChooseFocus @call-back="upload_images_event" @chooseFocus="upload_event">
<iconfont name="icon-layout-module-single-images" size="40rpx" color="#999"></iconfont>
</component-upload>
<button :disabled="isEmpty(comment_input_value)" size="mini" type="primary" class="margin-0 bg-main cr-white" @tap="send_comment">{{$t('common.send')}}</button>
</view>
</view>
</view>
<!-- 分享弹窗 -->
<component-share-popup ref="share"></component-share-popup>
<!-- 公共 -->
<component-common ref="common"></component-common>
</view>
</template>
<script>
const app = getApp();
import { get_math, isEmpty, video_get_top_left_padding } from '@/common/js/common/common.js';
import commentInfoComponent from '@/pages/plugins/video/components/comment-info.vue';
import componentLoading from '@/pages/plugins/video/components/loading.vue';
import commentMoreComponent from '@/pages/plugins/video/components/comment-more.vue';
import componentSearch from '@/pages/plugins/video/components/search.vue';
import componentSharePopup from '@/components/share-popup/share-popup';
import componentNoData from '@/components/no-data/no-data';
import componentBottomLine from '@/components/bottom-line/bottom-line';
import componentPopup from '@/components/popup/popup';
import componentUpload from '@/components/upload/upload';
import componentCommon from '@/components/common/common';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO || H5
bar_height = 0;
// #endif
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
// 5,7,0 是误差,, 10 是下边距66是高度bar_height是不同小程序下的导航栏距离顶部的高度
// #ifdef MP
top_content_style: 'padding-top:' + (bar_height + 5) + 'px;padding-bottom:10px;',
// #endif
// #ifdef H5 || MP-TOUTIAO
top_content_style: 'padding-top:' + (bar_height + 7) + 'px;padding-bottom:10px;',
// #endif
// #ifdef APP
top_content_style: 'padding-top:' + bar_height + 'px;padding-bottom:10px;',
// #endif
data_list_loding_status: 1,
data_list_loding_msg: '',
video_data_list: [],
display_video_list: [],
current_index: 0,
video_contexts: [], // 原生的video视频
create_video_contexts: [], // 使用uni.createVideoContext创建的视频上下文
paused: false,
current_video_progress: 0,
current_video_duration: 1,
is_seeking: false,
show_comment_modal: false,
active_comments: {},
comments_page: 1, // 当前评论页
comments_page_total: 5, // 评论总页数
goods_bottom_line_status: false, //评论页是否显示底部线
comment_item_loading: false,
comment_start_y: 0, // 评论开始拖拽位置
comment_current_y: 0, // 评论当前拖拽位置
move_distance: 0, // 评论拖拽距离
current_video_id: '', // 当前播放视频的ID
is_slide_start: false,
swiper_key: get_math(),
comment_scroll_top: 0, // 评论滚动距离顶部的距离
input_placeholder: this.$t('video-detail.video-detail.98yyuf'),
comment_input_value: '',
propMaxNum: 1,
form_images_list: [],
share_info: {},
menu_button_info: '',
direction: 'direction',
base_config_data: {},
video_switch_debounce_timer: null, // 视频切换防抖定时器
video_cleanup_timer: null, // 视频清理定时器
comment_scroll_debounce_timer: null, // 评论滚动防抖定时器
// 添加下拉菜单状态管理
active_dropdown_id: null, // 当前显示下拉菜单的评论ID
params: {},
header_padding_left: '',
report_type_list: [], // 举报类型列表
popup_report_status: false, // 举报弹窗状态
current_main_index: 0, // 默认选中第一个举报原因
current_sub_index: 0, // 默认选中第一个具体类型
report_comment_id: '', // 举报的评论id
comment_value: '',
is_add_comment: false,
// 监听键盘高度变化事件
listener_height: 0,
comments_reply_data: {},
editor_path_type: 'video',
is_manual_pause: false, // 是否手动暂停
// 视频滚动
swiper_start_y: 0,
swiper_current_y: 0,
swiper_move_distance: 0,
swiper_move_throttle_timer: null,
};
},
components: {
commentInfoComponent,
commentMoreComponent,
componentSearch,
componentSharePopup,
componentNoData,
componentBottomLine,
componentPopup,
componentLoading,
componentUpload,
componentCommon
},
computed: {
// 视频列表高度
swiperStyle() {
return this.show_comment_modal ? (this.move_distance > 0 ? `height: calc(30% + ${this.move_distance}px);` : 'height: 30%;') : 'height: 100%;';
},
// 评论内容区域高度
commentContentStyle() {
return this.show_comment_modal && this.move_distance > 0 ? `transform: translateY(3px); height: calc(70% + 20rpx - ${this.move_distance}px);` : `transform: translateY(0); height: calc(70% + 20rpx);`;
},
// 当前播放视频的索引
current_video_index() {
return this.video_data_list.findIndex(item => item.id == this.current_video_id);
},
// 如果是第一个或者最后一个的情况下,取消无限轮播
close_circular() {
if (this.video_data_list.length > 0) {
return this.video_data_list[0].id == this.current_video_id || this.video_data_list[this.video_data_list.length - 1].id == this.current_video_id;
} else {
return true
}
}
},
onLoad(params) {
// 调用公共事件方法
app.globalData.page_event_onload_handle(params);
// 设置参数
this.setData({
params: app.globalData.launch_params_handle(params),
});
},
onShow() {
// 调用公共事件方法
app.globalData.page_event_onshow_handle();
console.log(this.is_manual_pause);
// 视频播放
if (!this.is_manual_pause && this.create_video_contexts[this.current_index]) {
this.video_play_event(this.create_video_contexts[this.current_index]);
}
// 公共onshow事件
if ((this.$refs.common || null) != null) {
this.$refs.common.on_show();
}
// 分享菜单处理
app.globalData.page_share_handle();
},
onHide() {
// 清理定时器
if (this.video_switch_debounce_timer) {
clearTimeout(this.video_switch_debounce_timer);
}
if (this.video_cleanup_timer) {
clearTimeout(this.video_cleanup_timer);
}
// 清理所有视频资源
this.cleanup_all_videos();
},
mounted() {
// 初始化
this.init();
// #ifdef H5
// 添加全局点击事件监听
document.addEventListener('click', this.handle_global_click);
// 添加触摸事件监听(移动端兼容)
document.addEventListener('touchstart', this.handle_global_click);
//#endif
// 创建监听事件
this.bind_keyboard_listener();
},
beforeDestroy() {
// 清理定时器
if (this.video_switch_debounce_timer) {
clearTimeout(this.video_switch_debounce_timer);
}
if (this.video_cleanup_timer) {
clearTimeout(this.video_cleanup_timer);
}
if (this.comment_scroll_debounce_timer) {
clearTimeout(this.comment_scroll_debounce_timer);
}
// 清理所有视频资源
this.cleanup_all_videos();
// 移除键盘事件监听器
// #ifdef H5
if (typeof document !== 'undefined') {
document.removeEventListener('keydown', this.handle_keydown);
}
// 移除全局事件监听器
document.removeEventListener('click', this.handle_global_click);
document.removeEventListener('touchstart', this.handle_global_click);
// #endif
this.unbind_keyboard_listener();
},
methods: {
isEmpty,
init() {
// 小程序下,获取小程序胶囊的宽度
let menu_button_info = 'max-width:100%';
// #ifndef MP-TOUTIAO
// #ifdef MP
// 判断是否有胶囊
const is_current_single_page = app.globalData.is_current_single_page();
// 如果有胶囊的时候,做处理
if (is_current_single_page == 0) {
const custom = uni.getMenuButtonBoundingClientRect();
menu_button_info = `max-width:calc(100% - ${custom.width + 10}px);`;
}
// #endif
// #endif
// 视频详情页需要添加padding-left
let padding_left = '';
// #ifdef MP-ALIPAY
padding_left = video_get_top_left_padding();
// #endif
this.setData({
header_padding_left: padding_left,
menu_button_info: menu_button_info,
current_video_id: isEmpty(this.current_video_id) ? this.params.id : this.current_video_id,
});
this.get_video_detail(this.current_video_id);
},
/*
* 获取视频详情
* @param {*} id 视频id
*/
get_video_detail(id) {
// 获取数据
uni.request({
url: app.globalData.get_request_url("detail", "index", "video"),
method: 'POST',
data: {
id: id
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
this.setData({
data_list_loding_status: 3,
video_data_list: [new_data.data],
report_type_list: new_data.report_type_list,
base_config_data: new_data.base_config_data,
editor_path_type: new_data.editor_path_type,
});
this.get_last_or_next_data_list(this.params.id, 1, 1);
} else {
this.setData({
data_list_loding_status: 0,
data_tabs_loding_msg: data.msg,
});
}
},
fail: (err) => {
this.setData({
data_list_loding_status: 2,
data_list_loding_msg: this.$t('common.internet_error_tips'),
});
}
});
},
/*
* 获取视频列表
* @param {*} id 视频id
* @param {*} is_last 是否获取上一批数据
* @param {*} is_next 是否获取下一批数据
*/
get_last_or_next_data_list(id, is_last = 0, is_next = 0) {
// 获取数据
uni.request({
url: app.globalData.get_request_url("lastnextdata", "index", "video"),
method: 'POST',
data: {
id: id,
is_last: is_last,
is_next: is_next,
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
// 第一次的数据
// let data_list = JSON.parse(JSON.stringify(this.video_data_list));
// 创建现有数据的ID映射表用于快速去重
const existing_ids = new Map();
this.video_data_list.forEach(item => {
existing_ids.set(item.id, true);
});
if (is_last == 1 && is_next == 1) {
// 上一批数据 - 去重处理
if (new_data.last.length > 0) {
const unique_last = new_data.last.filter(item => !existing_ids.has(item.id));
if (unique_last.length > 0) {
this.video_data_list.unshift(...unique_last);
// 更新ID映射表
unique_last.forEach(item => existing_ids.set(item.id, true));
}
}
// 下一批数据 - 去重处理
if (new_data.next.length > 0) {
const unique_next = new_data.next.filter(item => !existing_ids.has(item.id));
if (unique_next.length > 0) {
this.video_data_list.push(...unique_next);
}
}
} else if (is_last == 1 && new_data.last.length > 0) { // 上一页数据 - 去重处理
const unique_last = new_data.last.filter(item => !existing_ids.has(item.id));
if (unique_last.length > 0) {
this.video_data_list.unshift(...unique_last);
}
} else if (is_next == 1 && new_data.next.length > 0) { // 下一页数据 - 去重处理
const unique_next = new_data.next.filter(item => !existing_ids.has(item.id));
if (unique_next.length > 0) {
this.video_data_list.push(...unique_next);
}
}
// 更新当前视频商品信息
const new_index = this.video_data_list.findIndex(item => item.id == this.params.id);
// 处理当前视频商品信息
this.video_data_list.forEach((item) => {
if (isEmpty(item.show_goods)) {
if (this.base_config_data && this.base_config_data.is_video_detail_show_goods_modal && this.base_config_data.is_video_detail_show_goods_modal == 1) {
item.show_goods = true;
} else {
item.show_goods = false;
}
}
});
// 更新所有视频信息
this.setData({
video_data_list: this.video_data_list,
// 逻辑说明:当是最后一个视频且需要播放下一个时,根据数组长度和新索引计算新的当前索引
// - 数组长度 > 2 时:新索引是最后一个元素则返回 2是倒数第二个则返回 1否则返回 0
// - 数组长度 <= 2 时:返回 length - 1
current_index: is_last == 1 && is_next == 1 ? this.calculate_new_index(this.video_data_list.length, new_index) : this.current_index,
});
if (is_last == 1 && is_next == 1) {
// 更新显示数据数据信息
this.update_display_data();
setTimeout(() => {
// 更新分享信息
this.update_share_info(this.display_video_list[this.current_index]);
this.display_video_list.forEach((item, index) => {
this.create_video_contexts[index] = uni.createVideoContext(`video_${index}`, this);
//#ifdef H5
if (document.getElementById(`video_${index}`) != null) {
this.video_contexts[index] = document.getElementById(`video_${index}`).querySelector('video');
}
//#endif
});
setTimeout(() => {
//#ifdef H5
if (this.video_contexts[this.current_index]) { // 当前播放的视频索引为0
this.video_play_event(this.video_contexts[this.current_index], true);
}
//#endif
//#ifndef H5
if (this.create_video_contexts[this.current_index]) { // 当前播放的视频索引为0
this.video_play_event(this.create_video_contexts[this.current_index], true);
}
//#endif
}, 200);
}, 0);
}
}
}
});
},
/**
* 计算新的视频索引
* 当是最后一个视频且需要播放下一个时,根据数组长度和新索引计算新的当前索引
* @param {number} listLength - 视频列表长度
* @param {number} newIndex - 新视频在列表中的索引
* @returns {number} 计算后的新索引
*/
calculate_new_index(listLength, newIndex) {
// 边界处理:空数组
if (listLength === 0) {
return 0;
}
// 数组长度 <= 2 时,返回最后一个索引
if (listLength <= 2) {
return listLength - 1;
}
// 数组长度 > 2 时,根据新索引位置返回对应值
// 新索引是最后一个元素 -> 返回 2
if (newIndex === listLength - 1) {
return 2;
}
// 新索引是倒数第二个元素 -> 返回 1
if (newIndex === listLength - 2) {
return 1;
}
// 其他情况 -> 返回 0
return 0;
},
// 视频滚动处理逻辑(带防抖)
handle_swiper_change(event) {
const { current } = event.detail;
// 防抖处理,避免快速切换时的重复操作
if (this.video_switch_debounce_timer) {
clearTimeout(this.video_switch_debounce_timer);
}
this.video_switch_debounce_timer = setTimeout(() => {
this.process_swiper_change(current);
}, 50); // 50ms防抖延迟
},
// 实际的swiper切换处理逻辑
process_swiper_change(current) {
// 先暂停所有视频,确保不会有后台播放
this.pause_all_videos_except(current);
const id = this.display_video_list[current]?.id || '';
// 更新状态
this.setData({
current_index: current,
paused: false,
is_manual_pause: false,
current_video_progress: 0,
current_video_duration: 1,
is_seeking: false,
current_video_id: id, // 更新当前播放视频的ID
});
//#ifdef H5
// 使用URLSearchParams处理当前查询参数
const url = new URL(location.href);
url.searchParams.set('id', id);
// 替换URL路径保持查询参数不变
const pathname = location.href.split('?')[0];
history.replaceState(null, '', pathname + url.search);
//#endif
const index = this.video_data_list.findIndex(item => item.id == id);
// 数据预加载逻辑
if (index < 2 && this.direction == 'prev') {
this.get_last_or_next_data_list(this.video_data_list[0].id, 1, 0);
} else if (index < this.video_data_list.length - 3 && this.direction == 'next') {
this.get_last_or_next_data_list(this.video_data_list[this.video_data_list.length - 1].id, 0, 1);
}
// 获取视频详细信息
this.get_video_data_detail(id);
// 边界处理逻辑
if (this.current_video_index == 0 && this.is_slide_start) {
const list = this.update_video_list([0, 1, 2]);
this.setData({
is_slide_start: false,
current_index: 0,
display_video_list: list,
swiper_key: get_math()
});
} else if (this.current_video_index == this.video_data_list.length - 1) {
const list = this.update_video_list([-2, -1, 0]);
this.setData({
current_index: list.length - 1,
display_video_list: list,
swiper_key: get_math()
});
} else {
this.setData({
is_slide_start: true,
swiper_key: get_math()
});
this.update_display_data();
}
// 更新分享信息
this.update_share_info(this.display_video_list[current]);
// 延迟播放当前视频确保DOM更新完成
setTimeout(() => {
this.play_current_video_safely(this.current_index);
}, 150);
},
update_video_list(offsets) {
let list = [];
for (let i = 0; i < offsets.length; i++) {
const targetIndex = this.current_video_index + offsets[i];
if (targetIndex >= 0 && targetIndex < this.video_data_list.length) {
list.push(this.get_video_by_index(targetIndex));
}
}
return list;
},
// 批量暂停除指定索引外的所有视频
pause_all_videos_except(exceptIndex) {
// 暂停 uni.createVideoContext 创建的视频
this.create_video_contexts.forEach((context, index) => {
if (index !== exceptIndex && context) {
try {
context.pause();
} catch (error) {
console.warn(`暂停视频 ${index} 失败:`, error);
}
}
});
},
// 安全播放当前视频
play_current_video_safely(index) {
// 优先使用 uni.createVideoContext
if (this.create_video_contexts[index]) {
this.video_play_event(this.create_video_contexts[index]);
return;
}
},
// 切换播放暂停
toggle_play_pause() {
const currentIndex = this.current_index;
// 检查视频上下文是否存在
const videoContext = this.create_video_contexts[currentIndex] || this.video_contexts[currentIndex];
if (!videoContext) {
console.warn(`当前索引 ${currentIndex} 无可用视频上下文`);
return;
}
this.setData({
paused: !this.paused,
is_manual_pause: !this.paused,
});
if (this.paused) {
// 暂停当前视频
try {
videoContext.pause();
} catch (error) {
console.warn('暂停视频失败:', error);
}
} else {
// 播放当前视频
this.video_play_event(videoContext);
}
},
// 视频拖拽开始
handle_swiper_touch_start(e) {
try {
// 如果是滚动区域内滚动到顶部才可以拖拽,如果是头部拖拽的话,一直都可以
this.swiper_start_y = e?.touches[0]?.pageY || 0;
this.swiper_current_y = this.swiper_start_y;
this.swiper_move_distance = 0;
} catch (error) {
console.error('handle_swiper_touch_start error:', error);
}
},
// 视频拖拽中
handle_swiper_touch_move(e) {
try {
const current_y = e?.touches[0]?.pageY || 0;
const distance = current_y - this.swiper_start_y;
// 只有向下移动且距离超过阈值10px才开始拖拽避免误触和抖动
if (Math.abs(distance) > 10) {
this.swiper_current_y = current_y;
// 使用节流控制 move_distance 的更新频率,避免计算属性频繁触发导致抖动
if (this.swiper_move_throttle_timer) {
return;
}
this.move_distance = distance;
// 设置节流定时器16ms 约等于 60fps保证流畅度同时避免过度更新
this.swiper_move_throttle_timer = setTimeout(() => {
this.swiper_move_throttle_timer = null;
}, 80);
}
} catch(error) {
console.error('handle_swiper_touch_move error:', error);
}
},
// 视频拖拽结束
handle_swiper_touch_end(e) {
try {
const move_distance = this.swiper_current_y - this.swiper_start_y;
// 只有滑动距离超过屏幕高度的 15% 才触发切换
if (move_distance > 0) {
// 向下滑动,切换到上一个
if (this.current_video_index <= 0) {
app.globalData.showToast('已经是第一个视频了');
}
} else {
// 向上滑动,切换到下一个
if (this.current_video_index >= this.video_data_list.length - 1) {
app.globalData.showToast('已经是最后一个视频了');
}
}
// 清理节流定时器
if (this.swiper_move_throttle_timer) {
clearTimeout(this.swiper_move_throttle_timer);
this.swiper_move_throttle_timer = null;
}
// 重置拖拽状态
this.swiper_start_y = 0;
this.swiper_current_y = 0;
} catch (error) {
console.error('handle_swiper_touch_end error:', error);
}
},
// 更新分享信息
update_share_info(data) {
const info = {
title: data.title || '',
desc: data.desc || '',
path: '/pages/plugins/video/detail/detail',
query: 'id=' + this.current_video_id,
img: data.cover || ''
}
this.setData({
share_info: info,
});
// 分享菜单处理
app.globalData.page_share_handle(info);
// 更新页面标题
uni.setNavigationBarTitle({title: data.title});
},
// 安全的视频播放事件处理
video_play_event(videoContext, is_first_play = false) {
if (!videoContext) {
this.setData({ paused: true, is_manual_pause: false });
return;
}
try {
if (is_first_play) {
//#ifdef H5
videoContext.play().catch((error) => {
this.setData({ paused: true, is_manual_pause: false });
});
//#endif
//#ifndef H5
videoContext.play();
//#endif
} else {
videoContext.play();
}
} catch (error) {
console.error('视频播放异常:', error);
this.setData({ paused: true, is_manual_pause: false });
}
},
// 安全获取视频数据的方法,处理索引超限情况
get_video_by_index(index) {
// 处理负数索引
if (index < 0) {
// 循环到数组末尾
const actualIndex = this.video_data_list.length + (index % this.video_data_list.length);
return this.video_data_list[actualIndex];
}
// 处理超出数组长度的索引
if (index >= this.video_data_list.length) {
// 循环到数组开头
const actualIndex = index % this.video_data_list.length;
return this.video_data_list[actualIndex];
}
// 正常情况直接返回
return this.video_data_list[index];
},
/*
* 更新显示的视频数据
*/
update_display_data() {
let list = [];
// 如果当前索引为0只显示当前元素和下一个元素
if (this.current_index == 0) {
if (this.current_video_index == 0) {
list = this.update_video_list([0, 1, 2]);
} else {
list = this.update_video_list([0, 1, -1]);
}
} else if (this.current_index == 1) { // 索引为1时为确保无限轮播正常需要改变数据插入顺序
list = this.update_video_list([-1, 0, 1]);
} else {
if (this.current_video_index == this.video_data_list.length - 1) {
list = this.update_video_list([-2, -1, 0]);
} else {
list = this.update_video_list([1, -1, 0]);
}
}
this.setData({
display_video_list: list
})
},
// 评论输入框事件
comment_input_event(e) {
this.comment_input_value = e.detail.value;
},
// 图片上传回调
upload_images_event(res) {
if((res || null) != null) {
if (this.form_images_list.length > 0) {
this.form_images_list.splice(0, 1, { url: res.url, name: res.name, size: res.size });
} else {
// 存储上传图片内容
this.form_images_list.push({
url: res.url,
name: res.name,
size: res.size,
});
}
}
},
upload_event() {
setTimeout(() => {
this.is_add_comment = true;
// this.$refs.commentInput.focus();
}, 100);
},
// 上传图片预览
upload_show_event(e) {
uni.previewImage({
current: this.form_images_list[e.currentTarget.dataset.index].url,
urls: this.form_images_list.map(item => item.url),
});
},
// 评论输入图片删除
comment_input_img_close(e) {
const index = e?.currentTarget?.dataset?.index || 0;
var list = this.form_images_list;
list.splice(index, 1);
this.setData({
form_images_list: list,
});
},
// swiper-item 的位置发生改变时
on_transition(e) {
const dy = e.detail.dy;
let status = 'direction';
if (dy > 0) {
status = 'next';
} else if (dy < 0) {
status = 'prev';
}
// 如果历史的是向下滑动,这次也是向下滑动,就不更新数据
if (this.direction != status) {
this.setData({
direction: status,
})
}
},
// 播放
handle_play() {
this.setData({ paused: false, is_manual_pause: false });
},
// 收藏
handle_like(e) {
if (!app.globalData.is_single_page_check()) {
return false;
}
var user = app.globalData.get_user_info(this, 'handle_like', e);
if (user != false) {
const id = e?.currentTarget?.dataset?.id || '';
this.set_givethumbs_num(id);
}
},
// 打开评论区
handle_comment(e) {
const id = e?.currentTarget?.dataset?.id || '';
const old_data = this.video_data_list.find(item => item.id == id);
// 初始化评论数据
const new_data = old_data.comments_list.map(item1 => ({
...item1,
show_sub_comment: false,
show_sub_comment_loading: false,
page: 0,
sub_comments: [],
}));
this.setData({
active_comments: new_data,
comments_page: 1,
comments_page_total: 5,
comment_item_loading: false,
show_comment_modal: true,
move_distance: 0,
})
},
// 关闭评论区
close_comment_modal() {
this.setData({
active_dropdown_id: null,
show_comment_modal: false,
comment_scroll_top: 0 + Math.random(), // 关闭评论时滚动到最顶部
comments_reply_data: {}, // 清空回复评论数据
form_images_list: [], // 清空上传图片
comment_input_value: '', // 清空输入框内容
move_distance: 0,
})
},
// 评论滚动事件, 记录滚动位置(带防抖)
handle_comment_scroll(e) {
// 清除之前的防抖定时器
if (this.comment_scroll_debounce_timer) {
clearTimeout(this.comment_scroll_debounce_timer);
}
// 设置新的防抖定时器
this.comment_scroll_debounce_timer = setTimeout(() => {
this.comment_scroll_top = e.detail.scrollTop;
}, 100); // 100ms防抖延迟
},
// 评论滚动到底部事件
handle_comment_to_lower_scroll() {
if (this.goods_bottom_line_status) {
return;
}
this.comment_item_loading = true;
// 获取数据
uni.request({
url: app.globalData.get_request_url("commentsreplylist", "index", "video"),
method: 'POST',
data: {
video_id: this.current_video_id,
page: this.comments_page + 1,
video_comments_id: 0
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
if (new_data.data.length > 0) {
// 初始化评论数据
const comment_data = new_data.data.map(item1 => ({
...item1,
show_sub_comment: false,
show_sub_comment_loading: false,
page: 0,
sub_comments: [],
}));
this.active_comments.push(...comment_data);
}
// 是否显示没有更多数据
if (new_data.page >= new_data.page_total) {
// 没有更多数据了
this.setData({
goods_bottom_line_status: true,
})
}
this.comments_page = new_data.page;
this.comments_page_total = new_data.page_total
}
},
complete: () => {
this.comment_item_loading = false;
}
});
},
// 评论拖拽开始
handle_comment_touch_start(e) {
const type = e?.target?.dataset?.type || 'header';
// 如果是滚动区域内滚动到顶部才可以拖拽,如果是头部拖拽的话,一直都可以
if ((this.comment_scroll_top <= 5 && type == 'scroll') || type == 'header') {
this.comment_start_y = e.touches[0].pageY;
this.comment_current_y = this.comment_start_y;
this.move_distance = 0;
}
},
// 评论拖拽中
handle_comment_touch_move(e) {
const type = e?.target?.dataset?.type || 'header';
// 如果是滚动区域内滚动到顶部才可以拖拽,如果是头部拖拽的话,一直都可以
if ((this.comment_scroll_top <= 5 && type == 'scroll') || type == 'header') {
this.comment_current_y = e.touches[0].pageY;
this.move_distance = this.comment_current_y - this.comment_start_y;
}
},
// 评论拖拽结束
handle_comment_touch_end(e) {
const type = e?.target?.dataset?.type || 'header';
// 如果是滚动区域内滚动到顶部才可以拖拽,如果是头部拖拽的话,一直都可以
if ((this.comment_scroll_top <= 5 && type == 'scroll') || type == 'header') {
const move_distance = this.comment_current_y - this.comment_start_y;
// 如果拖拽距离足够大,关闭评论弹窗
if (move_distance > 150) {
this.close_comment_modal();
} else {
this.move_distance = 0;
}
}
},
// 评论
send_comment() {
let comment_text = this.comment_input_value;
if (!comment_text.trim()) return;
// video_id 视频id video_comments_id 父级评论id id 当前评论id
let new_video_comments_id = 0;
let reply_comments_id = 0
if (!isEmpty(this.comments_reply_data)) {
const { video_comments_id, id } = this.comments_reply_data;
new_video_comments_id = video_comments_id == 0 ? id : video_comments_id;
reply_comments_id = video_comments_id == 0 ? 0 : id;
}
uni.request({
url: app.globalData.get_request_url("comments", "index", "video"),
method: 'POST',
data: {
video_id: this.current_video_id,
video_comments_id: new_video_comments_id, // 如果父级评论id为0说明没有父级id所以取当前id
reply_comments_id: reply_comments_id, // 如果父级评论id为0说明没有父级id所以回复id为0
content: comment_text,
images: this.form_images_list.length > 0 ? this.form_images_list[0].url : '',
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
// 没有回复时的评论
if (new_video_comments_id == 0) {
this.active_comments.unshift({
...new_data,
show_sub_comment: false,
show_sub_comment_loading: false,
page: 0,
sub_comments: [],
})
this.video_data_list.forEach(item => {
if (item.id == this.current_video_id) {
item.comments_count++;
}
})
this.setData({
video_data_list: this.video_data_list,
comment_scroll_top: 0 + Math.random() // 添加主评论时滚动到最顶部
})
} else {
this.active_comments.forEach(item => {
if (item.id == new_video_comments_id) {
item.sub_comments.unshift(new_data);
item.comments_count++;
if (!item.show_sub_comment) {
item.show_sub_comment = true;
// 如果回复总数跟当前显示的数量对得上,就不显示展开. 如果之前没有页面时设置页数和分页都为1 否则保持之前的分页
item.page = item.comments_count == item.sub_comments.length ? (!isEmpty(item.page) && item.page > 0 ? item.page : 1) : 0;
// 如果之前没有数据时设置总页数为1
item.page_total = !isEmpty(item.page_total) ? item.page_total : 1;
}
}
})
}
// 清空输入框, 更新数据内容
this.setData({
active_comments: this.active_comments,
form_images_list: [],
comment_input_value: '',
comments_reply_data: {},
input_placeholder: this.$t('video-detail.video-detail.98yyuf'),
});
} else {
if (app.globalData.is_login_check(res.data)) {
app.globalData.showToast(res.data.msg);
} else {
app.globalData.showToast(this.$t('common.sub_error_retry_tips'));
}
}
}
});
},
// 展开子评论
open_sub_comment(id, is_level) {
const comment = this.active_comments.find(item => item.id == id);
if (comment) {
comment.show_sub_comment = true;
comment.show_sub_comment_loading = true;
// 如果是一级,并且有子评论数据,不需要调用接口,直接渲染评论信息
if (is_level == 1 && !isEmpty(comment.sub_comments)) {
comment.show_sub_comment_loading = false;
return;
}
uni.request({
url: app.globalData.get_request_url("commentsreplylist", "index", "video"),
method: 'POST',
data: {
video_id: this.current_video_id,
video_comments_id: id,
page: comment.page + 1,
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
if (comment.page == 0) {
comment.sub_comments = new_data.data;
} else if (new_data.data.length > 0) {
comment.sub_comments.push(...new_data.data);
}
comment.page = new_data.page;
comment.page_total = new_data.page_total
}
},
complete: () => {
comment.show_sub_comment_loading = false;
}
});
}
},
// 收起子评论
close_sub_comment(id) {
const comment = this.active_comments.find(item => item.id == id);
if (comment) {
comment.show_sub_comment = false;
}
},
// 分享事件
handle_share() {
if ((this.$refs.share || null) != null) {
this.$refs.share.init({
status: true,
share_info: this.share_info,
});
}
},
// 更新视频数据信息
get_video_data_detail(id) {
uni.request({
url: app.globalData.get_request_url("data", "index", "video"),
method: 'POST',
data: {
id: id,
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
// 更新视频数据
const index = this.video_data_list.findIndex(item => item.id == id);
if (index !== -1) {
// 使用Object.assign更新原对象保持引用不变
Object.assign(this.video_data_list[index], {
...this.video_data_list[index],
...new_data.data
});
}
}
}
});
},
// 更新点赞数量
set_givethumbs_num(id, comments_id) {
uni.request({
url: app.globalData.get_request_url("givethumbs", "index", "video"),
method: 'POST',
data: {
video_id: id,
...(isEmpty(comments_id) ? {} : {video_comments_id: comments_id}),
},
dataType: 'json',
success: res => {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
// 提取更新点赞状态的公共函数
const updateThumbsStatus = (target, new_data) => {
target.give_thumbs_count = new_data.count;
target.is_give_thumbs = new_data.is_active;
};
// 优化后的遍历逻辑
for (let i = 0; i < this.video_data_list.length; i++) {
const item = this.video_data_list[i];
if (item.id == id) {
if (!isEmpty(comments_id)) {
// 安全检查comments数组是否存在
if (this.active_comments && Array.isArray(this.active_comments)) {
for (let j = 0; j < this.active_comments.length; j++) {
const comment = this.active_comments[j];
if (comment.id == comments_id) {
updateThumbsStatus(comment, new_data);
console.log(comment);
break; // 处理完当前item后跳出循环
} else {
// 安全检查sub_comments数组是否存在
if (comment.sub_comments && Array.isArray(comment.sub_comments)) {
for (let k = 0; k < comment.sub_comments.length; k++) {
const sub_comment = comment.sub_comments[k];
if (sub_comment.id == comments_id) {
updateThumbsStatus(sub_comment, new_data);
break; // 处理完当前item后跳出循环
}
}
}
}
}
}
this.video_data_list[i].comments_list = this.active_comments;
}
break; // 处理完当前item后跳出外层循环
}
}
this.setData({
video_data_list: this.video_data_list
})
} else {
if (app.globalData.is_login_check(res.data)) {
app.globalData.showToast(res.data.msg);
} else {
app.globalData.showToast(this.$t('common.sub_error_retry_tips'));
}
}
}
});
},
// 主评论回复
comment_reply(comments) {
this.active_dropdown_id = null;
if (!isEmpty(comments)) {
this.setData({
comments_reply_data: comments,
});
}
},
// 删除回复评论数据
comment_data_delete() {
this.setData({
comments_reply_data: {},
});
},
// 播放进度变化时触发
handle_time_update(e) {
if (this.is_seeking) return;
let duration = this.current_video_duration;
// #ifdef MP-ALIPAY
if (e.detail.videoDuration > 0) {
duration = e.detail.videoDuration;
}
// #endif
// #ifndef MP-ALIPAY
if (e.detail.duration > 0) {
duration = e.detail.duration;
}
// #endif
this.setData({
current_video_duration: duration,
current_video_progress: e.detail.currentTime,
});
},
// 视频进度条拖动时触发事件
handle_slider_changing() {
this.is_seeking = true;
},
// 评论点赞
comment_like(id) {
this.active_dropdown_id = null;
this.set_givethumbs_num(this.current_video_id, id);
},
// 视频进度条拖动完成触发事件
handle_slider_change(e) {
const seek_time = e.detail.value;
if (this.create_video_contexts[this.current_index]) {
this.create_video_contexts[this.current_index].seek(seek_time);
this.setData({
current_video_progress: seek_time,
})
}
setTimeout(() => {
this.is_seeking = false;
}, 100);
},
// 视频进度条时间显示
format_time(seconds) {
if (isNaN(seconds) || seconds < 0) {
return '00:00';
}
const min = Math.floor(seconds / 60);
const sec = Math.floor(seconds % 60);
return `${min < 10 ? '0' : ''}${min}:${sec < 10 ? '0' : ''}${sec}`;
},
// 返回上一页
handle_back() {
app.globalData.page_back_prev_event();
},
// 跳转搜索记录页面
handle_search() {
// 跳转到搜索记录页面
app.globalData.url_open(`/pages/plugins/video/search-record/search-record`, false);
},
// 关闭推荐商品
product_close_event(e) {
const id = e?.currentTarget?.dataset?.id || '';
this.video_data_list.forEach((item) => {
if (item.id == id) {
item.show_goods = false;
}
});
this.setData({
video_data_list: this.video_data_list
})
},
// 点击商品卡片触发事件
handle_product_card_item(e) {
const id = e?.currentTarget?.dataset?.id || '';
const data = this.video_data_list.find(item => item.id == id);
if (!isEmpty(data) && !isEmpty(data.goods)) {
app.globalData.url_open(data.goods.goods_url);
}
},
// 点击购买商品按钮触发事件
handle_product_button(e) {
const id = e.currentTarget.dataset.id;
this.video_data_list.forEach((item, index) => {
if (item.id == id) {
if (item.show_goods && !isEmpty(item.goods)) {
app.globalData.url_open(item.goods.goods_url);
} else {
item.show_goods = true;
}
}
});
this.setData({
video_data_list: this.video_data_list
})
},
// 清理所有视频资源
cleanup_all_videos() {
try {
// 暂停所有视频
this.pause_all_videos_except(-1);
} catch (error) {
console.error('清理视频资源时出错:', error);
}
},
// 处理下拉菜单切换
handle_toggle_dropdown(comment_id) {
// 如果点击的是同一个组件,则切换状态;否则关闭其他组件并打开当前组件
if (this.active_dropdown_id == comment_id) {
this.active_dropdown_id = null;
} else {
this.active_dropdown_id = comment_id;
}
},
// 关闭下拉菜单
handle_dropdown_item_click(comment_id, obj) {
if (this.active_dropdown_id == comment_id) {
this.active_dropdown_id = null;
}
// 处理不同操作
if (obj.type == 'delete') {
// 确认删除
uni.showModal({
title: this.$t('common.warm_tips'),
content: this.$t('common.delete_confirm_tips'),
success: (res) => {
if (res.confirm) {
// 调用删除接口
this.delete_comment(comment_id);
}
}
});
} else if (obj.type == 'report') {
// 举报评论
this.popup_report_status = true;
this.report_comment_id = comment_id;
}
},
// 删除评论
delete_comment(comment_id) {
uni.request({
url: app.globalData.get_request_url("delete", "index", "video"),
method: 'POST',
data: {
ids: comment_id,
},
dataType: 'json',
success: res => {
if (res.data.code == 0) {
// 删除评论数据
this.delete_comment_handle(comment_id);
// 显示删除成功提示
app.globalData.showToast(res.data.msg, 'success');
} else {
if (app.globalData.is_login_check(res.data)) {
app.globalData.showToast(res.data.msg);
} else {
app.globalData.showToast(this.$t('common.sub_error_retry_tips'));
}
}
}
});
},
// 删除评论数据处理
delete_comment_handle(comment_id) {
// 删除成功从active_comments中移除对应数据
if (this.active_comments && Array.isArray(this.active_comments)) {
// 创建新的数组来存储过滤后的结果
const filteredComments = [];
for (let i = 0; i < this.active_comments.length; i++) {
const comment = this.active_comments[i];
// 清空回复评论数据(仅当匹配时)
if (comment.id === this.comments_reply_data.id || (comment.sub_comments && Array.isArray(comment.sub_comments) && comment.sub_comments.some(subComment => subComment.id === this.comments_reply_data.id))) {
this.comments_reply_data = {};
}
// 如果是父级评论且 id 匹配,跳过整个评论(包括子评论)
if (comment.id === comment_id) {
continue;
}
// 处理子评论
if (comment.sub_comments && Array.isArray(comment.sub_comments)) {
// 查找并移除匹配的子评论
const targetSubCommentIndex = comment.sub_comments.findIndex(subComment => subComment.id === comment_id);
if (targetSubCommentIndex !== -1) {
// 移除目标子评论
comment.sub_comments.splice(targetSubCommentIndex, 1);
// 更新显示状态和评论数
if (comment.sub_comments.length === 0) {
comment.show_sub_comment = false;
}
comment.comments_count -= 1;
}
}
// 保留当前评论
filteredComments.push(comment);
}
// 删除之后更新评论数据
this.video_data_list.forEach(item => {
if (item.id == this.current_video_id) {
item.comments_count = filteredComments.length;
}
})
// 更新数据
this.setData({
active_comments: filteredComments,
video_data_list: this.video_data_list
})
}
},
// 处理全局点击事件
handle_global_click(e) {
// 检查点击目标是否在下拉菜单相关元素内
const target = e.target || e.srcElement;
// 查找点击元素是否在comment-option或dropdown-menu内
let isInDropdown = false;
let currentElement = target;
while (currentElement && currentElement !== document) {
// 检查是否点击了下拉菜单触发器或菜单本身
if (currentElement.classList &&
(currentElement.classList.contains('comment-option') ||
currentElement.classList.contains('dropdown-menu') ||
currentElement.closest('.comment-option') ||
currentElement.closest('.dropdown-menu'))) {
isInDropdown = true;
break;
}
currentElement = currentElement.parentNode;
}
// 如果点击的不是下拉菜单相关元素,则关闭所有下拉菜单
if (!isInDropdown && this.active_dropdown_id !== null) {
this.active_dropdown_id = null;
}
},
// 关闭举报弹窗
popup_report_close_event() {
this.setData({
popup_report_status: false,
current_main_index: 0,
current_sub_index: 0,
});
},
// 直接选择主原因(用于一行显示的点击)
select_main_reason(e) {
const index = e?.currentTarget?.dataset?.index || 0;
const main_index = parseInt(index);
if (main_index !== this.current_main_index) {
this.setData({
current_main_index: main_index,
current_sub_index: 0, // 默认选中第一个子类型
});
}
},
// 直接选择子类型(用于一行显示的点击)
select_sub_reason(e) {
const index = e?.currentTarget?.dataset?.index || 0;
const sub_index = parseInt(index);
if (sub_index !== this.current_sub_index) {
this.setData({
current_sub_index: sub_index,
});
}
},
/*
* 提交举报
*/
submit_report() {
// 获取选中的举报原因和具体类型
const main_reason = this.report_type_list[this.current_main_index];
const sub_reason = main_reason.data[this.current_sub_index];
// 调用举报接口
uni.request({
url: app.globalData.get_request_url("report", "index", "video"),
method: 'POST',
data: {
id: this.report_comment_id,
reason: main_reason.name,
type: sub_reason
},
dataType: 'json',
success: res => {
if (res.data.code == 0) {
// 显示删除成功提示
app.globalData.showToast(res.data.msg, 'success');
// 关闭弹窗
this.popup_report_close_event();
} else {
if (app.globalData.is_login_check(res.data)) {
app.globalData.showToast(res.data.msg);
} else {
app.globalData.showToast(this.$t('common.sub_error_retry_tips'));
}
}
}
});
},
// 键盘显示时,切换输入框
add_comment() {
//#ifndef H5
this.is_add_comment = true;
this.active_dropdown_id = null;
//#endif
},
/**
* 键盘高度变化监听处理
* @param {Object} res - 键盘高度变化事件对象
*/
listener(res) {
// 减1是为了兼容避免跟键盘之间会不连贯
if (res.height > 0) {
this.listener_height = res.height - 1;
} else {
this.listener_height = 0;
}
},
/**
* 绑定键盘高度变化监听事件
*/
bind_keyboard_listener() {
uni.onKeyboardHeightChange(this.listener);
},
/**
* 解绑键盘高度变化监听事件
*/
unbind_keyboard_listener() {
uni.offKeyboardHeightChange(this.listener);
},
}
};
</script>
<style lang="scss" scoped>
@import './detail.css';
</style>