更新记录
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.previewImageAPI)
二、文件结构
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 = true、videoUrl = 当前视频地址; - 页面出现全屏半透明黑色遮罩(
.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;
}
十一、注意事项与最佳实践
- 两个组件文件必须一起迁移:
Ryan-Post.vue依赖同目录下的PostVideo.vue。 - 视频识别依赖后缀名:带查询参数(如
?token=xxx)或无后缀的视频地址可能识别失败,必要时改造isVideo方法(例如改为includes('.mp4')或由数据字段显式标记类型)。 - 播放角标图标:视频角标使用了
fa fa-play类名(Font Awesome),若项目未引入 Font Awesome 图标字体,角标内可能不显示三角图标,可自行替换为 uni-app 图标或图片。 - 预览只包含图片:
uni.previewImage的urls已自动过滤视频,无需调用方处理。 - 视频封面复用第一张图片:混排时所有视频共用第一张图片作为封面,如需每个视频独立封面,需改造数据结构与
getVideoPoster逻辑。 +N仅为视觉提示:超过 8 个媒体时组件仍会渲染全部项,如需“只显示 9 格”需在传入前对数组截断。- 网络资源跨域/鉴权:小程序与 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>

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 0
赞赏 0
下载 12581294
赞赏 1949
赞赏
京公网安备:11010802035340号