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

1797 lines
96 KiB
Plaintext
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" :style="width_height_style">
<view v-if="video_data_list && video_data_list.length > 0" class="content pr" :style="width_height_style">
<!-- 视频列表 -->
<view class="swiper" ref="swiper">
<block v-for="(video_item, index) in video_data_list" :key="video_item.id">
<view class="pr" @tap.stop="toggle_play_pause" :style="width_height_style" @touchstart.prevent="ListTouchStart" @touchmove.prevent="ListTouchMove">
<view class="video-bg" :style="width_height_style">
<template v-if="!isEmpty(video_item.cover)">
<image class="video-bg-image" :style="width_height_style" :src="video_item.cover" mode="scaleToFill"></image>
</template>
</view>
<video class="video" :style="width_height_style + swiperStyle" :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" @tap.stop="toggle_play_pause"></video>
<view v-if="paused && current_index == index" class="play-icon" :style="width_height_style + swiperStyle">
<view class="flex-1 pr flex-row align-c jc-c">
<view class="play-icon-bg"></view>
<view class="pa z-i">
<u-icon propName="bofang" propSize="120rpx" propColor="#4F3E35"></u-icon>
</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">
<u-icon propName="givealike" propSize="60rpx" :propColor="video_item.is_give_thumbs == 0 ? '#fff' : '#F4B73F'"></u-icon>
<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">
<u-icon propName="comment" propSize="60rpx" propColor="#fff"></u-icon>
<text class="action-text">{{ video_item.comments_count }}</text>
</view>
<view class="action-item" @tap.stop="handle_share">
<u-icon propName="share-solid" propSize="60rpx" propColor="#fff"></u-icon>
<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">
<view v-if="video_item.show_goods" class="flex-row align-c product-card-item mb-10" :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 ml-10">
<text class="product-name text-line-1 mb-10">{{ 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">
<u-icon propName="close" propSize="40rpx" propColor="#999"></u-icon>
</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">
<u-icon propName="cart-have" propSize="30rpx" propColor="#F5C366"></u-icon>
<text class="size-14 cr-f ml-10">{{$t('common.buy')}}{{$t('common.goods')}}</text>
</view>
<u-icon propName="angle-right" propSize="30rpx" propColor="#fff"></u-icon>
</view>
</view>
</view>
<!-- Progress Bar -->
<view class="progress-bar-container" v-if="current_index == index" :style="'width: ' + (windowWidth - 20) + 'px'">
<slider class="flex-1 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>
</block>
</view>
<!-- 搜索框 -->
<view v-if="!show_comment_modal" class="header-top" :style="top_content_style + menu_button_info + ' width: ' + windowWidth + 'px;'">
<view id="search-height" class="flex-row align-c">
<!-- 支付宝小程序自带返回按钮,这里就不给返回按钮了,这里给留出一点空间就行 -->
<!-- #ifndef MP-ALIPAY -->
<view class="cp" @tap="handle_back">
<u-icon propName="arrow-left" propSize="36rpx" propColor="#bbb" class="mr-10"></u-icon>
</view>
<!-- #endif -->
<view class="flex-1" :style="header_padding_left">
<component-search propIsDisabled @disabledSearch="handle_search" :propsWidth="windowWidth" />
</view>
</view>
</view>
</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" :style="width_height_style" @tap="close_comment_modal">
<view class="comment-content bottom-line-exclude-bottom" @tap="comment_modal_content" :style="commentContentStyle">
<view class="comment-header" data-type="header" @tap.stop @touchstart.prevent="handle_comment_touch_start" @touchmove.prevent="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" :style="'width:' + windowWidth + 'px;'" data-type="scroll" @tap.stop @touchstart.prevent="handle_comment_touch_start" @touchmove.prevent="handle_comment_touch_move" @touchend="handle_comment_touch_end">
<!-- 评论内容区域 -->
<!-- <scroll-view class="flex-1 comment-list flex-row" scroll-y :scroll-top="comment_scroll_top" show-scrollbar="false" scroll-with-animation :scroll-with-touch="!is_dragging" @scrolltolower="handle_comment_to_lower_scroll" @scroll="handle_comment_scroll"> -->
<list class="comment-list comment-scroll" :show-scrollbar="false" :scrollable="!is_dragging" loadmoreoffset="100" @scroll="handle_comment_scroll" @loadmore="handle_comment_to_lower_scroll">
<template v-if="active_comments && active_comments.length > 0">
<cell v-for="(comment_item, index) in active_comments" :key="comment_item.id">
<view class="comment-item flex-col">
<commentInfoComponent :style="window_more_style" :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 mt-10">
<view v-if="comment_item.sub_comments && Array.isArray(comment_item.sub_comments) && comment_item.sub_comments.length > 0 && comment_item.show_sub_comment" class="sub-comment-list flex-col jc-c mb-10">
<view v-for="(sub_comment_item, sub_comment_index) in comment_item.sub_comments" :key="sub_comment_index" class="sub-comment-item flex-row align-s mb-10">
<commentInfoComponent :style="window_sub_more_style" :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 :style="window_more_style" :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">
<component-loading :style="window_more_style"></component-loading>
</template>
<view v-else class="sub-comment-more flex-row align-c">
<template v-if="comment_item.page != null && comment_item.page < comment_item.page_total">
<commentMoreComponent :style="window_more_style" class="mr-10" :propId="comment_item.id" :propIsLevel="2" :propText="$t('common.expand')" @comment_more_event="open_sub_comment"></commentMoreComponent>
</template>
<commentMoreComponent :style="window_more_style" :propId="comment_item.id" :propText="$t('common.retract')" propIconName="arrow-top" @comment_more_event="close_sub_comment"></commentMoreComponent>
</view>
</template>
</template>
</view>
</view>
</cell>
<template v-if="comment_item_loading">
<cell>
<view class="flex-row align-c jc-c" :style="window_more_style">
<component-loading></component-loading>
</view>
</cell>
</template>
<template v-else>
<cell>
<!-- 结尾 -->
<component-bottom-line :propStatus="goods_bottom_line_status"></component-bottom-line>
</cell>
</template>
</template>
<template v-else>
<cell>
<view class="flex-row align-c jc-c" :style="window_more_style">
<component-no-data :propMsg="$t('common.no_data')"></component-no-data>
</view>
</cell>
</template>
<!-- </view> -->
</list>
</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-1 flex-col align-c">
<view v-if="!isEmpty(comments_reply_data)" class="comment-reply-content flex-row align-c jc-sb" :style="window_more_style">
<text class="size-12 cr-f text-line-1 mr-10">@{{ isEmpty(comments_reply_data.user) ? '' : comments_reply_data.user.user_name_view }}:{{ comments_reply_data.content }}</text>
<view data-type="image" @tap="comment_data_delete">
<u-icon propName="close-line" propSize="24rpx" propColor="#fff"></u-icon>
</view>
</view>
<view class="flex-row align-s pt-4" :style="window_more_style">
<view class="flex-1 comment-input-content flex-col jc-c">
<view class="flex-row align-s pr-16 box-border-box" @tap="add_comment">
<text :class="'flex-1 comment-input text-line-1 ' + (isEmpty(comment_input_value) ? 'cr-grey' : 'cr-black')">{{ isEmpty(comment_input_value) ? $t('video-detail.video-detail.98yyuf') : comment_input_value }}</text>
<view class="pt-8" @tap="upload_tap">
<component-upload :propMaxNum="propMaxNum" :propPathType="editor_path_type" propSlot propSingleCall propIsAllInfo @callBack="upload_images_event">
<u-icon propName="layout-module-single-images" propSize="40rpx" propColor="#999"></u-icon>
</component-upload>
</view>
</view>
<view v-if="form_images_list && 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">
<image :src="item.url" :data-index="index" @tap="upload_show_event" mode="aspectFill" class="comment-input-img"></image>
<view class="form-img-icon flex-row align-c jc-c" @tap="comment_input_img_close">
<u-icon propName="close" propSize="30rpx" propColor="#000" class="flex-row align-c jc-c" :data-index="index"></u-icon>
</view>
</view>
</view>
</view>
<view v-if="!isEmpty(comment_input_value)" class="pt-4 flex-row align-c ml-10">
<button size="mini" type="default" class="margin-0 bg-main border-0" @tap="send_comment"><text class="comment-send-button">{{$t('common.send')}}</text></button>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 举报弹窗 -->
<u-popup ref="popupReportRef" propMode="bottom" propCloseType="text" :propWidth="'width:' + windowWidth + 'px;'" propTitleBorder class="pointer-events-auto" :propTitle="$t('video-detail.video-detail.rfsdfg')" :propCloseable="true" @close="popup_report_close_event" @callBack="submit_report">
<view class="report-content">
<!-- 主要内容区域 -->
<view class="report-body">
<!-- 第一层:举报原因选择 -->
<view v-if="report_type_list && report_type_list.length > 0" class="report-section">
<view class="report-label flex-row align-c">{{$t('video-detail.video-detail.rfsdfg')}}<text class="ml-10 report-required">*</text></view>
<view class="flex-row align-c flex-wrap">
<radio-group class="flex-row align-c flex-wrap" :style="window_more_style" @change="select_main_reason">
<label v-for="(mainItem, main_index) in report_type_list" :key="main_index" class="flex-row align-c mr-10">
<view class="flex-row align-c">
<radio :value="main_index.toString()" :checked="current_main_index === main_index" style="transform:scale(0.7)" />
<text class="report-name flex-row align-c">{{mainItem.name}}</text>
</view>
</label>
</radio-group>
</view>
</view>
<!-- 第二层:具体类型选择(当有主类别选中时显示) -->
<view class="report-section mt-20" v-if="current_main_index >= 0 && report_type_list[current_main_index] && report_type_list[current_main_index].data">
<view class="report-label flex-row align-c">{{$t('video-detail.video-detail.fsdf33')}}<text class="ml-10 report-required">*</text></view>
<view class="flex-row align-c flex-wrap">
<radio-group class="flex-row align-c flex-wrap" :style="window_more_style" @change="select_sub_reason">
<label v-for="(subItem, sub_index) in report_type_list[current_main_index].data" :key="sub_index" class="flex-row align-c mr-10">
<view class="flex-row align-c">
<radio :value="sub_index.toString()" :checked="current_sub_index === sub_index" style="transform:scale(0.7)" />
<text class="report-name flex-row align-c">{{subItem}}</text>
</view>
</label>
</radio-group>
</view>
</view>
</view>
</view>
</u-popup>
<!-- 添加评论弹出框 -->
<view v-if="is_add_comment" class="keyboard-input br-top-shadow" :style="'width:'+ windowWidth +'px;bottom:' + listener_height + 'px;'">
<view class="flex-1 flex-col">
<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">
<text class="size-12 cr-f text-line-1 mr-10">@{{ comments_reply_data.user.user_name_view }}:{{ comments_reply_data.content }}</text>
<view data-type="image" @tap="comment_data_delete">
<u-icon propName="close-line" propSize="24rpx" propColor="#fff"></u-icon>
</view>
</view>
<view class="flex-row align-c pr-16 box-border-box">
<textarea ref="commentRef" :value="comment_input_value" :focus="is_add_comment" class="comment-input mr-10 cr-black" placeholder-class="cr-grey" auto-height maxlength="500" :show-confirm-bar="false" :adjust-position="false" :placeholder="$t('video-detail.video-detail.98yyuf')" @input="comment_input_event" @confirm="send_comment" />
</view>
<view v-if="form_images_list && 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">
<image :src="item.url" :data-index="index" @tap="upload_show_event" mode="aspectFill" class="comment-input-img"></image>
<view class="form-img-icon flex-row align-c jc-c" @tap="comment_input_img_close">
<u-icon propName="close" propSize="30rpx" propColor="#000" class="flex-row align-c jc-c" :data-index="index"></u-icon>
</view>
</view>
</view>
</view>
<view class="flex-row align-c jc-sb pt-10">
<component-upload :propMaxNum="propMaxNum" :propPathType="editor_path_type" propSlot propSingleCall propIsAllInfo propChooseFocus propFailChooseFocus @call-back="upload_images_event" @chooseFocus="upload_event">
<u-icon propName="layout-module-single-images" propSize="40rpx" propColor="#999"></u-icon>
</component-upload>
<button :disabled="isEmpty(comment_input_value)" size="mini" type="default" :class="'margin-0 bg-main border-0 ' + (isEmpty(comment_input_value) ? 'disabled' : '')" @tap="send_comment"><text class="comment-send-button text">{{$t('common.send')}}</text></button>
</view>
</view>
</view>
<!-- 分享弹窗 -->
<u-share-popup ref="share" class="pointer-events-auto"></u-share-popup>
</view>
</template>
<script>
const app = getApp();
import { get_math, isEmpty, video_get_top_left_padding, showToast } 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 componentNoData from '@/components/no-data/no-data';
import componentBottomLine from '@/components/bottom-line/bottom-line';
import componentUpload from '@/components/upload/upload';
import scrollMixins from './scrollMixins';
// 多语言
//#ifdef APP-NVUE
const BindingX = uni.requireNativePlugin('bindingx');
const animation = weex.requireModule('animation');
const modal = weex.requireModule('modal');
import i18n from '@/locale/index.js';
// nvue页面在方法中使用时的处理
import { initVueI18n } from '@dcloudio/uni-i18n';
import indexNvue from '@/locale/index-nvue.js';
const { t } = initVueI18n(indexNvue)
//#endif
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO || H5
bar_height = 0;
// #endif
export default {
//#ifdef APP-NVUE
i18n,
//#endif
mixins:[scrollMixins],
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
top_content_style: 'padding-top:' + bar_height + 'px;padding-bottom:10px;',
data_list_loding_status: 1,
data_list_loding_msg: '',
video_data_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, // 评论拖拽距离
is_dragging: false, // 是否正在拖拽中
current_video_id: '', // 当前播放视频的 ID
comment_scroll_top: 0, // 评论滚动距离顶部的距离
comment_input_value: '',
propMaxNum: 1, // 上传数量
form_images_list: [],
share_info: {},
menu_button_info: '',
base_config_data: {},
comment_scroll_debounce_timer: null, // 评论滚动防抖定时器
comment_move_throttle_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, // 是否手动暂停
windowWidth: 0,
windowHeight: 0,
padding_width: 0,
sub_comment_padding_left: 0,
};
},
components: {
commentInfoComponent,
commentMoreComponent,
componentSearch,
componentNoData,
componentBottomLine,
componentLoading,
componentUpload
},
watch: {
// 监听路由变化
listener_height(e) {
if (e > 0) {
this.handle_comment_input_value();
} else {
this.is_add_comment = false;
}
}
},
computed: {
// 视频列表高度
swiperStyle() {
return this.show_comment_modal ? (this.move_distance > 0 ? `height: ${Math.round(this.windowHeight * 0.3) + this.move_distance}px;` : `height: ${Math.round(this.windowHeight * 0.3)}px;`) : `height: ${this.windowHeight}px;`;
},
// 评论内容区域高度
commentContentStyle() {
const baseHeight = Math.round(this.windowHeight * 0.7);
return this.show_comment_modal && this.move_distance > 0
? `transform: translateY(3px); width:${this.windowWidth}px; height: ${baseHeight - this.move_distance}px;`
: `transform: translateY(0); width:${this.windowWidth}px; height: ${baseHeight}px;`;
},
// 当前播放视频的索引
current_video_index() {
if (this.video_data_list && this.video_data_list.length > 0) {
return this.video_data_list.findIndex(item => item.id == this.current_video_id);
} else {
return -1;
}
},
close_circular() {
try {
if (this.video_data_list && 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
}
} catch {
console.log('close_circular');
}
},
width_height_style() {
return `width: ${ this.windowWidth }px;height: ${ this.windowHeight }px;`;
},
window_more_style() {
return `width: ${ this.windowWidth - this.padding_width }px;`
},
window_sub_more_style() {
return `width: ${ this.windowWidth - this.sub_comment_padding_left }px;`
}
},
onLoad(params) {
try {
// 调用公共事件方法
app.globalData.page_event_onload_handle(params);
// 设置参数
this.params = app.globalData.launch_params_handle(params);
} catch {
console.log('close_circular');
}
},
onShow() {
try {
const data = uni.getWindowInfo();
this.windowWidth = data.windowWidth > 800 ? 800 : data.windowWidth;
this.windowHeight = data.windowHeight;
// 调用公共事件方法
app.globalData.page_event_onshow_handle();
// 视频播放
if (!this.is_manual_pause && this.create_video_contexts && 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();
} catch {
console.log('close_circular');
}
},
onHide() {
// 清理所有视频资源
this.cleanup_all_videos();
},
mounted() {
try {
uni.$on('chooseFocus', (data) => {
this.upload_event();
})
uni.$on('callBack', (res) => {
this.upload_images_event(res);
})
// 初始化
this.init();
// 创建监听事件
this.bind_keyboard_listener();
} catch {
console.log('close_circular');
}
},
beforeDestroy() {
// 清理定时器
if (this.comment_scroll_debounce_timer) {
clearTimeout(this.comment_scroll_debounce_timer);
}
if (this.comment_move_throttle_timer) {
clearTimeout(this.comment_move_throttle_timer);
}
// 清理所有视频资源
this.cleanup_all_videos();
this.unbind_keyboard_listener();
uni.$off('chooseFocus');
uni.$off('callBack');
},
methods: {
isEmpty,
init() {
try {
// 小程序下,获取小程序胶囊的宽度
let menu_button_info = `max-width:${ this.windowWidth }px;`;
// #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(${ this.windowWidth } - ${custom.width + 10}px);`;
}
// #endif
// #endif
// 视频详情页,需要添加 padding-left
let padding_left = '';
// #ifdef MP-ALIPAY
padding_left = video_get_top_left_padding();
// #endif
this.padding_width = app.globalData.rpx_to_px(30);
this.sub_comment_padding_left = app.globalData.rpx_to_px(80);
this.header_padding_left = padding_left;
this.menu_button_info = menu_button_info;
this.current_video_id = isEmpty(this.current_video_id) ? this.params.id : this.current_video_id;
this.get_video_detail(this.current_video_id);
//设置参数
this.typeX = false //开启左右滑动
this.playCount = 2 //剩余多少视频加载视频列表
this.startDistance = 5 //判断左右上下拖动的启动距离 px
this.minTime = 300 //判断快速滑动的时间,该时间内无视回弹距离判断
this.backDistance = 200 //判断上下滑动的回弹距离 px
} catch (error) {
console.error('init error:', error);
}
},
/*
* 获取视频详情
* @param {*} id 视频 id
*/
get_video_detail(id) {
try {
// 获取数据
uni.request({
url: app.globalData.get_request_url("detail", "index", "video"),
method: 'POST',
data: {
id: id
},
dataType: 'json',
success: res => {
try {
const data = res.data;
if (data.code == 0) {
const new_data = data.data;
// 数据更新
this.data_list_loding_status = 3;
this.video_data_list = [new_data.data];
this.report_type_list = new_data.report_type_list;
this.base_config_data = new_data.base_config_data;
this.editor_path_type = new_data.editor_path_type;
this.get_last_or_next_data_list(this.params.id, 0, 1);
} else {
this.data_list_loding_status = 0;
this.data_tabs_loding_msg = data.msg;
}
} catch {
console.error('get_video_detail error:', error);
}
},
fail: (err) => {
this.data_list_loding_status = 2;
this.data_list_loding_msg = t('common_internet_error_tips');
}
});
} catch (error) {
console.error('get_video_detail error:', error);
}
},
/*
* 获取视频列表
* @param {*} id 视频 id
* @param {*} is_last 是否获取上一批数据
* @param {*} is_next 是否获取下一批数据
*/
get_last_or_next_data_list(id, is_last = 0, is_next = 0) {
try {
// 获取数据
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_next == 1 && new_data.next && 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.video_data_list = this.video_data_list;
this.$set(this, 'video_data_list', this.video_data_list);
this.video_data_list.forEach((item, index) => {
// this.create_video_contexts[index] = uni.createVideoContext(`video_${index}`, this);
this.$set(this.create_video_contexts, index, uni.createVideoContext(`video_${index}`, this));
});
// 逻辑说明:当是最后一个视频且需要播放下一个时,根据数组长度和新索引计算新的当前索引
// - 数组长度 > 2 时:新索引是最后一个元素则返回 2是倒数第二个则返回 1否则返回 0
// - 数组长度 <= 2 时:返回 length - 1
// this.current_index = is_next == 1 ? new_index : this.current_index;
if (is_next == 1) {
setTimeout(() => {
// const index = this.video_data_list.findIndex(item => item.id == this.current_index);
if (this.create_video_contexts && this.create_video_contexts[this.current_index]) { // 当前播放的视频索引为 0
this.video_play_event(this.create_video_contexts[this.current_index], true);
}
}, 100);
}
}
}
});
} catch (error) {
console.error('get_last_or_next_data_list error:', error);
}
},
// 评论输入框内容改变一下,确保能正常展开
handle_comment_input_value() {
this.is_add_comment = true;
const data = this.comment_input_value;
this.comment_input_value = '';
setTimeout(() => {
this.comment_input_value = data;
}, 100);
},
// 实际的 swiper 切换处理逻辑
process_swiper_change(current) {
try {
// 先暂停所有视频,确保不会有后台播放
this.pause_all_videos_except(current);
const id = this?.video_data_list[current]?.id || '';
// 更新状态
this.paused = false;
this.current_index = current;
this.is_manual_pause = false;
this.current_video_progress = 0;
this.current_video_duration = 1;
this.is_seeking = false;
this.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
// 获取视频详细信息
this.get_video_data_detail(id);
// 更新分享信息
this.update_share_info(this.video_data_list[this.current_video_index]);
setTimeout(() => {
this.play_current_video_safely(this.current_index);
}, 50);
} catch (error) {
console.error('process_swiper_change error:', error);
}
},
showToast(msg) {
app.globalData.showToast(msg);
},
// 批量暂停除指定索引外的所有视频
pause_all_videos_except(exceptIndex) {
try {
if (this.create_video_contexts && this.create_video_contexts.length > 0) {
// 暂停 uni.createVideoContext 创建的视频
this.create_video_contexts.forEach((context, index) => {
if (index !== exceptIndex && context) {
try {
context.pause();
} catch (error) {
console.warn(`暂停视频 ${index} 失败:`, error);
}
}
});
}
} catch (error) {
console.error('pause_all_videos_except error:', error);
}
},
// 安全播放当前视频
play_current_video_safely(index) {
try {
// 优先使用 uni.createVideoContext
if (this.create_video_contexts && this.create_video_contexts[index]) {
this.video_play_event(this.create_video_contexts[index]);
return;
}
} catch (error) {
console.error('play_current_video_safely error:', error);
}
},
// 切换播放暂停
toggle_play_pause(e) {
try {
const currentIndex = this.current_index;
// 检查视频上下文是否存在
const videoContext = this.create_video_contexts[currentIndex] || this.video_contexts[currentIndex];
if (!videoContext) {
console.warn(`当前索引 ${currentIndex} 无可用视频上下文`);
e.stopPropagation();
return;
}
this.paused = !this.paused;
this.is_manual_pause = !this.paused;
if (this.paused) {
// 暂停当前视频
try {
videoContext.pause();
} catch (error) {
e.stopPropagation();
console.warn('暂停视频失败:', error);
}
} else {
// 播放当前视频
this.video_play_event(videoContext);
}
e.stopPropagation();
} catch (error) {
console.error('toggle_play_pause error:', error);
}
},
// 更新分享信息
update_share_info(data) {
try {
const info = {
title: data?.title || '',
desc: data?.desc || '',
path: '/pages/plugins/video/detail/detail',
query: 'id=' + this.current_video_id,
img: data.cover || ''
}
this.share_info = info;
// 分享菜单处理
app.globalData.page_share_handle(info);
// 更新页面标题
uni.setNavigationBarTitle({title: data.title});
} catch (error) {
console.error('update_share_info error:', error);
}
},
// 安全的视频播放事件处理
video_play_event(videoContext, is_first_play = false) {
try {
if (!videoContext) {
this.paused = true;
this.is_manual_pause = false;
return;
}
try {
if (is_first_play) {
//#ifdef H5
videoContext.play().catch((error) => {
this.paused = true;
this.is_manual_pause = false;
});
//#endif
//#ifndef H5
videoContext.play();
//#endif
} else {
videoContext.play();
}
} catch (error) {
console.error('视频播放异常:', error);
this.paused = true;
this.is_manual_pause = false;
}
} catch (error) {
console.error('video_play_event error:', error);
}
},
// 评论输入框事件
comment_input_event(e) {
try {
this.comment_input_value = e.detail.value;
} catch (error) {
console.error('comment_input_event error:', error);
}
},
// 阻止上传图片事件触发父级方法
upload_tap(e) {
e.stopPropagation()
},
// 图片上传回调
upload_images_event(res) {
try {
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,
});
}
}
} catch (error) {
console.error('upload_images_event error:', error);
}
},
upload_event() {
setTimeout(() => {
this.handle_comment_input_value();
}, 100);
},
// 上传图片预览
upload_show_event(e) {
try {
const index = e?.currentTarget?.dataset?.index || 0;
uni.previewImage({
current: this?.form_images_list[index]?.url || '',
urls: this.form_images_list.map(item => item.url),
});
} catch (error) {
console.error('upload_show_event error:', error);
}
},
// 评论输入图片删除
comment_input_img_close(e) {
try {
const index = e?.currentTarget?.dataset?.index || 0;
var list = this.form_images_list;
list.splice(index, 1);
// 图片赋值
this.form_images_list = list;
} catch (error) {
console.error('comment_input_img_close error:', error);
}
},
// 播放
handle_play() {
try {
this.paused = false;
this.is_manual_pause = false;
} catch (error) {
console.error('handle_play error:', error);
}
},
comment_modal_content(e) {
try {
e.stopPropagation();
} catch (error) {
console.error('comment_modal error:', error);
}
},
// 收藏
handle_like(e) {
try {
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(this.current_video_id);
}
e.stopPropagation();
} catch (error) {
console.error('handle_like error:', error);
}
},
// 打开评论区
handle_comment(e) {
try {
e.stopPropagation();
// const id = this.current_video_id;
const old_data = this.video_data_list.find(item => item.id == this.current_video_id);
if (old_data && old_data.comments_list) {
// 初始化评论数据
const new_data = old_data.comments_list.map(item1 => ({
...item1,
show_sub_comment: false,
show_sub_comment_loading: false,
page: 0,
sub_comments: [],
}));
this.active_comments = new_data;
this.comments_page = 1;
this.comments_page_total = 5;
this.comment_item_loading = false;
this.show_comment_modal = true;
this.move_distance = 0;
this.is_dragging = false; // 重置拖拽状态
this.comment_start_y = 0; // 重置起始位置
this.comment_current_y = 0; // 重置当前位置
this.comment_scroll_top = 0 + Math.random(); // 滚动到最顶部
}
} catch (error) {
console.error('handle_comment error:', error);
}
},
// 关闭评论区
close_comment_modal(e) {
try {
this.active_dropdown_id = null;
this.show_comment_modal = false;
this.comment_scroll_top = 0 + Math.random(); // 关闭评论时滚动到最顶部
this.comments_reply_data = {}; // 清空回复评论数据
this.form_images_list = []; // 清空上传图片
this.comment_input_value = ''; // 清空输入框内容
this.move_distance = 0;
this.is_dragging = false; // 重置拖拽状态
this.comment_start_y = 0; // 重置起始位置
this.comment_current_y = 0; // 重置当前位置
// 清理节流定时器
if (this.comment_move_throttle_timer) {
clearTimeout(this.comment_move_throttle_timer);
this.comment_move_throttle_timer = null;
}
if (this.$refs.commentRef) {
this.$refs.commentRef.blur();
}
if (e) {
e.stopPropagation();
}
} catch (error) {
console.error('close_comment_modal error:', error);
}
},
// 评论滚动事件,记录滚动位置(带防抖)
handle_comment_scroll(e) {
try {
this.active_dropdown_id = null;
// 清除之前的防抖定时器
if (this.comment_scroll_debounce_timer) {
clearTimeout(this.comment_scroll_debounce_timer);
}
// 设置新的防抖定时器
this.comment_scroll_debounce_timer = setTimeout(() => {
this.comment_scroll_top = Math.abs(e.contentOffset.y);
}, 100); // 100ms防抖延迟
} catch (error) {
console.error('handle_comment_scroll error:', error);
}
},
// 评论滚动到底部事件
handle_comment_to_lower_scroll() {
try {
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.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;
}
});
} catch (error) {
console.error('handle_comment_to_lower_scroll error:', error);
}
},
// 评论拖拽开始
handle_comment_touch_start(e) {
try {
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 || 0;
this.comment_current_y = this.comment_start_y;
this.move_distance = 0;
this.is_dragging = false; // 开始时不设置为拖拽状态,需要移动一定距离后才算拖拽
}
} catch (error) {
console.error('handle_comment_touch_start error:', error);
}
},
// 评论拖拽中
handle_comment_touch_move(e) {
try {
const type = e?.target?.dataset?.type || 'header';
// 如果是滚动区域内滚动到顶部才可以拖拽,如果是头部拖拽的话,一直都可以
if ((this.comment_scroll_top <= 5 && type == 'scroll') || type == 'header') {
// 阻止默认行为,防止页面滚动干扰
if (e.preventDefault) {
e.preventDefault();
}
const current_y = e?.touches[0]?.pageY || 0;
const move_y = current_y - this.comment_current_y;
// 小于3的时候不认为是有效滑动避免因为手指抖动影响
if (move_y < 3) {
return;
}
const distance = current_y - this.comment_start_y;
// 只有向下移动且距离超过阈值15px才开始拖拽避免误触和抖动
if (distance > 15) {
this.is_dragging = true;
this.comment_current_y = current_y;
// 使用节流控制 move_distance 的更新频率,避免计算属性频繁触发导致抖动
if (this.comment_move_throttle_timer) {
return;
}
this.move_distance = distance;
// 设置节流定时器16ms 约等于 60fps保证流畅度同时避免过度更新
this.comment_move_throttle_timer = setTimeout(() => {
this.comment_move_throttle_timer = null;
}, 60);
}
}
} catch(error) {
console.error('handle_comment_touch_move error:', error);
}
},
// 评论拖拽结束
handle_comment_touch_end(e) {
try {
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;
}
this.is_dragging = false; // 拖拽结束,重置状态
// 清理节流定时器
if (this.comment_move_throttle_timer) {
clearTimeout(this.comment_move_throttle_timer);
this.comment_move_throttle_timer= null;
}
}
} catch (error) {
console.error('handle_comment_touch_end error:', error);
}
},
// 评论
send_comment() {
try {
this.active_dropdown_id = null;
let comment_text = this.comment_input_value;
if (!comment_text.trim()) {
app.globalData.showToast('请填写评论内容');
};
// 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) {
// 关闭输入框
this.is_add_comment = false;
//关闭键盘
uni.hideKeyboard();
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.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;
}
}
})
}
// 更新视频数据
const videoItem = this.video_data_list.find(item => item.id == this.current_video_id);
if (videoItem) {
videoItem.comments_list = this.active_comments;
if (new_video_comments_id == 0) {
videoItem.comments_count++;
}
}
this.$set(this, 'video_data_list', this.video_data_list);
// 清空输入框,更新数据内容
this.active_comments = this.active_comments;
this.form_images_list = [];
this.comment_input_value = '';
this.comments_reply_data = {};
} else {
if (app.globalData.is_login_check(res.data)) {
app.globalData.showToast(res.data.msg);
} else {
app.globalData.showToast(t('common_sub_error_retry_tips'));
}
}
}
});
} catch (error) {
console.error('send_comment error:', error);
}
},
// 展开子评论
open_sub_comment(id, is_level) {
try {
this.active_dropdown_id = null;
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;
}
});
}
} catch (error) {
console.error('open_sub_comment error:', error);
}
},
// 收起子评论
close_sub_comment(id) {
try {
this.active_dropdown_id = null;
const comment = this.active_comments.find(item => item.id == id);
if (comment) {
comment.show_sub_comment = false;
}
} catch (error) {
console.error('close_sub_comment error:', error);
}
},
// 分享事件
handle_share() {
try {
if ((this.$refs.share || null) != null) {
this.$refs.share.init({
status: true,
share_info: this.share_info,
});
}
} catch (error) {
console.error('handle_share error:', error);
}
},
// 更新视频数据信息
get_video_data_detail(id) {
try {
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
});
}
}
}
});
} catch (error) {
console.error('get_video_data_detail error:', error);
}
},
// 更新点赞数量
set_givethumbs_num(id, comments_id) {
try {
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);
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;
} else {
updateThumbsStatus(item, new_data);
}
break; // 处理完当前item后跳出外层循环
}
}
// this.video_data_list = this.video_data_list;
this.$set(this, '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(t('common_sub_error_retry_tips'));
}
}
}
});
} catch (error) {
console.error('set_givethumbs_num error:', error);
}
},
// 主评论回复
comment_reply(comments) {
try {
this.active_dropdown_id = null;
if (!isEmpty(comments)) {
this.comments_reply_data = comments;
}
} catch (error) {
console.error('comment_reply error:', error);
}
},
// 删除回复评论数据
comment_data_delete() {
try {
this.active_dropdown_id = null;
this.comments_reply_data = {};
} catch (error) {
console.error('comment_data_delete error:', error);
}
},
// 播放进度变化时触发
handle_time_update(e) {
try {
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.$set(this, 'current_video_duration', duration);
this.$set(this, 'current_video_progress', e.detail.currentTime)
} catch (error) {
console.error('handle_time_update error:', error);
}
},
// 视频进度条拖动时触发事件
handle_slider_changing() {
try {
this.is_seeking = true;
e.stopPropagation();
} catch (error) {
console.error('handle_slider_changing error:', error);
}
},
// 评论点赞
comment_like(id) {
try {
this.active_dropdown_id = null;
this.set_givethumbs_num(this.current_video_id, id);
} catch (error) {
console.error('comment_like error:', error);
}
},
// 视频进度条拖动完成触发事件
handle_slider_change(e) {
try {
const seek_time = e.detail.value;
if (this.create_video_contexts && this.create_video_contexts[this.current_index]) {
this.create_video_contexts[this.current_index].seek(seek_time);
this.current_video_progress = seek_time;
}
setTimeout(() => {
this.is_seeking = false;
}, 100);
// e.stopPropagation();
} catch (error) {
console.error('handle_slider_change error:', error);
}
},
// 视频进度条时间显示
format_time(seconds) {
try {
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}`;
} catch (error) {
console.error('format_time error:', error);
}
},
// 返回上一页
handle_back(e) {
try {
app.globalData.page_back_prev_event();
e.stopPropagation();
} catch (error) {
console.error('handle_back error:', error);
}
},
// 跳转搜索记录页面
handle_search() {
try {
// 跳转到搜索记录页面
app.globalData.url_open(`/pages/plugins/video/search-record/search-record`, false);
} catch (error) {
console.error('handle_search error:', error);
}
},
// 关闭推荐商品
product_close_event(e) {
try {
this.video_data_list.forEach((item) => {
if (item.id == this.current_video_id) {
item.show_goods = false;
}
});
// this.video_data_list = this.video_data_list;
this.$set(this, 'video_data_list', this.video_data_list);
e.stopPropagation();
} catch (error) {
console.error('product_close_event error:', error);
}
},
// 点击商品卡片触发事件
handle_product_card_item(e) {
try {
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);
}
e.stopPropagation();
} catch (error) {
console.error('handle_product_card_item error:', error);
}
},
// 点击购买商品按钮触发事件
handle_product_button(e) {
try {
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.video_data_list = this.video_data_list;
this.$set(this, 'video_data_list', this.video_data_list);
e.stopPropagation();
} catch (error) {
console.error('handle_product_button error:', error);
}
},
// 清理所有视频资源
cleanup_all_videos() {
try {
// 暂停所有视频
this.pause_all_videos_except(-1);
} catch (error) {
console.error('清理视频资源时出错:', error);
}
},
// 处理下拉菜单切换
handle_toggle_dropdown(comment_id) {
try {
if (this.active_dropdown_id == comment_id) {
this.active_dropdown_id = null;
} else {
this.active_dropdown_id = comment_id;
}
} catch (error) {
console.error('handle_toggle_dropdown error:', error);
}
},
// 关闭下拉菜单
handle_dropdown_item_click(comment_id, obj) {
try {
if (this.active_dropdown_id == comment_id) {
this.active_dropdown_id = null;
}
// 处理不同操作
if (obj.type == 'delete') {
// 确认删除
uni.showModal({
title: t('common_warm_tips'),
content: t('common_delete_confirm_tips'),
success: (res) => {
if (res.confirm) {
// 调用删除接口
this.delete_comment(comment_id);
}
}
});
} else if (obj.type == 'report') {
// 举报评论
this.$refs.popupReportRef.open();
this.report_comment_id = comment_id;
}
} catch (error) {
console.error('handle_dropdown_item_click error:', error);
}
},
// 删除评论
delete_comment(comment_id) {
try {
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(t('common_sub_error_retry_tips'));
}
}
}
});
} catch (error) {
console.error('send_comment error:', error);
}
},
// 删除评论数据处理
delete_comment_handle(comment_id) {
try {
// 删除成功从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);
}
// 更新视频数据
const videoItem = this.video_data_list.find(item => item.id == this.current_video_id);
if (videoItem) {
videoItem.comments_list = filteredComments;
videoItem.comments_count = filteredComments.length;
}
// 更新数据
this.active_comments = filteredComments;
// this.video_data_list = this.video_data_list;
this.$set(this, 'video_data_list', this.video_data_list)
}
} catch (error) {
console.error('delete_comment_handle error:', error);
}
},
// 关闭举报弹窗
popup_report_close_event() {
try {
// this.$refs.popupReportRef.close();
this.current_main_index = 0;
this.current_sub_index = 0;
} catch (error) {
console.error('popup_report_close_event error:', error);
}
},
// 直接选择主原因(用于一行显示的点击)
select_main_reason(e) {
try {
const index = e?.detail?.value || 0;
const main_index = parseInt(index);
this.current_main_index = main_index;
this.current_sub_index = 0; // 默认选中第一个子类型
} catch (error) {
console.error('select_main_reason error:', error);
}
},
// 直接选择子类型(用于一行显示的点击)
select_sub_reason(e) {
try {
const index = e?.detail?.value || 0;
const sub_index = parseInt(index);
this.current_sub_index = sub_index;
} catch (error) {
console.error('select_sub_reason error:', error);
}
},
/*
* 提交举报
*/
submit_report() {
try {
// 获取选中的举报原因和具体类型
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(t('common_sub_error_retry_tips'));
}
}
}
});
} catch (error) {
console.error('submit_report error:', error);
}
},
// 键盘显示时,切换输入框
add_comment() {
try {
//#ifndef H5
this.active_dropdown_id = null;
this.handle_comment_input_value();
this.active_dropdown_id = null;
//#endif
} catch (error) {
console.error('add_comment error:', error);
}
},
/**
* 键盘高度变化监听处理
* @param {Object} res - 键盘高度变化事件对象
*/
listener(res) {
try {
if (res.height > 0) {
this.listener_height = res.height - 1;
} else {
this.listener_height = 0;
}
} catch (error) {
console.error('listener error:', error);
}
},
/**
* 绑定键盘高度变化监听事件
*/
bind_keyboard_listener() {
try {
uni.onKeyboardHeightChange(this.listener);
} catch (error) {
console.error('bind_keyboard_listener error:', error);
}
},
/**
* 解绑键盘高度变化监听事件
*/
unbind_keyboard_listener() {
try {
uni.offKeyboardHeightChange(this.listener);
} catch (error) {
console.error('unbind_keyboard_listener error:', error);
}
}
}
};
</script>
<style lang="scss" scoped>
@import './detail-nvue.css';
</style>