Vue+萤石云EZUIKit.js实战:自定义直播流播放器与多视频管理
1. 从零开始为什么选择Vue和EZUIKit.js如果你正在开发一个需要集成视频直播功能的Web应用特别是像安防监控、在线教育或者直播后台这类需要同时管理多个视频流的项目那你大概率已经研究过各种方案了。市面上的播放器插件很多但一旦涉及到私有协议、低延迟直播流尤其是像海康、大华、萤石云这类安防设备的视频流选择就变得非常有限。我当初接手一个智慧园区项目需要在一个大屏上同时展示十几个摄像头的实时画面并且要能控制声音、截图、对讲第一个想到的就是萤石云的开放平台。但说实话一开始我也被它的文档“劝退”过就像很多开发者吐槽的那样文档写得比较“含蓄”很多细节需要自己摸索。不过经过几个项目的实战我发现Vue EZUIKit.js这个组合一旦摸清了门道其实是实现高度定制化视频播放和多视频管理非常高效、稳定的方案。简单来说EZUIKit.js是萤石云官方提供的Web端视频播放SDK它封装了复杂的流媒体协议解析和渲染逻辑让我们能通过简单的JavaScript API来播放萤石设备的直播和录像。而Vue作为目前最主流的前端框架之一其响应式数据和组件化开发模式与EZUIKit.js的动态创建、销毁实例的需求简直是天作之合。你可以把每个视频播放器封装成一个Vue组件通过数据驱动来动态生成和管理多个播放器实例状态管理起来非常清晰。这篇文章我就把自己踩过的坑、总结的最佳实践用最直白的方式分享给你目标是让你看完就能动手快速搭建起一个功能强大、界面自定义的视频播放管理系统。2. 环境搭建与EZUIKit.js的引入姿势万事开头难我们先搞定最基础的环境。这里假设你已经有一个Vue项目了无论是Vue 2还是Vue 3Composition API的写法会稍有不同但核心思路一致我们都需要先把EZUIKit.js引入进来。2.1 两种引入方式直接Script与NPM官方提供了两种引入方式各有优劣我两种都用过给你分析一下。方式一直接在index.html中引入CDN链接这是最快速、最直接的方法。你只需要在项目的public/index.html文件的head或body底部添加一行脚本标签。!DOCTYPE html html langen head meta charsetutf-8 meta http-equivX-UA-Compatible contentIEedge meta nameviewport contentwidthdevice-width,initial-scale1.0 title我的视频监控平台/title /head body div idapp/div !-- 引入 EZUIKit.js -- script srchttps://open.ys7.com/sdk/js/3.0/ezuikit.js/script /body /html优点简单粗暴全局可用在任何Vue组件里都能直接访问window.EZUIKit这个全局对象。缺点无法利用现代前端工程的打包优化如Tree Shaking版本管理不够灵活依赖全局变量。方式二通过NPM包安装推荐这是我更推荐的方式尤其对于中大型项目管理起来更规范。npm install ezuikit-js --save # 或者使用 yarn yarn add ezuikit-js安装完成后你需要在用到播放器的Vue组件中单独引入。这里有个关键点EZUIKit.js在初始化播放器时需要一个真实的DOM容器ID。在Vue的单文件组件中我们通常通过ref来获取DOM或者确保在mounted生命周期钩子之后再进行初始化因为那时DOM才真正渲染完成。template div !-- 为播放器准备一个容器id需唯一 -- div idmyPlayerContainer refplayerContainer/div /div /template script // 在Vue组件中引入 import EZUIKit from ezuikit-js; export default { name: MyPlayer, mounted() { // 确保DOM已挂载后再初始化播放器 this.initPlayer(); }, methods: { initPlayer() { // 注意这里的 id 参数传入的是上面 div 的 id 字符串不是 ref 对象 const player new EZUIKit.EZUIKitPlayer({ id: myPlayerContainer, // 对应DOM元素的id accessToken: 你的萤石云AccessToken, url: ezopen://open.ys7.com/设备序列号/1.live, width: 800, height: 450, template: pcLive, // 使用PC直播模板 }); this.playerInstance player; // 保存实例便于后续控制 } }, beforeDestroy() { // 组件销毁前务必销毁播放器实例释放资源 if (this.playerInstance) { this.playerInstance.stop(); // 某些版本可能需要调用destroy() // this.playerInstance.destroy(); } } } /script实测建议我强烈推荐使用NPM方式并结合ref来获取DOM元素。这样更符合Vue的响应式哲学也能更好地融入你的构建流程。记得在组件销毁时手动销毁播放器实例避免内存泄漏。2.2 获取关键信息AccessToken与播放地址玩转EZUIKit.js两个东西必不可少accessToken和url。这就像是打开视频大门的钥匙和地址。AccessToken这是调用萤石云开放平台API的凭证。你需要根据官方文档通过AppKey和AppSecret来获取。通常后端服务器会负责这个获取和刷新的过程因为AccessToken有过期时间前端只需要从后端接口请求一个有效的Token即可。千万不要把AppKey和Secret硬编码在前端代码里这极其不安全。播放地址url格式通常是ezopen://open.ys7.com/设备序列号/通道号.live。其中设备序列号就是你的摄像头设备身上的那一串号码。通道号对于单通道设备如大部分家用摄像头就是1。对于多通道NVR网络录像机可能是1,2,3... 代表不同的摄像头通道。.live代表直播流如果是回放录像则可能是.rec加上时间参数。一个常见的场景是前端通过API从后端获取到一个设备列表列表中包含了每个设备的accessToken和playUrl。这样前端就可以动态地循环这个列表为每个设备创建一个播放器实例。3. 核心实战打造你的自定义播放器官方提供了几个内置的播放器模板simple,standard,security,voice但说实话样式比较固定很难满足个性化需求。好在EZUIKit.js还提供了一个更强大的pcLive模板以及一个叫做themeData的自定义配置项这才是我们实现高度自定义的利器。3.1 破解“静音”难题与基础控件配置很多朋友第一个坑就是怎么默认关闭声音文档里说设置audio: 0或者autoplay: false但在pcLive或某些模板下你可能发现根本不生效。我当初也在这里折腾了好久。其实关键在于使用themeData来配置控件栏。当你使用了自定义主题配置后播放器的初始化逻辑会更完整静音设置也就生效了。我们先来看一个完整的、包含静音和常用控件的初始化配置// 在Vue组件的methods或setup中定义配置 getPlayerConfig() { // 自定义主题配置 - 这是核心 const themeData { header: { show: false, // 我通常隐藏顶部栏更简洁 backgroundColor: #000, }, footer: { color: #FFFFFF, activeColor: #1890FF, // 按钮激活时的颜色 backgroundColor: rgba(0, 0, 0, 0.5), // 半透明背景 btnList: [ { iconId: play, part: left, defaultActive: 1, // 1表示默认显示0表示隐藏 memo: 播放/暂停, isrender: 1 // 1表示渲染此按钮 }, { iconId: sound, // 声音按钮 part: left, defaultActive: 0, // 默认声音关闭按钮状态为“静音” memo: 声音开关, isrender: 1 }, { iconId: capturePicture, part: left, defaultActive: 1, memo: 截图, isrender: 1 }, { iconId: recordvideo, part: left, defaultActive: 0, // 根据需求决定是否显示录制按钮 memo: 录制, isrender: 1 }, { iconId: zoom, part: left, defaultActive: 0, memo: 电子放大, isrender: 1 }, { iconId: hd, part: right, defaultActive: 1, memo: 清晰度切换, isrender: 1 }, { iconId: expend, part: right, defaultActive: 1, memo: 全屏, isrender: 1 } ] } }; return { id: this.containerId, // 动态传入的容器ID accessToken: this.deviceInfo.token, url: this.deviceInfo.playUrl, width: 100%, // 可以设置为100%自适应容器宽度 height: 100%, template: pcLive, // 使用pcLive模板 audio: 0, // 重要配合themeData中的sound按钮实现默认静音 themeData: themeData, // 注入自定义主题 handleError: (error) { console.error(播放器错误:, error); // 这里可以更新组件状态显示错误信息给用户 }, handleSuccess: () { console.log(播放器初始化成功); } }; }关键点解析audio: 0这个参数必须和themeData.footer.btnList中sound按钮的defaultActive: 0配合使用。这样初始化后视频是静音状态并且底部控件栏的声音按钮显示为“开启声音”的图标。btnList里的iconId是固定的代表不同的功能。你可以通过设置isrender: 0来彻底隐藏某个按钮比如我不需要“云台控制”pantile和“语音对讲”talk就直接不配它们或者设isrender为0。part控制按钮在左(left)还是右(right)侧区域。3.2 深度自定义移除Logo与修改控件样式pcLive模板默认在顶部中央会显示一个“EZVIZ”的Logo在很多项目集成中客户都要求去掉。文档没明说但通过分析SDK和demo我发现可以通过themeData的header配置来实现。如果你想完全隐藏顶部栏包括Logo和设备信息就像上面代码那样设置header: { show: false }。如果你需要顶部栏但只想移除Logo可以这样配置header的btnListheader: { color: #1890ff, backgroundColor: #000, btnList: [ { iconId: deviceName, // 只显示设备名称 part: left, defaultActive: 1, memo: 设备名称, isrender: 1 }, // 不配置 logo 相关的 iconId它就不会被渲染 // 默认可能有的‘deviceID’, ‘cloudRec’, ‘rec’ 等都可以通过 isrender:0 控制 ] }至于控件样式比如颜色、背景都可以在themeData的header和footer对象里直接修改color、activeColor、backgroundColor等属性。这些颜色值支持CSS颜色格式比如十六进制、rgba。通过调整rgba的透明度可以做出非常好看的半透明效果这是我个人比较喜欢的风格不会完全遮挡视频内容。4. 进阶技巧多视频播放与动态管理单个播放器搞定后真正的挑战来了如何优雅地管理多个同时播放的视频比如一个9宫格、16宫格的监控墙。核心思想就是一个播放器实例对应一个DOM容器和一个设备流。4.1 动态创建与循环渲染我们不可能在模板里手动写几十个div。正确的做法是利用Vue的v-for循环结合动态的ref或id来生成容器并在mounted或数据更新后为每个容器初始化播放器。这里有一个我项目中常用的方案template div classvideo-wall !-- 根据设备列表动态生成容器 -- div v-for(device, index) in deviceList :keydevice.id classvideo-item div :idplayer-container-${device.id} classplayer-wrapper/div div classdevice-title{{ device.name }}/div /div /div /template script import EZUIKit from ezuikit-js; export default { data() { return { deviceList: [], // 从后端API获取的设备列表 playerInstances: new Map(), // 用Map来保存播放器实例key为设备ID }; }, mounted() { this.fetchDevicesAndInit(); }, methods: { async fetchDevicesAndInit() { // 1. 模拟从后端获取设备列表 const res await this.$api.getDeviceList(); this.deviceList res.data; // 2. 等待下一次DOM更新循环确保所有容器div都已渲染 this.$nextTick(() { // 3. 遍历设备列表为每个设备创建播放器 this.deviceList.forEach(device { this.initSinglePlayer(device); }); }); }, initSinglePlayer(device) { // 防止重复初始化 if (this.playerInstances.has(device.id)) { return; } const containerId player-container-${device.id}; const playerConfig { id: containerId, accessToken: device.accessToken, url: device.playUrl, width: 100%, height: 100%, template: pcLive, audio: 0, // 多视频同时播放默认静音是必须的否则声音会混杂 themeData: { /* ... 你的自定义主题 ... */ }, }; try { const player new EZUIKit.EZUIKitPlayer(playerConfig); // 将实例存储起来key为设备ID this.playerInstances.set(device.id, player); } catch (error) { console.error(初始化设备 ${device.name} 播放器失败:, error); // 可以在这里更新UI显示该视频格子的错误状态 } }, }, beforeDestroy() { // 组件销毁时清理所有播放器实例 this.playerInstances.forEach((player, deviceId) { player.stop(); // 根据需要调用 destroy // player.destroy(); }); this.playerInstances.clear(); } } /script style scoped .video-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px; } .video-item { position: relative; aspect-ratio: 16 / 9; /* 保持16:9比例 */ background-color: #000; } .player-wrapper { width: 100%; height: 100%; } .device-title { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.7); color: white; padding: 4px 8px; font-size: 12px; } /style要点说明使用$nextTick确保Vue已经将v-for循环生成的DOM元素渲染到页面上再执行初始化播放器的JS代码否则会找不到容器元素。使用Map存储实例比用普通对象更专业键值对管理更方便并且能保证键的顺序设备ID。默认静音多视频播放时一定要将每个播放器的audio设为0否则所有摄像头的声音会同时播放造成混乱。用户可以通过点击每个视频单独的控制栏声音按钮来开启/关闭特定视频的声音。错误处理初始化过程用try...catch包裹避免一个视频流失败导致整个页面脚本崩溃。资源释放在beforeDestroy生命周期中务必遍历销毁所有播放器实例这是防止内存泄漏的好习惯。4.2 性能优化与事件联动当视频数量增多比如超过9个性能就需要考虑了。EZUIKit.js每个实例都会创建视频解码和渲染上下文对浏览器负担不小。优化建议一分页/懒加载不要一次性加载所有视频。可以结合分页组件或者监听滚动事件只初始化出现在可视区域内的视频播放器。对于离开视口的播放器可以调用player.stop()暂停播放回来时再重新初始化或恢复。优化建议二降低清晰度在初始化配置中可以通过参数指定播放的流类型主码流、子码流。子码流分辨率低、码率小对带宽和CPU解码压力都更小。这通常需要在设备的萤石云后台进行子码流配置然后在播放URL中指定通道如1.hd.live主码流1.live子码流具体看设备支持。多画面同时显示时使用子码流是常见的做法。事件联动示例一键全屏某个视频假设我们想在每个视频的角落加一个“独占全屏”的按钮。我们需要在自定义themeData的footer的btnList里添加一个自定义按钮吗这比较复杂。一个更简单的思路是在视频容器外用Vue自己渲染一个按钮并调用播放器实例的全屏方法。// 在初始化播放器后保存实例引用 this.playerInstances.set(device.id, player); // 在Vue模板中为一个按钮绑定点击事件 methods: { handleFullscreen(deviceId) { const player this.playerInstances.get(deviceId); if (player player.fullScreen) { // 注意方法名可能是fullScreen或expend需查证 player.fullScreen(); // 调用实例的全屏方法 } } }同理你可以实现一键截图、一键开启所有声音等联动功能核心就是通过你保存的playerInstancesMap拿到具体的播放器实例然后调用SDK提供的方法如capturePictureopenSound等。这些方法通常可以在播放器实例的对象上找到具体需要查阅SDK源码或通过控制台打印实例对象来探索。5. 常见坑点与调试心得最后分享几个我踩过印象最深的坑希望能帮你节省时间。坑1跨域与HTTPS问题如果你的网站是HTTPS协议而加载的EZUIKit.js脚本或视频流地址是HTTP浏览器会因为混合内容Mixed Content策略而阻止。确保你的页面和所有资源都走HTTPS。另外萤石云的SDK和流地址通常都支持HTTPS直接用https://开头的CDN地址就行。坑2AccessToken过期AccessToken有效期通常只有几天。你需要设计一个刷新机制。一种做法是前端每次初始化播放器前都向后端请求一个最新的Token。更优的做法是后端在Token快过期时主动刷新并提供一个稳定的接口给前端获取当前有效Token。前端在播放器遇到handleError回调且错误码与Token相关时可以尝试重新获取Token并重启播放器。坑3template和themeData的兼容性不是所有template都完全支持themeData的所有配置。pcLive模板对themeData的支持是最全面的。如果你用的是simple或standard某些自定义配置可能无效。所以确定好设计稿后最好先用pcLive模板进行开发。坑4销毁与重建在Vue组件中如果设备列表发生动态变化比如切换了分组你需要销毁旧的、不在列表中的播放器并创建新的。一定要先调用player.stop()再将其从playerInstancesMap中移除最后将对应的DOM容器清空或移除。直接移除DOM而不销毁播放器实例可能会导致内存泄漏。调试时多打开浏览器的开发者工具F12Network面板查看JS SDK是否加载成功视频流m3u8或flv请求是否正常。Console面板关注EZUIKit.js打印的日志和错误信息它的错误提示有时能给出关键线索。Application面板 Storage检查是否有缓存的Token等信息。说到底和EZUIKit.js打交道一半靠文档一半靠实践和摸索。它功能强大能应付复杂的安防视频场景但需要你耐心地去理解和配置。希望这篇结合了Vue实战经验的分享能让你在开发自定义视频播放和多视频管理的道路上走得更顺畅。当你看到自己定制的监控墙成功运行所有视频流畅播放时那种成就感还是很足的。如果在实践中遇到新的问题不妨多看看官方GitHub仓库的Issues和Demo源码那里往往藏着解决方案。

