基于uniapp+vue3+uvue短视频+聊天+直播app系统

0 阅读1分钟

2025最新版uni-app+vue3+pinia2+uv-ui+nvue搭建小视频+聊天+直播于一体的商城app系统。支持编译运行到web+小程序端+app端

未标题-3.png

6d89f0afd5c2d9db92e4e2c83459cf08_1289798-20250702222204084-1847949639.gif

使用技术

  • 开发工具:HbuilderX 4.66
  • 框架技术:Uniapp+Vue3+Vite5+Nvue+Pinia2
  • UI组件库:uv-ui+vk-uview
  • 弹框组件:uaPopup(uniapp封装多端弹框组件)
  • 自定义组件:uaNavbar+uaTabbar组件
  • 本地缓存:pinia-plugin-unistorage
  • 运行支持:h5+小程序+APP端

未标题-1.png

5c6a6fdb9b138078e259099bdc14e45e_1289798-20250702222949518-645512129.gif

全屏沉浸式丝滑上下切换短视频/直播,迷你拖拽播放进度条。

p2.gif

项目框架结构

使用uniapp+vue3搭建项目框架模板,采用vue3 setup语法编码开发。

b0dcbd0b8d31cad5ebb90bed6d89a8be_1289798-20250702224431047-1822448283.png

uniapp+vue3实现短视频模块

9cff9ef5a4d109d314ef76b2ce15a2c0_1289798-20250702231614932-901103364.png

整体分为顶部固定tabs+视频区+底部视频信息浮层三大模块。

<ua-layout>
    <view class="ua__swipervideo flex1">
        <swiper
            class="ua__swipervideo-wrap flex1"
            :current="currentVideo"
            vertical
            :circular="true"
            :duration="200"
            @change="handleChange"
            @transition="handleTransition"
        >
            <swiper-item v-for="(item, index) in videoList" :key="index">
                <video
                    class="ua__swipervideo-player flex1"
                    :id="'uplayer' + index"
                    :src="item.src"
                    :danmu-list="item.danmu"
                    :enable-danmu="true"
                    :controls="false"
                    :loop="true"
                    :autoplay="index == currentVideo"
                    :show-center-play-btn="false"
                    object-fit="contain"
                    @click="handleClickVideo"
                    @play="isPlaying=true"
                    @timeupdate="handleTimeUpdate"
                    :style="{'width': `${winWidth}px`, 'height': `${winHeight}px`}"
                >
                </video>
                
                <!-- 浮层模块 -->
                <view class="ulive__video-float__info flexbox flex-col">
                    <view class="flexbox flex-row flex-alignb">
                        <!-- 左侧 -->
                        <view class="vdinfo__left flex1 flexbox flex-col">
                            <view class="ltrow danmu flexbox" @click="handleOpenDanmu"><text class="danmu-txt"></text><uv-icon class="ico" name="edit-pen" color="#fff" size="14" /></view>
                            <view class="ltrow"><text class="ait">@{{item.author}}</text></view>
                            <view class="ltrow"><text class="desc">{{item.desc}}</text></view>
                        </view>
                        <!-- 右侧操作栏 -->
                        <view class="vdinfo__right flexbox flex-col">
                            <view class="rtbtn avatar flexbox flex-col">
                                <view class="ubox"><image class="uimg" :src="item.avatar" mode="aspectFill" /></view>
                                <view class="btn flexbox" :class="{'active': item.isFollow}" @click="handleFollow(index)"><uv-icon :name="item.isFollow ? 'checkmark' : 'plus'" :color="item.isFollow ? '#ff007f' : '#fff'" size="11" /></view>
                            </view>
                            <view class="rtbtn flexbox flex-col" @click="handleLiked(index)"><uv-icon name="heart-fill" :color="item.isLike ? '#ff007f' : '#fff'" size="40" /><text class="num">{{item.likeNum+(item.isLike ? 1 : 0)}}</text></view>
                            <view class="rtbtn flexbox flex-col" @click="handleOpenComment(index)"><uv-icon name="chat-fill" color="#fff" size="40" /><text class="num">{{item.replyNum}}</text></view>
                            <view class="rtbtn flexbox flex-col"><uv-icon name="star-fill" color="#fff" size="40" /><text class="num">{{item.starNum}}</text></view>
                            <view class="rtbtn flexbox flex-col" @click="handleOpenShare(index)"><uv-icon name="share-fill" color="#fff" size="40" /><text class="num">{{item.shareNum}}</text></view>
                        </view>
                    </view>
                </view>
            </swiper-item>
        </swiper>
        
        <!-- 固定tabs(脱离滑动区) -->
        <view class="ulive__video-header__tabs" :style="{'margin-top': `${menuBarT}px`}">
            <uv-tabs :current="tabsCurrent" :list="tabsList" />
        </view>
        
        <!-- 播放暂停按钮 -->
        <view v-if="!isPlaying" class="ua__swipervideo-playbtn" :style="{'left': `${winWidth/2}px`, 'top': `${winHeight/2}px`}" @click="handleClickVideo">
            <text class="ua__swipervideo-playico welive-icon welive-icon-play nvueicon"></text>
        </view>
        
        <!-- 播放mini进度条 -->
        <view v-if="sliderValue > 0" class="ua__swipervideo-slider">
            <slider
                :value="sliderValue"
                :step="sliderStep"
                :max="sliderMaxValue"
                activeColor="#fff"
                backgroundColor="rgba(255,255,255,.2)"
                block-color="#fff"
                block-size="6"
                @changing="onSliderChanging"
                @change="onSliderChange"
            />
        </view>
    </view>
    
    <template #footer>
        <ua-tabbar bgcolor="transparent" color="rgba(255,255,255,.7)" :border="false" :dock="false" transparent z-index="1000" />
    </template>
