urbanu 1 month ago
parent
commit
0d6b1c5f07
5 changed files with 111 additions and 41 deletions
  1. 2 2
      index.html
  2. BIN
      public/favicon.ico
  3. 45 0
      src/api/material.ts
  4. 1 1
      src/views/home/index.vue
  5. 63 38
      src/views/material/index.vue

+ 2 - 2
index.html

@@ -4,8 +4,8 @@
   <meta charset="UTF-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
-  <link rel="icon" href="/logo.svg" />
-  <title>CoinVEX</title>
+  <link rel="icon" href="/favicon.ico" />
+  <title>Daytask</title>
 </head>
 
 <body>

BIN
public/favicon.ico


+ 45 - 0
src/api/material.ts

@@ -0,0 +1,45 @@
+import { http } from "@/utils/http";
+
+// 素材信息 (匹配后端 MaterialInfo 结构)
+export interface MaterialInfo {
+  id: number;
+  name: string;       // 素材名称
+  url: string;        // 访问URL
+  content?: string;   // 文字内容(type=text时使用)
+  type: 'image' | 'video' | 'text';
+  createdAt: number;
+}
+
+// 素材分类
+export interface MaterialCategory {
+  id: number;
+  name: string;
+  nameVi?: string;
+  icon?: string;
+  sort: number;
+  status: number;
+}
+
+// 素材列表参数
+interface MaterialListParams {
+  current: number;
+  size: number;
+  categoryId?: number;
+}
+
+// 获取素材列表
+export function requestGetMaterialList(params: MaterialListParams): Promise<CommonPageResponse<MaterialInfo>> {
+  return http.request({
+    url: "/api/v1/dt/material/list",
+    method: "get",
+    params
+  });
+}
+
+// 获取素材分类列表
+export function requestGetMaterialCategories(): Promise<CommonResponse<MaterialCategory[]>> {
+  return http.request({
+    url: "/api/v1/dt/material/categories",
+    method: "get"
+  });
+}

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

