app里video层级最高导致全屏视频上的操作的东西显示不出来的问题

为什么出现这个问题?

UniApp 在 App 端渲染页面时,用的是一个 原生 WebView(当做浏览器看待,手机系统来展示html界面) + 原生组件混合层

  • 页面(HTML、Vue 组件)在 WebView 层
  • <video><map><canvas> 等是 原生控件层
  • 原生层始终盖在 WebView 上面;
  • CSS 的 z-indexposition: fixedoverflow 对原生控件层 完全无效
  • 不管你怎么用 z-index 调整,只要视频是原生控件,它永远在最上面。

需求效果

解决方法

使用subNVue原子窗体开发
uni-app subNVue 原生子窗体开发指南 - DCloud问答
pages.json 页面路由 | uni-app官网(ask.dcloud.net.cn/article/359...)
subNVuevue 页面的子窗体,它不是全屏页面,就是用于解决 vue 页面中的层级覆盖和原生界面自定义用的。它也不是组件,就是一个原生子窗体

1. 创建主页面和操作栏页面

项目结构

bash 复制代码
pages/index/
├── index.vue          # 主页面(视频全屏播放层)
└── subNVue/
    ├── overlay.nvue   # 悬浮UI层(顶部导航 + 底部操作栏 + 用户信息)
    └── comment.nvue   # 评论弹出层(评论列表和交互)

2. pages.json 配置

json 复制代码
 {
  	"path" : "pages/index/index",
    "style": {
      "navigationStyle": "custom", 
      "app-plus": {
        "subNVues": [
          {
            "id": "overlay",
            "path": "pages/index/subnvue/overlay",
            "style": {
              "position": "absolute",
              "top": 0,
              "left": 0,
              "width": "100%",
              "height": "100%",
              "background": "transparent"
            }
          },
          {
            "id": "commentPopup",
            "path": "pages/index/subnvue/comment",
            "style": {
              "position": "dock",
              "dock": "bottom",
              "width": "100%",
              "height": "900px",
              "background": "transparent"
            }
          }
        ]
      }
  	}
  }

3. 主页面 index.vue

只保留视频和顶部导航

vue 复制代码
  <template>
	<view class="video-page">
		<!-- 视频全屏播放层 -->
		<video 
			class="video-bg" 
			src="/static/video.mp4"
			autoplay 
			loop 
			:show-fullscreen-btn="false"
			:show-center-play-btn="false"
			:controls="false"
			enable-play-gesture
			objectFit="cover"
		></video>
	</view>
</template>

<script setup>
import { onReady } from '@dcloudio/uni-app'

let overlaySubNVue = null
let commentSubNVue = null

onReady(() => {
	// #ifdef APP-PLUS
	// 获取并显示overlay层
	overlaySubNVue = uni.getSubNVueById('overlay')
	if (overlaySubNVue) {
		overlaySubNVue.show('none')
		console.log('✅ overlay层已显示')
	}
	
	// 获取comment层
	commentSubNVue = uni.getSubNVueById('commentPopup')
	if (commentSubNVue) {
		// 先显示再立即隐藏,确保层初始化
		commentSubNVue.show('none')
		setTimeout(() => {
			commentSubNVue.hide('none')
			console.log('✅ comment层已初始化并隐藏')
		}, 50)
	}
	// #endif
})
</script>

<style scoped>
.video-page {
	width: 100%;
	height: 100vh;
	background:  rgba(0, 0, 0, 0.9);
}

.video-bg {
	width: 100%;
	height: 90%;
}
</style>
  

4. 子窗体

4.1 导航栏和底部内容 overlay.nvue