</ua-layout>

8a56cdf2862a08a044698e25a654820e_1289798-20250702231915208-1096141902.png

// 监听播放进度条
const handleTimeUpdate = (e) => {
    let { currentTime, duration } = e.detail
    sliderValue.value = currentTime
    sliderMaxValue.value = duration
    sliderStep.value = currentTime / duration
}

// 滑动条拖动中
const onSliderChanging = (e) => {
    sliderValue.value = e.detail.value
    handlePause()
}

// 滑动条拖动结束
const onSliderChange = (e) => {
    sliderValue.value = e.detail.value
    let video = getVideoContext()
    if(!video) return
    video.seek(sliderValue.value)
    handlePlay()
}

未标题-2.png

未标题-4.png

uniapp+vue3聊天模块

image.png

聊天模块是之前开发的一个uniapp+vue3仿微信app聊天项目的迁移模块。如果对聊天项目感兴趣,可以去看看下面这篇分享文章。

uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天语音/朋友圈

未标题-5.png

未标题-6.png

未标题-7.png

uniapp+vue3直播模块

9ef0a675f0265e039d58aa969fdc20b9_1289798-20250702232522310-1147536811.png

直播模块分为顶部信息+直播流视频区+滚动消息(加入直播间+送礼物+讲解商品)+底部工具栏

