Explorar o código

feat: 兼容移动端

shuisheng %!s(int64=2) %!d(string=hai) anos
pai
achega
53ffc2885c

+ 7 - 0
src/App.vue

@@ -3,9 +3,11 @@
 </template>
 
 <script lang="ts" setup>
+import { isMobile } from 'billd-utils';
 import { onMounted } from 'vue';
 
 import { loginMessage } from '@/hooks/use-login';
+import router, { routerName } from '@/router';
 import { useUserStore } from '@/store/user';
 import cache from '@/utils/cache';
 
@@ -18,6 +20,11 @@ onMounted(() => {
     userStore.setToken(token);
     userStore.getUserInfo();
   }
+  if (isMobile()) {
+    router.push({ name: routerName.h5 });
+  } else {
+    router.push({ name: routerName.home });
+  }
   // 启用vconsole
   // import('vconsole')
   //   .then((VConsole) => {

+ 12 - 0
src/api/area.ts

@@ -0,0 +1,12 @@
+import request from '@/utils/request';
+
+export function fetchAreaLiveRoomList(params: {
+  orderName: string;
+  orderBy: string;
+}) {
+  return request.instance({
+    url: '/area/area_live_room_list',
+    method: 'get',
+    params,
+  });
+}

+ 5 - 0
src/assets/css/videojs.scss

@@ -0,0 +1,5 @@
+@import 'video.js/dist/video-js.min.css';
+
+.vjs-loading-spinner {
+  display: none !important;
+}

BIN=BIN
src/assets/img/logo-txt.png


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 13 - 0
src/assets/img/logo-txt.svg


+ 3 - 1
src/hooks/use-play.ts

@@ -1,4 +1,4 @@
-import 'video.js/dist/video-js.min.css';
+import '@/assets/css/videojs.scss';
 
 import videoJs from 'video.js';
 import Player from 'video.js/dist/types/player';
@@ -101,6 +101,8 @@ export function useHlsPlay() {
               type: 'application/x-mpegURL',
             },
           ],