vue 复制代码
<template>
  <view class="overlay-container">
    <!-- 顶部导航栏 -->
    <view class="top-nav">
      <view class="back-btn" @click="goBack">
        <image src="/static/video/Frame@2x.png" class="back-icon"></image>
      </view>
    </view>

    <!-- 底部内容容器 -->
    <view class="bottom-wrapper">
      <!-- 用户和描述区域 -->
      <view class="content-area">
        <!-- 用户信息 -->
        <view class="user-row">
          <image :src="postData.userAvatar" class="user-avatar" @click="goToUserProfile"></image>
          <text class="user-name" @click="goToUserProfile">{{ postData.username }}</text>
          <view class="btn-follow" :class="{ 'followed': postData.isFollowed }" @click="handleFollow">
            <image 
              v-if="!postData.isFollowed"
              src="/static/video/Frame 2033196032@2x.png" 
              class="btn-follow-img"
            ></image>
            <text v-else class="btn-follow-text">已关注</text>
          </view>
        </view>

        <!-- 描述文字 -->
        <text class="desc-text">{{ postData.content }}</text>
      </view>
    </view>

    <!-- 底部操作栏(移到最外层) -->
    <view class="bottom-action">
      <view class="input" @click="handleComment">
        <text class="input-placeholder">说点什么吧~</text>
      </view>
      
      <view class="actions">
        <view class="action" @click="handleLike">
          <image 
            :src="postData.isLiked ? '/static/video/喜欢 red@2x.png' : '/static/video/喜欢 (4) 1@2x.png'" 
            class="action-icon"
          ></image>
          <text class="action-num">{{ postData.likeCount }}</text>
        </view>

        <view class="action" @click="handleCollect">
          <image 
            :src="postData.isCollected ? '/static/home/收藏 (5) 1@2x.png' : '/static/home/收藏.png'" 
            class="action-icon"
          ></image>
          <text class="action-num">{{ postData.collectCount }}</text>
        </view>
        
        <view class="action" @click="handleComment">
          <image src="/static/video/评论 (1) 1@2x.png" class="action-icon"></image>
          <text class="action-num">{{ postData.commentCount }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, reactive } from 'vue'

// 容器高度
const containerHeight = ref(500)

// 文章数据
const postData = reactive({
  userAvatar: '/static/video/Ellipse 216@2x.png',
  username: '栗子',
  isFollowed: false,
  content: '早上出来散步,湖边那排柳树都发芽了!看着真清爽,我家小区的树还没冒绿呢。',
  likeCount: 145,
  collectCount: 86,
  commentCount: 76,
  isLiked: false,
  isCollected: false
})

// 返回
const goBack = () => {
  uni.navigateBack()
}

// 跳转个人主页
const goToUserProfile = () => {
  uni.navigateTo({
    url: '/pages/home/home'
  })
}

// 关注/取消关注
const handleFollow = () => {
  postData.isFollowed = !postData.isFollowed
  const title = postData.isFollowed ? '关注成功' : '已取消关注'
  uni.showToast({ title, icon: 'none' })
}

// 点赞
const handleLike = () => {
  postData.isLiked = !postData.isLiked
  postData.likeCount += postData.isLiked ? 1 : -1
}

// 收藏
const handleCollect = () => {
  postData.isCollected = !postData.isCollected
  postData.collectCount += postData.isCollected ? 1 : -1
  
  if (postData.isCollected) {
    // 收藏成功,显示短时间提示
    uni.showToast({ 
      title: '收藏成功', 
      icon: 'none',
      duration: 1500
    })
  } else {
    // 取消收藏,显示长时间提示(不自动消失需要手动关闭)
    uni.showToast({ 
      title: '取消收藏', 
      icon: 'none',
      duration: 1000 // 1秒后自动消失
    })
  }
}

// 打开评论弹窗
const handleComment = () => {
  // #ifdef APP-PLUS
  // 1. 隐藏overlay层
  const overlaySubNVue = uni.getSubNVueById('overlay')
  if (overlaySubNVue) {
    overlaySubNVue.hide()
    console.log('✅ overlay层已隐藏')
  }
  
  // 2. 显示评论弹窗
  const commentSubNVue = uni.getSubNVueById('commentPopup')
  if (commentSubNVue) {
    commentSubNVue.show('slide-in-bottom', 300)
    console.log('✅ 评论弹窗已打开')
  } else {
    console.error('❌ 未找到评论弹窗')
  }
  // #endif
}
</script>

<style>
.overlay-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 750rpx;
  height: 1624rpx;
  flex: 1;
}

/* 顶部导航 */
.top-nav {
  position: absolute;
  top: 88rpx;
  left: 32rpx;
  z-index: 100;
}

.back-btn {
  width: 60rpx;
  height: 60rpx;
  justify-content: center;
  align-items: center;
  flex-direction: row;
}

.back-icon {
  width: 64rpx;
  height: 64rpx;
}