相关新闻

【2026年最新600套毕设项目分享】基于SpringBoot的社区互助系统(14103)

【2026年最新600套毕设项目分享】基于SpringBoot的社区互助系统(14103)

有需要的同学,源代码和配套文档领取,加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码(前后端源代码SQL脚本)配套文档(LWPPT开题报告/任务书)远程调试控屏包运行一键启动项目&…

2026/7/2 21:27:54 阅读更多 →
SmolVLA效果实测报告:灰色占位图输入下语言指令驱动动作的鲁棒性验证

SmolVLA效果实测报告:灰色占位图输入下语言指令驱动动作的鲁棒性验证

SmolVLA效果实测报告:灰色占位图输入下语言指令驱动动作的鲁棒性验证 1. 项目背景与测试目的 SmolVLA是一个专门为经济实惠的机器人技术设计的紧凑型视觉-语言-动作模型。这个模型最大的特点是参数量只有约500M,却能够实现视觉感知、语言理解和动作生成…

2026/7/5 5:15:46 阅读更多 →
TQQQ实战避坑指南:为什么3倍杠杆ETF长期持有反而亏钱?

TQQQ实战避坑指南:为什么3倍杠杆ETF长期持有反而亏钱?

TQQQ实战避坑指南:为什么3倍杠杆ETF长期持有反而亏钱? 如果你在2021年底看着纳斯达克指数节节攀升,被FOMO情绪驱使,或者听信了某些“长期持有TQQQ就能获得三倍于QQQ回报”的简化论调,那么过去几年的市场震荡很可能给你…

2026/5/17 9:21:00 阅读更多 →

最新新闻

Docker run 命令 6 大核心参数实战:-v、-w、-e、-u、--rm、-it 组合解析

Docker run 命令 6 大核心参数实战:-v、-w、-e、-u、--rm、-it 组合解析

Docker Run 命令 6 大核心参数实战指南:-v、-w、-e、-u、--rm、-it 的组合艺术当你在终端输入docker run的那一刻,一个精密的容器化引擎便开始运作。但真正让这个简单的命令变得强大的,是那些看似不起眼的参数。本文将深入探讨六个最常用却常…

2026/7/6 2:05:46 阅读更多 →
3款轻量级骨架提取模型对比:MobilePose vs Lightweight OpenPose vs MoveNet,移动端实测 20+ FPS

3款轻量级骨架提取模型对比:MobilePose vs Lightweight OpenPose vs MoveNet,移动端实测 20+ FPS

3款轻量级骨架提取模型移动端实测:性能、精度与部署全解析在移动端和边缘计算设备上实现实时人体姿态估计一直是计算机视觉领域的难点。随着AI模型轻量化技术的进步,MobilePose、Lightweight OpenPose和MoveNet等模型让20FPS的实时骨架提取成为可能。本文…

2026/7/6 2:05:46 阅读更多 →
mRemoteNG免费远程连接管理器:3天从零到精通的完整教程