+          controls: false,
+          preload: 'auto',
         },
         function onPlayerReady() {
           console.log('Your player is ready!');

+ 29 - 3
src/interface.ts

@@ -149,10 +149,11 @@ export interface ILiveRoom {
   id?: number;
   /** 用户信息 */
   user?: IUser;
-  /** 直播间名字 */
+  /** 用户信息 */
+  users?: IUser[];
+  /** 分区信息 */
+  area?: IArea;
   name?: string;
-  /** 联表(live表) */
-  live?: string;
   user_live_room?: IUserLiveRoom & { user: IUser };
   /** 权重 */
   weight?: number;
@@ -271,6 +272,31 @@ export interface IQqUser {
   deleted_at?: any;
 }
 
+export interface IArea {
+  id?: number;
+  name?: string;
+  remark?: string;
+  /** 权重 */
+  weight?: number;
+  area_live_rooms?: IAreaLiveRoom[];
+  created_at?: string;
+  updated_at?: string;
+  deleted_at?: string;
+}
+
+export interface IAreaLiveRoom {
+  id?: number;
+  area_id?: number;
+  live_room_id?: number;
+  /** 分区信息 */
+  area?: IUser;
+  /** 直播间信息 */
+  live_room?: ILiveRoom;
+  created_at?: string;
+  updated_at?: string;
+  deleted_at?: string;
+}
+
 export interface ILive {
   id?: number;
   /** 用户信息 */

+ 8 - 26
src/layout/head/index.vue

@@ -5,8 +5,7 @@
         class="logo-wrap"
         @click="router.push('/')"
       >
-        <!-- <div class="logo"></div> -->
-        <div class="txt">Billd直播</div>
+        <div class="logo"></div>
       </div>
 
       <div class="nav">
@@ -373,28 +372,11 @@ function handleStartLive(key) {
       margin-right: 20px;
       cursor: pointer;
 
-      // .logo {
-      //   margin-right: 5px;
-      //   width: 35px;
-      //   height: 35px;
-      //   border-radius: 50%;
-      //   font-size: 12px;
-      //   // animation: rotate 3s linear infinite;
+      .logo {
+        width: 90px;
+        height: 56px;
 
-      //   @include setBackground('@/assets/img/logo.webp');
-      //   @keyframes rotate {
-      //     0% {
-      //       transform: rotate(0deg);
-      //     }
-      //     100% {
-      //       transform: rotate(360deg);
-      //     }
-      //   }
-      // }
-      .txt {
-        color: $theme-color-gold;
-        font-weight: 500;
-        font-size: 18px;
+        @include setBackground('@/assets/img/logo-txt.png');
       }
     }
 
@@ -539,12 +521,12 @@ function handleStartLive(key) {
     }
     .qqlogin {
       .btn {
-        box-sizing: border-box;
-        width: 35px;
-        height: 35px;
         display: flex;
         align-items: center;
         justify-content: center;
+        box-sizing: border-box;
+        width: 35px;
+        height: 35px;
         border-radius: 50%;
         background-color: papayawhip;
         font-size: 13px;

+ 3 - 3
src/main.scss

@@ -1,7 +1,7 @@
 body {
   padding: 0;
   margin: 0;
-  font-family: Quotes, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
-    Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
-    sans-serif;
+  font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica,
+    Segoe UI, Arial, Roboto, PingFang SC, miui, Hiragino Sans GB,
+    Microsoft Yahei, sans-serif;
 }

+ 7 - 0
src/router/index.ts

@@ -7,6 +7,7 @@ import type { RouteRecordRaw } from 'vue-router';
 export const routerName = {
   home: 'home',
   about: 'about',
+  h5: 'h5',
   account: 'account',
   rank: 'rank',
   sponsors: 'sponsors',
@@ -38,6 +39,7 @@ export const defaultRoutes: RouteRecordRaw[] = [
         path: '/',
         component: () => import('@/views/home/index.vue'),
       },
+
       {
         name: routerName.about,
         path: '/about',
@@ -117,6 +119,11 @@ export const defaultRoutes: RouteRecordRaw[] = [
       },
     ],
   },
+  {
+    name: routerName.h5,
+    path: '/h5',
+    component: () => import('@/views/h5/index.vue'),
+  },
   {
     name: routerName.oauth,
     path: '/oauth/:platform',

+ 205 - 0
src/views/h5/index.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="h5-wrap">
+    <div class="logo-bar">
+      <div class="logo"></div>
+    </div>
+    <nav class="nav-list">
+      <div
+        v-for="(item, index) in navList"
+        :key="index"
+        class="item"
+        :class="{ active: currentNav.id === item.id }"
+        @click="currentNav = item"
+      >
+        {{ item.name }}
+      </div>
+    </nav>
+    <div class="swiper">
+      <div class="item"></div>
+    </div>
+    <div class="type-list">
+      <div
+        v-for="(item, index) in liveRoomList"
+        :key="index"
+        class="item"
+      >
+        <div class="title">
+          <div class="left">{{ item.name }}</div>
+          <div class="right">进去看看</div>
+        </div>
+        <div class="live-room-list">
+          <div
+            v-for="(iten, indey) in item.area_live_rooms"
+            :key="indey"
+            class="live-room"
+          >
+            <div
+              class="cover"
+              :style="{
+                backgroundImage: `url('${
+                  iten.live_room?.cover_img || iten.live_room?.users?.[0].avatar
+                }')`,
+              }"
+            >
+              <div class="txt">{{ iten.live_room?.users?.[0].username }}</div>
+            </div>
+            <div class="desc">{{ iten.live_room?.name }}</div>
+          </div>
+          <div
+            v-if="!item.area_live_rooms?.length"
+            class="null"
+          >
+            暂无数据
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, ref } from 'vue';
+
+import { fetchAreaLiveRoomList } from '@/api/area';
+import { IArea } from '@/interface';
+
+const navList = ref([
+  { id: 1, name: '频道' },
+  { id: 2, name: '排行' },
+  { id: 3, name: '我的' },
+]);
+
+const currentNav = ref(navList.value[0]);
+const liveRoomList = ref<IArea[]>([]);
+
+async function getLiveRoomList() {
+  try {
+    const res = await fetchAreaLiveRoomList({
+      orderName: 'created_at',
+      orderBy: 'desc',
+    });
+    if (res.code === 200) {
+      liveRoomList.value = res.data.rows;
+    }
+  } catch (error) {
+    console.log(error);
+  }
+}
+
+onMounted(() => {
+  getLiveRoomList();
+});
+</script>
+
+<style lang="scss" scoped>
+.h5-wrap {
+  background-color: #f4f4f4;
+  .logo-bar {
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    padding: 0 20px;
+    height: 40px;
+    border-bottom: 1px solid #e7e7e7;
+    background-color: white;
+    .logo {
+      width: 90px;
+      height: 36px;
+
+      @include setBackground('@/assets/img/logo-txt.png');
+    }
+  }
+  .nav-list {
+    display: flex;
+    align-items: center;
+    height: 40px;
+    background-color: white;
+    line-height: 40px;
+    .item {
+      position: relative;
+      margin: 0 20px;
+      &.active {
+        &::after {
+          position: absolute;
+          right: 0;
+          bottom: 0;
+          width: 100%;
+          height: 4px;
+          background-color: $theme-color-gold;
+          content: '';
+        }
+      }
+    }
+  }
+  .swiper {
+    width: 100%;
+    height: 130px;
+    background: red;
+  }
+  .type-list {
+    .item {
+      margin: 15px 0;
+      padding: 15px;
+      background-color: white;
+
+      .title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin-bottom: 10px;
+        .left {
+        }
+        .right {
+          color: #999;
+          font-size: 14px;
+        }
+      }
+      .live-room-list {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        .live-room {
+          display: inline-block;
+          margin-bottom: 10px;
+          width: 48%;
+          .cover {
+            position: relative;
+            overflow: hidden;
+            width: 100%;
+            height: 100px;
+            border-radius: 8px;
+            background-position: center center;
+            background-size: cover;
+
+            .txt {
+              position: absolute;
+              bottom: 0;
+              left: 0;
+              box-sizing: border-box;
+              padding: 4px 8px;
+              width: 100%;
+              border-radius: 0 0 4px 4px;
+              background-image: linear-gradient(
+                -180deg,
+                rgba(0, 0, 0, 0),
+                rgba(0, 0, 0, 0.6)
+              );
+              color: white;
+              text-align: initial;
+              font-size: 13px;
+
+              @extend %singleEllipsis;
+            }
+          }
+          .desc {
+            font-size: 14px;
+            margin-top: 4px;
+            @extend %singleEllipsis;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 1 - 0
src/views/home/index.vue

@@ -177,6 +177,7 @@ async function handleTipBtn() {
 }
 
 function changeLiveRoom(item: ILive) {
+  if (item.id === currentLiveRoom.value?.id) return;
   currentLiveRoom.value = item;
 
   nextTick(async () => {

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio