更新记录

1.0(2026-09-09)

1.0

  • 自动识别数组中的图片视频(按 URL 后缀判断);
  • 根据媒体数量自动选择布局(单图、双图、三列九宫格);
  • 图片点击后调用 uni.previewImage 全屏预览,且预览列表中自动过滤掉视频
  • 视频点击后弹出全屏遮罩 + 内置视频播放器(PostVideo 组件)进行播放;
  • 媒体数量超过 8 个时,在最后一项上显示 +N 角标。

平台兼容性

uni-app(5.24)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- - - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - -

Ryan-Post 媒体九宫格组件 使用说明

一、组件简介

Ryan-Post 是一个用于 uni-app 的媒体内容展示组件,常用于帖子、动态、朋友圈等场景中图片与视频混合列表的展示。

组件接收一个媒体 URL 数组,自动完成以下工作:

  • 自动识别数组中的图片视频(按 URL 后缀判断);

  • 根据媒体数量自动选择布局(单图、双图、三列九宫格);

  • 图片点击后调用 uni.previewImage 全屏预览,且预览列表中自动过滤掉视频

  • 视频点击后弹出全屏遮罩 + 内置视频播放器(PostVideo 组件)进行播放;

  • 媒体数量超过 8 个时,在最后一项上显示 +N 角标。

  • 组件路径:components/Ryan-Post/Ryan-Post.vue

  • 依赖组件:components/Ryan-Post/PostVideo.vue(视频播放弹层,需与 Ryan-Post.vue 放在同一目录)

  • 适用平台:uni-app(H5 / 小程序 / App,视频能力依赖各平台 video 组件与 uni.previewImage API)


二、文件结构

components/Ryan-Post/
├── Ryan-Post.vue   # 媒体九宫格主组件(本组件)
└── PostVideo.vue    # 视频播放弹层组件(Ryan-Post 内部自动引入,无需手动注册)

注意:Ryan-Post.vue 中通过 import PostVideo from "./PostVideo.vue" 引入依赖,迁移组件时两个文件必须一起复制,且保持同级目录。


三、引入与注册

在页面中按需引入即可(easycom 规范下放入 components/ 目录也可自动引入):

<script>
import Ryan-Post from '@/components/Ryan-Post/Ryan-Post.vue';

export default {
  components: {
    Ryan-Post
  }
};
</script>

四、基础用法

参考 pages/index/index.vue

<template>
  <view class="container">
    <view class="header">
      <text class="title">媒体查看器演示</text>
    </view>
    <!-- 引入媒体查看器组件 -->
    <Ryan-Post :images="mediaList" />
  </view>
</template>

<script>

export default {
  data() {
    return {
      mediaList: [
        'https://picsum.photos/400/300?random=1',
        'https://www.w3schools.com/html/mov_bbb.mp4',
        'https://picsum.photos/400/300?random=2',
        'https://picsum.photos/400/300?random=3',
        'https://picsum.photos/400/300?random=4',
        'https://picsum.photos/400/300?random=5',
        'https://picsum.photos/400/300?random=6'
      ]
    };
  }
};
</script>

组件只需要一个 images 属性,传入图片/视频 URL 混合数组即可,无需其他配置。


五、Props

属性名 类型 默认值 说明
images Array [] 媒体地址数组,元素为图片或视频的 URL 字符串。数组为空或不传时组件不渲染(v-if="images && images.length"

images 数组元素规则

  • 支持完整网络地址:以 http://https:// 开头的 URL 原样使用;
  • 支持本地/相对路径地址:原样返回(源码中拼接 BaseUrl 的逻辑已被注释,如需统一拼接域名可自行放开);
  • 图片与视频可以任意顺序混排,组件内部自动区分处理。

六、媒体类型识别

组件通过 URL 后缀名(不区分大小写)判断是否为视频,支持的视频后缀:

.mp4  .mov  .avi  .webm  .flv  .wmv  .mkv  .3gp
  • 命中上述后缀 → 按视频渲染(显示 video 元素 + 中央播放角标);
  • 其余地址 → 按图片渲染(以 background-image 方式填充)。

注意:判断逻辑为 url.toLowerCase().endsWith(ext),因此不带后缀的视频地址(如带签名参数的 CDN 地址 xxx.mp4?sign=...)无法识别。这类地址建议在传入前处理,或扩展 isVideo 方法。


七、布局规则

组件根据媒体数量自动套用不同样式类(getImageClass 方法):

媒体数量 样式类 单项尺寸 布局效果
1 个 single 宽 100%,高 180px 整行大图
2 个 double (100% - 6px) / 2,高 120px 两列等分
3 个及以上 grid (100% - 12px) / 3,高 80px 三列网格(九宫格)
恰好 4 个时的第 1 个 first-row (100% - 6px) / 2,高 80px 首项占半行,其余按三列排列

其他样式细节:

  • 容器使用 flex 换行布局,间距 gap: 6px
  • 每项圆角 8px,超出部分 overflow: hidden
  • 图片以 background-size: cover; background-position: center 裁剪填充;
  • 视频缩略图上显示灰色占位层(.post-image-zoom)与中央圆形播放角标(.video-badge)。

+N 角标

images.length > 8 时,会在最后一个媒体项上覆盖半透明黑色遮罩并显示 +{总数 - 8} 文字(例如 9 个媒体显示 +1)。

注意:当前实现中所有媒体项都会渲染,+N 只是覆盖在最后一项上的视觉提示,并不会真正截断只显示 8 个。


八、交互行为

点击媒体项触发 previewMedia(idx)

1. 点击图片

  • 自动过滤掉数组中的视频,只保留图片 URL;
  • 自动换算当前图片在纯图片列表中的序号;
  • 调用 uni.previewImage 打开系统图片预览,支持左右滑动浏览全部图片。
// 组件内部逻辑(节选)
const urls = this.images
  .filter(img => !this.isVideo(img))
  .map(img => this.getImageUrl(img));
const imageIndex = this.images
  .slice(0, idx)
  .filter(img => !this.isVideo(img)).length;
uni.previewImage({
  current: urls[imageIndex] || urls[0],
  urls: urls
});

2. 点击视频

  • 设置 showVideo = truevideoUrl = 当前视频地址
  • 页面出现全屏半透明黑色遮罩(.video-mask),并挂载 PostVideo 播放器;
  • 点击播放器右上角关闭按钮(或遮罩区域)触发 closeVideo,关闭弹层并清空视频地址。

视频封面(poster)

视频缩略图的封面取数组中第一张非视频图片getVideoPoster 方法);若数组中没有任何图片,则封面为空。