<ua-layout>
    <view class="ua__swipervideo flex1">
        <swiper
            class="ua__swipervideo-wrap flex1"
            :current="currentLive"
            vertical
            @change="handleChange"
        >
            <swiper-item v-for="(item, index) in liveList" :key="index">
                <video
                    class="ua__swipervideo-player flex1"
                    :id="'uplayer' + index"
                    :src="item.src"
                    :controls="false"
                    :loop="true"
                    :autoplay="index == currentLive"
                    :show-center-play-btn="false"
                    object-fit="contain"
                    :style="{'width': `${winWidth}px`, 'height': `${winHeight}px`}"
                >
                </video>
                
                <!-- 浮层模块 -->
                <swiper class="ulive__swiperscreen flex1" :current="1">
                    <!-- 清屏 -->
                    <swiper-item>
                        第一屏
                    </swiper-item>
                    <swiper-item>
                        <!-- 顶部区域 -->
                        <view class="ulive__headlayer" :style="{'top': menuBarT+'px'}">
                            <!-- logo+关注 -->
                            <view class="ulive__hd-liveinfo flexbox flex-row flex-alignc">
                                <view class="ulive__hd-avatar ulive__mask flex-alignc">
                                    <image class="logo" :src="item.logo" mode="widthFix" />
                                    <view class="flex1 flexbox flex-col ml-10">
                                        <text class="name">{{item.name}}</text>
                                        <text class="zan">{{item.likeNum}}本场点赞</text>
                                    </view>
                                    <view class="btn flexbox flex-row flex-alignc" :class="{'active': item.isFollow}" @click="handleFollow(index)"><text class="btntext" :class="{'active': item.isFollow}">{{item.isFollow ? '已关注' : '关注'}}</text></view>
                                </view>
                                <view class="ulive__hd-onlineuser flex1">
                                    <uv-icon name="close" color="#fff" @click="handleLiveQuit" />
                                </view>
                            </view>
                            <view class="ulive__hd-livewrap flexbox flex-row">
                                <view class="ulive__hd-livewrap__left flex1 flexbox flex-col">
                                    <view class="ulive__hd-livewrap__tags flexbox flex-row">
                                        <view class="ulive__roundwrap ulive__mask">
                                            <uv-icon name="shopping-cart" color="#ffdd1a" /><text class="ulive__roundtext">服饰鞋包榜第1名</text>
                                        </view>
                                        <view class="ulive__roundwrap ulive__mask ml-10">
                                            <uv-icon name="level" color="#ffdd1a" /><text class="ulive__roundtext">小时榜</text>
                                        </view>
                                    </view>
                                    <!-- 红包+福袋倒计时 -->
                                    <view class="ulive__hd-livewrap__redpacket flexbox flex-row">
                                        <view class="ulive__redpacket-item ulive__mask" @click="handleOpenRedpacket(1)">
                                            <image class="ulive__redpacket-image" src="/static/icon-fudai.png" mode="widthFix" /><text class="ulive__redpacket-time">04:49</text>
                                        </view>
                                        <view class="ulive__redpacket-item ulive__mask" @click="handleOpenRedpacket(2)">
                                            <image class="ulive__redpacket-image" src="/static/icon-hb.png" mode="widthFix" /><text class="ulive__redpacket-time">04:49</text>
                                        </view>
                                        <view class="ulive__redpacket-item ulive__mask center">
                                            <image class="ulive__redpacket-image" src="/static/icon-rotate.png" mode="widthFix" /><text class="ulive__redpacket-time">04:49</text>
                                        </view>
                                    </view>
                                </view>
                                <view class="ulive__hd-livewrap__right flexbox flex-col">
                                    <view class="ulive__roundwrap ulive__mask mr-20">
                                        <uv-icon name="kefu-ermai" color="#fff" /><text class="ulive__roundtext ml-5">后台</text>
                                    </view>
                                </view>
                            </view>
                        </view>
                        
                        <!-- 底部区域 -->
                        <view class="ulive__footlayer">
                            <!-- 商品提示层 -->
                            <view class="ulive__ft-livewrap-placeholder animated fadeIn">
                                <view class="ulive__ft-livewrap-hotbuy flexbox flex-row">
                                    <image class="gimg" :src="item.poster" mode="aspectFill" />
                                    <view class="ginfo flex1">
                                        <view class="flexbox flex-row"><text class="user c-ffdd1a">Andy</text><text class="c-fff">等{{item.saleNum}}人在购买</text></view>
                                        <text class="gdesc clamp1">{{item.desc}}</text>
                                    </view>
                                    <view class="btn"><text class="btntext">去购买</text></view>
                                </view>
                            </view>
                            <!-- 加入直播间/送礼物提示 -->
                            <view class="ulive__ft-livewrap-animateview flexbox flex-col">
                                <view class="ulive__ft-livewrap-animatejoin ulive__ft-livewrap-placeholder">
                                    <view v-if="joinRoomData" class="ulive__ft-livewrap-joinroom"><text class="ulive__ft-livewrap-joinroom__text">欢迎{{joinRoomData}}加入了直播间</text></view>
                                </view>
                                
                                <!-- 送礼物 -->
                                <view class="ulive__ft-livewrap-animategift ulive__ft-livewrap-placeholder">
                                    <view v-if="!isEmpty(sendGiftData)" class="ulive__ft-livewrap-activegift flexbox flex-row flex-alignc">
                                        <image class="avatar" :src="sendGiftData.avatar" />
                                        <view class="info flex1"><text class="name">{{sendGiftData.user}}</text><text class="desc">送出</text></view>
                                        <image class="gift" :src="sendGiftData.pic" />
                                    </view>
                                </view>
                            </view>
                            <!-- 聊天浮层+商品讲解 -->
                            <view class="ulive__ft-livewrap-mixinview flexbox flex-row">
                                <!-- 聊天消息 -->
                                <view class="ulive__ft-livewrap-chats flex1">
                                    <scroll-view class="ulive__ft-livewrap-chats__scrollview flex1" scroll-y show-scrollbar="false" :scroll-into-view="scrollToView" :lower-threshold="5" @scroll="handleMsgScroll" @scrolltolower="handleMsgScrollLower">
                                        <block v-for="(msgitem, msgidx) in item.message" :key="msgidx">
                                            <view v-if="msgitem.type == 'notice'" class="notice" :id="`msg-${msgitem.id}`"><view class="item"><text class="noticetext">{{msgitem.content}}</text></view></view>
                                            <view v-else-if="msgitem.type == 'gift'" class="gift" :id="`msg-${msgitem.id}`">
                                                <view class="item">
                                                    <text class="giftuser">{{msgitem.user}}</text>
                                                    <text class="gifttext">送出了{{msgitem.content}}</text>
                                                    <image class="giftimg" :src="msgitem.img" mode="widthFix" />
                                                    <text class="giftnum">x{{msgitem.num}}</text>
                                                </view>
                                            </view>
                                            <view v-else class="msg" :id="`msg-${msgitem.id}`">
                                                <view class="item">
                                                    <text v-if="msgitem.tag" class="tag">{{msgitem.tag}}</text>
                                                    <text class="user">{{msgitem.user}}</text>
                                                    <text class="text" :style="[fixTextStyle]">{{msgitem.isbuy ? '正在购买' : msgitem.content}}</text>
                                                    <text v-if="msgitem.isbuy" class="tag tag-buy">去购买</text>
                                                </view>
                                            </view>
                                        </block>
                                    </scroll-view>
                                    <view v-if="!isEmpty(msgUnread)" class="ulive__ft-livewrap-chats__unread" @click="handleMsgIsRead"><text class="c-eb4868 fs-24">{{msgUnread.length}}条新消息</text></view>
                                </view>
                                <!-- 商品讲解 -->
                                <view v-if="isVisibleGoodsTalk" class="ulive__ft-livewrap-activegoods animated fadeInRight" id="goodsTalkID">
                                    <view class="ulive__ft-livewrap-activegoods__hotsale flexbox flex-row">
                                        <image class="fimg" src="/static/icon-hot.png" mode="widthFix" /><text class="c-fff fs-32">热卖 x{{item.saleNum}}</text>
                                    </view>
                                    <swiper class="ulive__ft-livewrap-activegoods__swiper">
                                        <swiper-item>
                                            <view class="ulive__ft-livewrap-activegoods__card">
                                                <view class="gwrap" @click="toGoodsDetail">
                                                    <image class="gimg" :src="item.poster" mode="aspectFill" />
                                                    <view class="waves"><text class="c-fff fs-24">讲解中</text></view>
                                                    <view class="close" @click.stop="isVisibleGoodsTalk=false"><uv-icon name="close-circle-fill" color="rgba(0, 0, 0, .3)" size="14" /></view>
                                                </view>
                                                <view class="ginfo flexbox flex-col">
                                                    <text class="clamp1 fs-24">{{item.desc}}</text>
                                                    <text class="clamp1 fs-24 c-eb4868">7天无理由退货</text>
                                                </view>
                                                <view class="btn flexbox flex-row"><text class="flex1 c-fff fs-28">¥79.00</text><text class="qiang"></text></view>
                                            </view>
                                        </swiper-item>
                                    </swiper>
                                </view>
                            </view>
                            <!-- 工具栏 -->
                            <view class="ulive__ft-livewrap-toolbar flexbox flex-row">
                                <view class="editorwrap flex1 flexbox flex-row flex-alignc">
                                    <view class="flex1" @click="handleOpenChatbox"><text class="editorwrap-text">说点什么...</text></view>
                                </view>
                                <view class="btnwrap flexbox flex-row">
                                    <view class="btn flexbox" @click="handleOpenMenus"><uv-icon name="grid" color="#3c9cff" size="22" /></view>
                                    <view class="btn flexbox" @click="handleOpenGoods(item)"><uv-icon name="shopping-cart-fill" color="#ffaa00" size="24" /></view>
                                    <view class="btn flexbox" @click="handleOpenGifts"><uv-icon name="gift" color="#ff0ad3" size="22" /></view>
                                    <view class="btn flexbox"><uv-icon name="more-dot-fill" color="#efe9ff" size="18" /></view>
                                </view>
                            </view>
                        </view>
                    </swiper-item>
                </swiper>
            </swiper-item>
        </swiper>
    </view>
</ua-layout>

以上就是uniapp+vue3实战小视频+聊天+直播app项目的一些知识分享。

基于tauri2.0+vue3.5+deepseek+arco搭建wins版流式输出AI系统

基于uniapp+vue3+deepseek+markdown搭建app版流式输出AI模板

vue3.5+deepseek+arco+markdown搭建web版流式输出AI模板

vue3仿Deepseek/ChatGPT流式聊天AI界面,对接deepseek/OpenAI API

Flutter3.x深度融合短视频+直播+聊天app实例

自研tauri2.0+vite6.x+vue3+rust+arco-design桌面版os管理系统Tauri2-ViteOS

自研tauri2.0+vite5+vue3+element-plus电脑版exe聊天系统Vue3-Tauri2Chat

原创electron31+vite5.x+elementPlus桌面端后台管理系统