/* ==================== 底部容器 ==================== */
.bottom-wrapper {
  position: absolute;
  bottom: 130rpx;
  left: 0;
  width: 750rpx;
  background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.6) 30%, rgba(0,0,0,0.85) 100%);
}

/* ==================== 内容区域 ==================== */
.content-area {
  padding: 40rpx 32rpx 30rpx 32rpx;
}

/* 用户信息行 */
.user-row {
  flex-direction: row;
  align-items: center;
  margin-bottom: 20rpx;
}

.user-avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 40rpx;
  border-width: 4rpx;
  border-color: #FFFFFF;
}

.user-name {
  margin-left: 20rpx;
  font-size: 36rpx;
  color: #FFFFFF;
  font-weight: 500;
}

.btn-follow {
  margin-left: 24rpx;
  width: 120rpx;
  height: 60rpx;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  border-radius: 30rpx;
}

.btn-follow.followed {
  background-color: rgba(255, 255, 255, 0.3);
  border-width: 2rpx;
  border-color: #FFFFFF;
}

.btn-follow-img {
  width: 120rpx;
  height: 60rpx;
}

.btn-follow-text {
  font-size: 26rpx;
  color: #FFFFFF;
  font-weight: 400;
}

/* 描述文字 */
.desc-text {
  font-size: 32rpx;
  line-height: 48rpx;
  color: #FFFFFF;
}

/* ==================== 底部操作栏 ==================== */
.bottom-action {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 750rpx;
  flex-direction: row;
  align-items: center;
  padding: 20rpx 32rpx 30rpx 32rpx;
  background-color:transparent;
  z-index: 10;
}

/* 输入框 */
.input {
  flex: 1;
  height: 70rpx;
  background-color: rgba(26, 26, 26, 0.8);
  border-radius: 35rpx;
  padding-left: 28rpx;
  padding-right: 28rpx;
  margin-right: 20rpx;
  justify-content: center;
}

.input-placeholder {
  font-size: 28rpx;
  color: #999999;
}

/* 操作按钮组 */
.actions {
  flex-direction: row;
  align-items: center;
}

/* 单个操作 */
.action {
  flex-direction: row;
  align-items: center;
  margin-left: 20rpx;
}

.action-icon {
  width: 50rpx;
  height: 50rpx;
}

.action-num {
  margin-left: 10rpx;
  font-size: 26rpx;
  color: #FFFFFF;
}
</style>