@@ -215,7 +215,7 @@ onMounted(() => {
 
   .banner-img {
     width: 100%;
-    height: min(50.667vw, 243.2px);
+    height: 244px;
     object-fit: cover;
   }
 }

+ 63 - 38
src/views/material/index.vue

@@ -56,18 +56,18 @@
         <van-list
           v-model:loading="loading"
           :finished="finished"
-          :finished-text="$t('common.noData')"
+          :finished-text="materialList.length ? '' : $t('common.noData')"
           @load="loadMore"
         >
           <!-- 图片素材 -->
-          <div class="image-grid" v-if="type === 'image' || type === ''">
+          <div class="image-grid" v-if="(type === 'image' || type === '') && imageList.length">
             <div
               class="image-item"
               v-for="item in imageList"
               :key="item.id"
               @click="previewImage(item)"
             >
-              <img :src="item.url" />
+              <img :src="getMediaUrl(item)" />
               <div class="image-actions">
                 <van-icon name="eye-o" @click.stop="previewImage(item)" />
                 <van-icon name="down" @click.stop="downloadImage(item)" />
@@ -76,16 +76,16 @@
           </div>
 
           <!-- 文本素材 -->
-          <div class="text-list" v-if="type === 'text' || type === ''">
+          <div class="text-list" v-if="(type === 'text' || type === '') && textList.length">
             <div
               class="text-item"
               v-for="item in textList"
               :key="item.id"
             >
-              <div class="text-title">{{ item.title }}</div>
+              <div class="text-title">{{ getName(item) }}</div>
               <div class="text-content">{{ item.content }}</div>
               <div class="text-actions">
-                <van-button size="small" @click="copyText(item.content)">
+                <van-button size="small" @click="copyText(item.content || '')">
                   {{ $t('common.copy') }}
                 </van-button>
               </div>
@@ -93,14 +93,14 @@
           </div>
 
           <!-- 视频素材 -->
-          <div class="video-list" v-if="type === 'video' || type === ''">
+          <div class="video-list" v-if="(type === 'video' || type === '') && videoList.length">
             <div
               class="video-item"
               v-for="item in videoList"
               :key="item.id"
             >
-              <video :src="item.url" controls></video>
-              <div class="video-title">{{ item.title }}</div>
+              <video :src="getMediaUrl(item)" controls></video>
+              <div class="video-title">{{ getName(item) }}</div>
             </div>
           </div>
         </van-list>
@@ -114,6 +114,7 @@ import { ref, computed, watch, onMounted } from "vue";
 import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { showToast, showImagePreview } from "vant";
+import { requestGetMaterialList, type MaterialInfo } from "@/api/material";
 
 const { t } = useI18n();
 const router = useRouter();
@@ -127,6 +128,16 @@ const refreshing = ref(false);
 const page = ref(1);
 const pageSize = 20;
 
+// 获取素材的URL (图片/视频)
+const getMediaUrl = (item: MaterialInfo): string => {
+  return item.url || '';
+};
+
+// 获取素材名称
+const getName = (item: MaterialInfo): string => {
+  return item.name || '';
+};
+
 const imageList = computed(() =>
   materialList.value.filter(m => m.type === 'image')
 );
@@ -146,33 +157,44 @@ const getMaterials = async (isRefresh = false) => {
   }
 
   try {
-    // 模拟数据
-    const types = type.value ? [type.value] : ['image', 'text', 'video'];
-    const mockList = Array(pageSize).fill(null).map((_, i) => {
-      const itemType = types[i % types.length];
-      return {
-        id: (page.value - 1) * pageSize + i + 1,
-        type: itemType,
-        title: `Material ${(page.value - 1) * pageSize + i + 1}`,
-        content: itemType === 'text' ? 'This is sample promotional text that can be copied and used for marketing purposes. Feel free to customize it according to your needs.' : undefined,
-        url: itemType === 'image'
-          ? `https://picsum.photos/300/200?random=${(page.value - 1) * pageSize + i}`
-          : itemType === 'video'
-          ? 'https://www.w3schools.com/html/mov_bbb.mp4'
-          : undefined
-      };
-    }) as MaterialInfo[];
-
-    if (isRefresh) {
-      materialList.value = mockList;
-    } else {
-      materialList.value = [...materialList.value, ...mockList];
-    }
+    const res = await requestGetMaterialList({
+      current: page.value,
+      size: pageSize
+    });
+
+    if (res.code === 200) {
+      let list = res.data?.list || [];
 
-    if (page.value >= 2) {
+      // 前端过滤类型
+      if (type.value) {
+        list = list.filter(m => m.type === type.value);
+      }
+
+      // 前端过滤关键词
+      if (keyword.value) {
+        const kw = keyword.value.toLowerCase();
+        list = list.filter(m =>
+          m.name?.toLowerCase().includes(kw) ||
+          m.content?.toLowerCase().includes(kw)
+        );
+      }
+
+      if (isRefresh) {
+        materialList.value = list;
+      } else {
+        materialList.value = [...materialList.value, ...list];
+      }
+
+      // 判断是否还有更多数据
+      const total = res.data?.paging?.total || 0;
+      if (materialList.value.length >= total || list.length < pageSize) {
+        finished.value = true;
+      }
+    } else {
       finished.value = true;
     }
   } catch (e) {
+    console.error('获取素材列表失败', e);
     finished.value = true;
   }
 
@@ -194,19 +216,22 @@ const loadMore = () => {
 };
 
 const previewImage = (item: MaterialInfo) => {
-  if (item.url) {
+  const url = getMediaUrl(item);
+  if (url) {
     showImagePreview({
-      images: imageList.value.map(i => i.url!),
-      startPosition: imageList.value.findIndex(i => i.id === item.id)
+      images: [url],
+      startPosition: 0
     });
   }
 };
 
 const downloadImage = (item: MaterialInfo) => {
-  if (item.url) {
+  const url = getMediaUrl(item);
+  if (url) {
     const link = document.createElement('a');
-    link.href = item.url;
-    link.download = `material_${item.id}.jpg`;
+    link.href = url;
+    link.download = item.name || `material_${item.id}.jpg`;
+    link.target = '_blank';
     link.click();
     showToast(t('material.download') + ' started');
   }