九、内置 PostVideo 组件说明

PostVideo.vue 为视频播放弹层,通常由 Ryan-Post 内部使用,一般不需要直接调用。如需单独使用,其 API 如下:

Props

属性名 类型 默认值 说明
showVideo Boolean false 是否显示播放器(为 false 时不渲染)
videoUrl String '' 视频地址(required: true
posterUrl String '' 视频封面图地址
showOverlay Boolean false 是否显示状态文字覆盖层(准备播放 / 播放中 / 已暂停 / 加载失败)
showCloseBtn Boolean true 是否显示右上角关闭按钮

Events

事件名 说明
close 点击关闭按钮时触发,父组件应据此隐藏播放器
play 视频开始播放时触发
pause 视频暂停时触发
error 视频加载失败时触发(组件内部同时会 uni.showToast 提示“视频加载失败”)

暴露方法(通过 ref 调用)

方法名 说明
play() 播放视频
pause() 暂停视频

播放器容器高 400rpx,视频使用 object-fit="contain" 完整显示,带圆角与阴影。


十、样式自定义

Ryan-Post.vue 的样式为 scoped,可通过深度选择器或调整以下类名覆盖样式:

类名 作用
.post-images 外层容器(flex 换行、间距)
.post-image.single / .double / .grid / .first-row 不同数量下的单项尺寸
.post-image-inner 图片背景层
.post-video 缩略图中的 video 元素
.video-badge 视频中央播放角标
.image-more / .more-text +N 遮罩与文字
.video-mask 视频弹层的全屏黑色遮罩

例如修改九宫格单项高度:

.post-images .post-image.grid {
  height: 100px;
}

十一、注意事项与最佳实践

  1. 两个组件文件必须一起迁移Ryan-Post.vue 依赖同目录下的 PostVideo.vue
  2. 视频识别依赖后缀名:带查询参数(如 ?token=xxx)或无后缀的视频地址可能识别失败,必要时改造 isVideo 方法(例如改为 includes('.mp4') 或由数据字段显式标记类型)。
  3. 播放角标图标:视频角标使用了 fa fa-play 类名(Font Awesome),若项目未引入 Font Awesome 图标字体,角标内可能不显示三角图标,可自行替换为 uni-app 图标或图片。
  4. 预览只包含图片uni.previewImageurls 已自动过滤视频,无需调用方处理。
  5. 视频封面复用第一张图片:混排时所有视频共用第一张图片作为封面,如需每个视频独立封面,需改造数据结构与 getVideoPoster 逻辑。
  6. +N 仅为视觉提示:超过 8 个媒体时组件仍会渲染全部项,如需“只显示 9 格”需在传入前对数组截断。
  7. 网络资源跨域/鉴权:小程序与 App 端播放网络视频需在平台后台配置合法域名(downloadFile / 视频域名白名单)。

十二、完整示例

<template>
  <view class="page">
    <!-- 纯图片 -->
    <Ryan-Post :images="photoList" />

    <!-- 图片 + 视频混排 -->
    <Ryan-Post :images="mixedList" />
  </view>
</template>

<script>

export default {
  data() {
    return {
      photoList: [
        'https://picsum.photos/400/300?random=1',
        'https://picsum.photos/400/300?random=2',
        'https://picsum.photos/400/300?random=3'
      ],
      mixedList: [
        'https://picsum.photos/400/300?random=10',
        'https://www.w3schools.com/html/mov_bbb.mp4',
        'https://picsum.photos/400/300?random=11'
      ]
    };
  }
};
</script>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。