4.2 评论弹窗内容 `comment.nvue

nvue 复制代码
<template>
  <view class="comment-popup">
    <!-- 遮罩层 -->
    <view class="mask" @click="closePopup"></view>

    <!-- 评论内容区 -->
    <view class="comment-content">
      <!-- 评论头部 -->
      <view class="comment-header">
        <text class="comment-title">全部评论 {{ commentList.length }}</text>
        <view class="close-btn" @click="closePopup">
          <text class="close-icon">×</text>
        </view>
      </view>

      <!-- 评论列表 -->
      <list class="comment-list" @loadmore="loadMoreComments">
        <cell v-for="(comment, index) in commentList" :key="comment.id">
          <view class="comment-item">
            <!-- 主评论 -->
            <view class="comment-main" @click="handleReply(comment)">
              <image :src="comment.avatar" class="comment-avatar"></image>
              <view class="comment-right">
                <view class="comment-user-info">
                  <view class="comment-user-left">
                    <text class="comment-username" :class="{ 'vip-username': comment.isVip }">{{ comment.username }}</text>
                    <image v-if="comment.isVip" src="/static/video/Group 2033195212@2x.png" class="vip-icon"></image>
                  </view>
                  <view class="comment-like" @click.stop="handleCommentLike(comment)">
                    <text class="comment-like-count">{{ comment.likeCount }}</text>
                    <image 
                      :src="comment.isLiked ? '/static/video/点赞 (3) 2@2x.png' : '/static/video/点赞 (3) 1@2x.png'" 
                      class="like-extra-icon"
                    ></image>
                  </view>
                </view>
                <text class="comment-text">{{ comment.content }}</text>
                <view class="comment-footer">
                  <text class="comment-time">{{ comment.time }}</text>
                  <text class="reply-btn">回复</text>
                </view>

                <!-- 回复列表 -->
                <view class="reply-wrapper" v-if="comment.replies && comment.replies.length > 0" @click.stop>
                  <view class="reply-list" :class="{ 'reply-list-expanded': comment.showAll }">
                    <view class="reply-item" v-for="(reply, rIndex) in comment.showReplies" :key="reply.id" @click="handleReply(reply)">
                      <image :src="reply.avatar" class="reply-avatar"></image>
                      <view class="reply-right">
                        <view class="reply-user-info">
                          <view class="reply-user-left">
                            <text class="reply-username" :class="{ 'vip-username': reply.isVip }">{{ reply.username }}</text>
                            <text class="reply-arrow" v-if="reply.replyToUsername">回复</text>
                            <text class="reply-to-username" v-if="reply.replyToUsername" :class="{ 'vip-username': reply.replyToIsVip }">{{ reply.replyToUsername }}</text>
                            <image v-if="reply.isVip" src="/static/images/huangguan.png" class="vip-icon-small"></image>
                          </view>
                          <view class="reply-like" @click.stop="handleReplyLike(reply)">
                            <text class="like-count">{{ reply.likeCount }}</text>
                            <image 
                              :src="reply.isLiked ? '/static/video/点赞 (3) 2@2x.png' : '/static/video/点赞 (3) 1@2x.png'" 
                              class="like-extra-icon-small"
                            ></image>
                          </view>
                        </view>
                        <text class="reply-text">{{ reply.content }}</text>
                        <view class="reply-footer">
                          <text class="reply-time">{{ reply.time }}</text>
                          <text class="reply-btn">回复</text>
                        </view>
                      </view>
                    </view>
                  </view>

                  <!-- 展开/收起更多回复 -->
                  <view class="expand-replies" v-if="comment.replies.length > 1" @click.stop="toggleReplies(comment)">
                    <text class="expand-line">------------------</text>
                    <text class="expand-text">{{ comment.showAll ? '收起回复' : '展开' + (comment.replies.length - 1) + '条回复' }}</text>
                    <image src="/static/video/下  拉 1@2x.png" class="expand-arrow" :class="{ 'arrow-up': comment.showAll }"></image>
                  </view>
                </view>
              </view>
            </view>
          </view>
        </cell>

        <!-- 加载状态 -->
        <cell>
          <view class="load-more">
            <text class="load-more-text" v-if="loadingMore">加载中...</text>
            <text class="load-more-text" v-else-if="noMoreData">没有更多了</text>
          </view>
        </cell>
      </list>

      <!-- 底部输入区域 -->
      <view class="bottom-input-area">

        <!-- 评论输入框 -->
        <view class="comment-input-wrapper">
          <input 
            class="comment-input" 
            :value="inputComment"
            :placeholder="replyPlaceholder"
            placeholder-style="color: #999999"
            @input="onInput"
            @confirm="submitComment"
          />
          <view class="send-btn" :class="{ 'active': inputComment.trim() }" @click="submitComment">
            <text class="send-text">发送</text>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'

// 加载状态
const loadingMore = ref(false)
const noMoreData = ref(false)
const currentPage = ref(1)

// 评论输入
const inputComment = ref('')
const replyingTo = ref(null)
const replyPlaceholder = ref('说点什么...')

// 评论列表
const commentList = reactive([
  {
    id: 1,
    avatar: '/static/video/Ellipse 2206@2x.png',
    username: '星子落满襟',
    isVip: true,
    content: '岁月静好',
    time: '50分钟前',
    likeCount: 16,
    isLiked: true,
    showAll: false,
    replies: [
      {
        id: 11,
        avatar: '/static/video/Ellipse 2207@2x.png',
        username: '风卷云舒时',
        isVip: false,
        content: '+1',
        time: '5小时前',
        likeCount: 2,
        isLiked: false,
        replyToUsername: '星子落满襟',
        replyToIsVip: true
      },
      {
        id: 12,
        avatar: '/static/images/myInsterest/Ellipse 217.png',
        username: '回复用户2',
        isVip: false,
        content: '同意',
        time: '4小时前',
        likeCount: 1,
        isLiked: false,
        replyToUsername: '风卷云舒时',
        replyToIsVip: false
      }
    ],
    showReplies: []
  },
  {
    id: 2,
    avatar: '/static/video/Ellipse 2208@2x.png',
    username: '巷口路灯下',
    isVip: false,
    content: '这天气太适合出门了',
    time: '1天前',
    likeCount: 15,
    isLiked: false,
    showAll: false,
    replies: [],
    showReplies: []
  },
  {
    id: 3,
    avatar: '/static/video/Ellipse 2207@2x.png',
    username: '风卷云舒时',
    isVip: true,
    content: '很棒的分享',
    time: '2天前',
    likeCount: 3,
    isLiked: false,
    showAll: false,
    replies: [],
    showReplies: []
  }
])

// 输入框输入
const onInput = (e) => {
  inputComment.value = e.detail.value
}

// 取消回复
const cancelReply = () => {
  replyingTo.value = null
  replyPlaceholder.value = '说点什么...'
  inputComment.value = ''
}

// 关闭弹窗
const closePopup = () => {
  // 清空输入和回复状态
  inputComment.value = ''
  replyingTo.value = null
  replyPlaceholder.value = '说点什么...'
  
  // #ifdef APP-PLUS
  // 1. 隐藏评论弹窗
  const wv = plus.webview.currentWebview()
  if (wv) {
    wv.hide('slide-out-bottom', 300)
    console.log('✅ 评论弹窗已关闭')
  }
  
  // 2. 延迟显示overlay层
  setTimeout(() => {
    const overlaySubNVue = uni.getSubNVueById('overlay')
    if (overlaySubNVue) {
      overlaySubNVue.show('none')
      console.log('✅ overlay层已显示')
    }
  }, 100)
  // #endif
}

// 提交评论或回复
const submitComment = () => {
  if (!inputComment.value.trim()) {
    uni.showToast({ title: '请输入内容', icon: 'none' })
    return
  }

  if (replyingTo.value) {
    // 提交回复
    const comment = commentList.find(c =>
      c.id === replyingTo.value.id ||
      (c.replies && c.replies.some(r => r.id === replyingTo.value.id))
    )

    if (comment) {
      const newReply = {
        id: Date.now(),
        avatar: '/static/video/Ellipse 216@2x.png',
        username: '我',
        isVip: false,
        content: inputComment.value,
        time: '刚刚',
        likeCount: 0,
        isLiked: false,
        replyToUsername: replyingTo.value.username,
        replyToIsVip: replyingTo.value.isVip || false
      }

      if (!comment.replies) {
        comment.replies = []
      }
      comment.replies.push(newReply)

      // 更新显示的回复
      if (!comment.showAll) {
        comment.showReplies = [comment.replies[0]]
      } else {
        comment.showReplies = comment.replies
      }

      uni.showToast({ title: '回复成功', icon: 'success' })
    }
  } else {
    // 提交评论
    const newComment = {
      id: Date.now(),
      avatar: '/static/video/Ellipse 216@2x.png',
      username: '我',
      isVip: false,
      content: inputComment.value,
      time: '刚刚',
      likeCount: 0,
      isLiked: false,
      showAll: false,
      replies: [],
      showReplies: []
    }

    commentList.unshift(newComment)
    uni.showToast({ title: '评论成功', icon: 'success' })
  }

  // 清空输入框和回复状态
  inputComment.value = ''
  replyingTo.value = null
  replyPlaceholder.value = '说点什么...'
}

// 展开/收起回复
const toggleReplies = (comment) => {
  if (comment.showAll) {
    comment.showAll = false
    comment.showReplies = [comment.replies[0]]
  } else {
    comment.showAll = true
    comment.showReplies = comment.replies
  }
}

// 评论点赞
const handleCommentLike = (comment) => {
  comment.isLiked = !comment.isLiked
  comment.likeCount += comment.isLiked ? 1 : -1
}

// 回复点赞
const handleReplyLike = (reply) => {
  reply.isLiked = !reply.isLiked
  reply.likeCount += reply.isLiked ? 1 : -1
}

// 回复评论
const handleReply = (item) => {
  replyingTo.value = item
  replyPlaceholder.value = `回复 ${item.username}:`
  
  uni.showToast({
    title: `回复 ${item.username}`,
    icon: 'none',
    duration: 1000
  })
}

// 加载更多评论
const loadMoreComments = () => {
  if (loadingMore.value || noMoreData.value) {
    return
  }

  loadingMore.value = true

  setTimeout(() => {
    const newComment = {
      id: commentList.length + 1,
      avatar: '/static/video/Ellipse 2207@2x.png',
      username: '新用户' + (commentList.length + 1),
      isVip: false,
      content: '这是新加载的评论内容',
      time: '刚刚',
      likeCount: Math.floor(Math.random() * 20),
      isLiked: false,
      showAll: false,
      replies: [],
      showReplies: []
    }

    commentList.push(newComment)
    currentPage.value++

    if (currentPage.value >= 5) {
      noMoreData.value = true
    }

    loadingMore.value = false
  }, 1000)
}

// 组件挂载
onMounted(() => {
  // 初始化评论显示的回复
  commentList.forEach(comment => {
    if (comment.replies && comment.replies.length > 0) {
      comment.showReplies = [comment.replies[0]]
    }
  })
})
</script>

<style>
.comment-popup {
  position: absolute;
  top: 0;
  left: 0;
  width: 750rpx;
  height: 1624rpx;
  justify-content: flex-end;
}

.mask {
  position: absolute;
  top: 0;
  left: 0;
  width: 750rpx;
  height: 1624rpx;
  background-color: rgba(0, 0, 0, 0.5);
}

.comment-content {
  width: 750rpx;
  height: 900rpx;
  background-color: #FFFFFF;
  border-top-left-radius: 32rpx;
  border-top-right-radius: 32rpx;
  overflow: hidden;
  flex-direction: column;
}

/* 评论头部 */
.comment-header {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  padding: 32rpx 32rpx 16rpx;
  height: 88rpx;
}


/* 评论输入框 */
.comment-input-wrapper {
  flex-direction: row;
  align-items: center;
  padding: 16rpx 32rpx 20rpx 32rpx;
  background-color: #FFFFFF;
}

.comment-input {
  flex: 1;
  height: 70rpx;
  background-color: #F5F5F5;
  border-radius: 35rpx;
  padding-left: 28rpx;
  padding-right: 28rpx;
  font-size: 28rpx;
  color: #333333;
  margin-right: 16rpx;
}

.send-btn {
  width: 120rpx;
  height: 60rpx;
  background-color: #E5E5E5;
  border-radius: 30rpx;
  justify-content: center;
  align-items: center;
}

.send-btn.active {
  background-color: #7E51FF;
}

.send-text {
  font-size: 28rpx;
  color: #fff;
}

.send-btn.active .send-text {
  color: #FFFFFF;
}

.comment-title {
  font-size: 32rpx;
  font-weight: 500;
  color: #333333;
}

.close-btn {
  width: 56rpx;
  height: 56rpx;
  justify-content: center;
  align-items: center;
  flex-direction: row;
}

.close-icon {
  font-size: 56rpx;
  color: #999999;
  line-height: 56rpx;
}

/* 评论列表 */
.comment-list {
  flex: 1;
  height: 0;
  padding-left: 32rpx;
  padding-right: 32rpx;
  padding-bottom: 0rpx;
}

/* 底部输入区域 */
.bottom-input-area {
  width: 750rpx;
  background-color: #FFFFFF;
  border-top-width: 1rpx;
  border-top-color: #F0F0F0;
}

.comment-item {
  padding-top: 24rpx;
  padding-bottom: 24rpx;
}

.comment-main {
  flex-direction: row;
}

.comment-avatar {
  width: 72rpx;
  height: 72rpx;
  border-radius: 36rpx;
}

.comment-right {
  flex: 1;
  margin-left: 20rpx;
}

.comment-user-info {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12rpx;
}

.comment-user-left {
  flex-direction: row;
  align-items: center;
}

.comment-username {
  font-size: 28rpx;
  font-weight: 500;
  color: #888888;
}

.vip-username {
  color: #7E51FF;
}

.vip-icon {
  width: 32rpx;
  height: 32rpx;
  margin-left: 8rpx;
}

.comment-text {
  font-size: 30rpx;
  line-height: 44rpx;
  color: #333333;
  margin-bottom: 12rpx;
}

.comment-footer {
  flex-direction: row;
  align-items: center;
}

.comment-time {
  font-size: 24rpx;
  color: #888888;
}

.reply-btn {
  font-size: 28rpx;
  color: #333333;
  margin-left: 12rpx;
}

.comment-like {
  flex-direction: row;
  align-items: center;
  padding-right: 37rpx;
}

.comment-like-count {
  font-size: 24rpx;
  color: #999999;
  margin-right: 2rpx;
}

.like-extra-icon {
  width: 45rpx;
  height: 45rpx;
  margin-left: 8rpx;
  margin-right: 10rpx;
}

/* 回复列表容器 */
.reply-wrapper {
  margin-top: 24rpx;
  margin-bottom: 0rpx;
}

.reply-list {
  height: 200rpx;
  overflow: hidden;
}

.reply-list-expanded {
  height: 600rpx;
}

.reply-item {
  flex-direction: row;
  margin-bottom: 24rpx;
}

.reply-avatar {
  width: 56rpx;
  height: 56rpx;
  border-radius: 28rpx;
}

.reply-right {
  flex: 1;
  margin-left: 16rpx;
}

.reply-user-info {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8rpx;
}

.reply-user-left {
  flex-direction: row;
  align-items: center;
}

.reply-username {
  font-size: 26rpx;
  font-weight: 500;
  color: #888888;
}

.reply-arrow {
  font-size: 24rpx;
  color: #CCCCCC;
  margin-left: 8rpx;
  margin-right: 8rpx;
}

.reply-to-username {
  font-size: 26rpx;
  font-weight: 500;
  color: #888888;
}

.vip-icon-small {
  width: 28rpx;
  height: 28rpx;
  margin-left: 6rpx;
}

.reply-text {
  font-size: 28rpx;
  line-height: 40rpx;
  color: #333333;
  margin-bottom: 8rpx;
}

.reply-footer {
  flex-direction: row;
  align-items: center;
}

.reply-time {
  font-size: 22rpx;
  color: #999999;
}

.reply-like {
  flex-direction: row;
  align-items: center;
  padding-right: 40rpx;
}

.like-count {
  font-size: 24rpx;
  color: #999999;
  margin-right: 6rpx;
}

.like-extra-icon-small {
  width: 45rpx;
  height: 45rpx;
  margin-left: 8rpx;
  margin-right: 10rpx;
}

/* 展开更多回复 */
.expand-replies {
  flex-direction: row;
  align-items: center;
  padding-top: 0rpx;
  padding-bottom: 16rpx;
  margin-top: 0rpx;
}

.expand-line {
  font-size: 20rpx;
  color: #E5E5E5;
  margin-right: 12rpx;
}

.expand-text {
  font-size: 26rpx;
  color: #4A6481;
  margin-right: 8rpx;
}

.expand-arrow {
  width: 24rpx;
  height: 24rpx;
}

.arrow-up {
  transform: rotate(180deg);
}

/* 加载更多 */
.load-more {
  padding-top: 32rpx;
  padding-bottom: 32rpx;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

.load-more-text {
  font-size: 24rpx;
  color: #999999;
}
</style>

5.总结

  1. 使用subNVue 将页面分为 video 以及 遮罩区域等
  2. 配置pages.json
  3. 代码拆解
  4. 注意nvue的css兼容问题 nvue默认flex布局且默认竖直排列
相关推荐
weixin_445476683 小时前
Vue+redis全局添加水印解决方案
前端·vue.js·redis
lecepin3 小时前
AI Coding 资讯 2025-10-29
前端·后端·面试
余道各努力,千里自同风3 小时前
小程序中获取元素节点
前端·小程序
PineappleCoder3 小时前
大模型也栽跟头的 Promise 题!来挑战一下?
前端·面试·promise
非凡ghost3 小时前
MousePlus(鼠标增强工具) 中文绿色版
前端·windows·计算机外设·软件需求
焚 城3 小时前
EXCEL(带图)转html【uni版】
前端·html·excel
我家媳妇儿萌哒哒3 小时前
Vue2 elementUI年份区间选择组件
前端·javascript·elementui
笨笨狗吞噬者3 小时前
【uniapp】小程序体积优化,分包异步化
前端·微信小程序·uni-app
该用户已不存在3 小时前
Golang 上传文件到 MinIO?别瞎折腾了,这 5 个库拿去用
前端·后端·go