mRemoteNG免费远程连接管理器:3天从零到精通的完整教程

mRemoteNG免费远程连接管理器:3天从零到精通的完整教程 【免费下载链接】mRemoteNG mRemoteNG is the next generation of mRemote, open source, tabbed, multi-protocol, remote connections manager. 项目地址: https://gitcode.com/gh_mirrors/mr/mRemoteNG …

2026/7/6 2:03:45 阅读更多 →
抖店体验分怎么提升-4点8分实操方法-抖音电商2026规则落地

抖店体验分怎么提升-4点8分实操方法-抖音电商2026规则落地

抖店体验分怎么提升?提升到4.8全套实操方法|抖音电商2026规则落地 前言 2026抖音电商体验分权重重新划定:商品体验50%、服务体验35%、物流体验15%,4.8分是店铺核心分水岭。低于4.8分,千川流量、商品卡自然流权重、平台…

2026/7/6 2:01:44 阅读更多 →
Haiwell Cloud SCADA 3 与主流 PLC 协议对比:支持 3 类设备驱动的连接实测

Haiwell Cloud SCADA 3 与主流 PLC 协议对比:支持 3 类设备驱动的连接实测

Haiwell Cloud SCADA 3 与主流 PLC 协议深度兼容性实测报告在工业自动化系统集成领域,多品牌PLC设备的互联互通一直是工程师面临的现实挑战。海为科技最新发布的Cloud SCADA 3版本以"内置多种工业设备驱动"为核心卖点,宣称能够无缝对接西门子、…

2026/7/6 1:59:44 阅读更多 →
数字通信同步技术:3种载波同步方法对比与低信噪比场景实战

数字通信同步技术:3种载波同步方法对比与低信噪比场景实战

数字通信同步技术:3种载波同步方法对比与低信噪比场景实战在数字通信系统中,载波同步是实现可靠数据传输的核心技术之一。当信号经过信道传输后,接收端需要精确恢复发送端的载波频率和相位,才能正确解调出原始信息。尤其在低信噪比…

2026/7/6 1:59:44 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