lsc hace 2 meses
padre
commit
e199a00f1a

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/icon/ppt/vbilibili.svg


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/icon/ppt/vfile.svg


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/icon/ppt/videoIcon.svg


+ 158 - 14
src/components/pages/aiAddCourse/aiCreateVideoDialog.vue

@@ -17,12 +17,18 @@
                 <div class="nav" :class="{ active: navActive == 4 }" @click="navClick(4)">最多收藏</div>
             </div>
             <div class="Box">
-                <div class="video_box video_box2" v-for="(item, index) in data" :key="index">
+                <div class="video_box video_box2" v-for="(item, index) in paginatedData" :key="index">
+                    <div class="video-checkbox">
+                        <el-checkbox 
+                            :value="isVideoSelected(item)" 
+                            @change="toggleVideoSelection(item)"
+                        ></el-checkbox>
+                    </div>
                     <!-- <img :src="item.pic" /> -->
                     <span class="name" v-html="item.title"></span>
                     <span class="author">作者:{{ item.author }}</span>
                     <span class="detail">{{ item.description }}</span>
-                    <div class="tag" v-if="item.tag.split(',').length > 0">
+                    <div class="tag" v-if="item.tag && typeof item.tag === 'string' && item.tag.split(',').length > 0">
                         <el-tooltip :content="tag" placement="top" effect="dark" v-for="(tag, index) in item.tag.split(',').slice(0, 5)" :key="index">
                             <!-- content to trigger tooltip here -->
                             <span >{{ tag }}</span>
@@ -36,6 +42,26 @@
                 </div>
                 <div v-if="data.length == 0" class="no_data">暂无数据</div>
             </div>
+            <!-- 分页控件 -->
+            <div class="pagination-box" v-if="data.length > 0">
+                <el-button 
+                    :disabled="currentPage === 1" 
+                    @click="prevPage"
+                    size="small"
+                >←上一页</el-button>
+                <span class="page-info">第 {{ currentPage }} / {{ totalPages }} 页</span>
+                <el-button 
+                    :disabled="currentPage >= totalPages" 
+                    @click="nextPage"
+                    size="small"
+                >下一页→</el-button>
+            </div>
+            <!-- 批量操作 -->
+            <div class="batch-actions" v-if="selectedVideos.length > 0">
+                <span class="selected-count">已选中 {{ selectedVideos.length }} 个视频</span>
+                <el-button type="primary" size="small" @click="batchAdd">批量加入</el-button>
+                <el-button size="small" @click="clearSelection">清空选择</el-button>
+            </div>
         </div>
         <span slot="footer" class="dialog-footer">
             <el-button @click="close">关 闭</el-button>
@@ -59,6 +85,10 @@ export default {
             type: String,
             default: ""
         },
+        grade: {
+            type: String,
+            default: ""
+        },
         courseState: {
             type: Number,
         },
@@ -89,15 +119,33 @@ export default {
             url: "",
             data: [],
             uJson: {},
-            navActive: 0
+            navActive: 0,
+            selectedVideos: [], // 选中的视频列表
+            currentPage: 1, // 当前页码
+            pageSize: 20, // 每页显示数量
+            totalPages: 1 // 总页数
         }
     },
     watch: {
         dialogVisibleAiCreateVideo(newValue, oldValue) {
             if (newValue) {
-                this.detail = ""
-                this.data = []
-                this.againEva()
+                this.selectedVideos = [];
+                // 默认以课程标题和年级为关键词
+                let keywords = [];
+                if (this.courseName) {
+                    keywords.push(this.courseName);
+                }
+                if (this.grade) {
+                    keywords.push(this.grade);
+                }
+                this.detail = keywords.join(',');
+                this.data = [];
+                this.currentPage = 1;
+                if (this.detail) {
+                    this.searchA();
+                } else {
+                    this.againEva();
+                }
             }
         },
     },
@@ -232,14 +280,16 @@ export default {
                     let _data = await _this.aiGet2(_content[a])
                     data2[a] = _data
                 }
-                _this.data = _.flatMap(_.zip(...data2), (pair) => pair.filter(value => value !== undefined))
-                // _this.data = _this.data.sort(
-                //     function (a, b) {
-                //         return b.play - a.play;
-                //     }
-                // );
-                _this.againEva2();
-                // _this.loading = false
+                    _this.data = _.flatMap(_.zip(...data2), (pair) => pair.filter(value => value !== undefined))
+                    // _this.data = _this.data.sort(
+                    //     function (a, b) {
+                    //         return b.play - a.play;
+                    //     }
+                    // );
+                    _this.againEva2();
+                    _this.currentPage = 1;
+                    _this.updateTotalPages();
+                    // _this.loading = false
             } catch (error){
                 console.log(error);
                 _this.loading = false
@@ -288,6 +338,8 @@ export default {
                         //     }
                         // );
                         _this.againEva2();
+                        _this.currentPage = 1;
+                        _this.updateTotalPages();
                     }else {
                         _this.$message.error("哎呀,请求太多了,服务器忙不过来了,请自行搜索关键词")
                         _this.loading = false
@@ -354,6 +406,7 @@ export default {
                             return aid.indexOf(el.aid) === -1
                         })
                         _this.$forceUpdate()
+                        _this.updateTotalPages();
                     }
                     _this.loading = false
                 })
@@ -361,8 +414,60 @@ export default {
                     console.log(error);
                     // _this.$message.error("哎呀,请求太多了,服务器忙不过来了,请自行搜索关键词")
                     _this.loading = false
+                    _this.updateTotalPages();
                 });
         },
+        // 检查视频是否被选中
+        isVideoSelected(item) {
+            return this.selectedVideos.some(v => v.bvid === item.bvid);
+        },
+        // 切换视频选择状态
+        toggleVideoSelection(item) {
+            const index = this.selectedVideos.findIndex(v => v.bvid === item.bvid);
+            if (index > -1) {
+                this.selectedVideos.splice(index, 1);
+            } else {
+                this.selectedVideos.push(item);
+            }
+        },
+        // 批量添加选中的视频
+        batchAdd() {
+            if (this.selectedVideos.length === 0) {
+                this.$message.warning('请先选择要添加的视频');
+                return;
+            }
+            this.selectedVideos.forEach(video => {
+                this.checkUrl(video.title, video.bvid);
+            });
+            this.$message.success(`成功添加 ${this.selectedVideos.length} 个视频`);
+            this.selectedVideos = [];
+        },
+        // 清空选择
+        clearSelection() {
+            this.selectedVideos = [];
+        },
+        // 更新总页数
+        updateTotalPages() {
+            this.totalPages = Math.ceil(this.data.length / this.pageSize);
+        },
+        // 获取当前页的数据
+        get paginatedData() {
+            const start = (this.currentPage - 1) * this.pageSize;
+            const end = start + this.pageSize;
+            return this.data.slice(start, end);
+        },
+        // 上一页
+        prevPage() {
+            if (this.currentPage > 1) {
+                this.currentPage--;
+            }
+        },
+        // 下一页
+        nextPage() {
+            if (this.currentPage < this.totalPages) {
+                this.currentPage++;
+            }
+        },
     },
     mounted () {
         
@@ -596,6 +701,45 @@ export default {
     padding: 5px 10px 10px;
     box-sizing: border-box;
     border-radius: 5px;
+    position: relative;
+}
+
+.video-checkbox {
+    position: absolute;
+    top: 10px;
+    left: 10px;
+    z-index: 10;
+    background: rgba(255, 255, 255, 0.9);
+    padding: 5px;
+    border-radius: 4px;
+}
+
+.pagination-box {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin: 20px 0;
+    gap: 15px;
+}
+
+.page-info {
+    font-size: 14px;
+    color: #606266;
+}
+
+.batch-actions {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 15px;
+    background: #f5f7fa;
+    border-top: 1px solid #e4e7ed;
+    margin-top: 20px;
+}
+
+.selected-count {
+    font-size: 14px;
+    color: #606266;
 }
 
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 8 - 3
src/components/pages/pptEasy/addCourse.vue


+ 611 - 0
src/components/pages/pptEasy/dialog/BilibiliSearchDialog.vue

@@ -0,0 +1,611 @@
+<template>
+  <el-dialog 
+    title="Bilibili视频检索" 
+    :visible.sync="dialogVisible" 
+    :append-to-body="true" 
+    width="660px"
+    :before-close="handleClose" 
+    class="dialog_diy bilibili-search-dialog"
+  >
+    <div style="box-sizing:border-box;padding:15px" v-loading="loading" element-loading-text="正在检索中,请稍等...">
+      <!-- 搜索和筛选区域 -->
+      <div class="search-filter-box">
+        <div class="search-item">
+          <div class="search-input-wrapper">
+            <el-input 
+              class="inputC" 
+              placeholder="搜索视频关键字(如需搜索多个可,隔开)" 
+              v-model="searchKeyword"
+              @keyup.enter.native="handleSearch"
+            ></el-input>
+            <div class="search_img" @click="handleSearch">
+              <img src="../../../../assets/icon/search.png" alt />
+            </div>
+          </div>
+        </div>
+        <div class="filter-item">
+          <el-select v-model="navActive" @change="navClick" placeholder="综合排序" class="sort-select">
+            <el-option label="综合排序" :value="0"></el-option>
+            <el-option label="最多播放" :value="1"></el-option>
+            <el-option label="最新发布" :value="2"></el-option>
+            <el-option label="最多弹幕" :value="3"></el-option>
+            <el-option label="最多收藏" :value="4"></el-option>
+          </el-select>
+        </div>
+        <div class="add-item">
+          <el-button type="primary" @click="batchAdd" :disabled="selectedVideos.length === 0">加入</el-button>
+        </div>
+      </div>
+      <!-- 视频列表 -->
+      <div class="Box">
+        <div 
+          class="video_box video_box2" 
+          :class="{ 'selected': isVideoSelected(item) }"
+          v-for="(item, index) in paginatedData" 
+          :key="index"
+          @click="toggleVideoSelection(item)"
+        >
+          <div class="video-checkbox" @click.stop>
+            <el-checkbox 
+              :value="isVideoSelected(item)" 
+              @change="toggleVideoSelection(item)"
+            ></el-checkbox>
+          </div>
+          <span class="name" v-html="item.title"></span>
+          <span class="detail">{{ item.description || '' }}</span>
+          <div class="video-info">
+            <span class="author">作者:{{ item.author || '' }}</span>
+            <span class="duration">时长:{{ item.duration || '' }}</span>
+          </div>
+          <div class="tag" v-if="item.tag && typeof item.tag === 'string' && item.tag.split(',').length > 0">
+            <el-tooltip :content="tag" placement="top" effect="dark" v-for="(tag, index) in item.tag.split(',').slice(0, 5)" :key="index">
+              <span>{{ tag }}</span>
+            </el-tooltip>
+          </div>
+          <div class="external-link" @click.stop="openUrl(item.bvid)" title="查看">
+            <svg t="1755224440400" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4991" width="16" height="16">
+              <path d="M853.333 469.333a42.667 42.667 0 0 0-42.666 42.667v256a42.667 42.667 0 0 1-42.667 42.667H256a42.667 42.667 0 0 1-42.667-42.667V256a42.667 42.667 0 0 1 42.667-42.667h256a42.667 42.667 0 0 0 0-85.333H256a128 128 0 0 0-128 128v512a128 128 0 0 0 128 128h512a128 128 0 0 0 128-128V512a42.667 42.667 0 0 0-42.667-42.667z" fill="#909399" p-id="4992"></path>
+              <path d="M682.667 213.333h270.933v270.933a42.667 42.667 0 1 0 85.334 0V170.667a42.667 42.667 0 0 0-42.667-42.667H682.667a42.667 42.667 0 0 0 0 85.333z" fill="#909399" p-id="4993"></path>
+              <path d="M894.293 174.293a42.667 42.667 0 0 0-60.586 0L591.147 417.707a42.667 42.667 0 0 0 60.586 60.586L894.293 234.88a42.667 42.667 0 0 0 0-60.587z" fill="#909399" p-id="4994"></path>
+            </svg>
+          </div>
+        </div>
+        <div v-if="data.length == 0" class="no_data">暂无数据</div>
+      </div>
+      <!-- 分页控件 -->
+      <div class="pagination-box" v-if="data.length > 0">
+        <el-button 
+          :disabled="currentPage === 1" 
+          @click="prevPage"
+          size="small"
+        >←上一页</el-button>
+        <span class="page-info">第 {{ currentPage }} / {{ totalPages }} 页</span>
+        <el-button 
+          :disabled="currentPage >= totalPages" 
+          @click="nextPage"
+          size="small"
+        >下一页→</el-button>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="handleClose">关 闭</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import _ from "lodash";
+
+export default {
+  name: "BilibiliSearchDialog",
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    courseName: {
+      type: String,
+      default: ""
+    },
+    grade: {
+      type: String,
+      default: ""
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      searchKeyword: "",
+      loading: false,
+      data: [],
+      navActive: 0,
+      selectedVideos: [], // 选中的视频列表
+      currentPage: 1, // 当前页码
+      pageSize: 20, // 每页显示数量
+      totalPages: 1 // 总页数
+    };
+  },
+  watch: {
+    visible(newVal) {
+      this.dialogVisible = newVal;
+      if (newVal) {
+        this.initDialog();
+      }
+    },
+    dialogVisible(newVal) {
+      this.$emit('update:visible', newVal);
+    }
+  },
+  methods: {
+    handleClose() {
+      this.dialogVisible = false;
+      this.selectedVideos = [];
+      this.searchKeyword = "";
+      this.data = [];
+      this.currentPage = 1;
+    },
+    initDialog() {
+      this.selectedVideos = [];
+      // 默认以课程标题和已选择的年级为关键词
+      let keywords = [];
+      if (this.courseName) {
+        keywords.push(this.courseName);
+      }
+      if (this.grade) {
+        // 如果grade是多个年级用逗号分隔的,也需要处理
+        keywords.push(this.grade);
+      }
+      this.searchKeyword = keywords.join(',');
+      this.data = [];
+      this.currentPage = 1;
+      if (this.searchKeyword) {
+        this.handleSearch();
+      }
+    },
+    // 搜索视频
+    async handleSearch() {
+      if (!this.searchKeyword) {
+        this.$message.error("请输入关键字");
+        return;
+      }
+      try {
+        this.loading = true;
+        let keywords = [];
+        if (this.searchKeyword.split(",").length > 1) {
+          keywords = this.searchKeyword.split(",");
+        } else {
+          keywords = this.searchKeyword.split(",");
+        }
+        this.data = [];
+        let data2 = [];
+        for (var a = 0; a < keywords.length; a++) {
+          let _data = await this.searchBilibili(keywords[a]);
+          data2[a] = _data;
+        }
+        this.data = _.flatMap(_.zip(...data2), (pair) => pair.filter(value => value !== undefined));
+        this.currentPage = 1;
+        this.updateTotalPages();
+        this.loading = false;
+      } catch (error) {
+        console.log(error);
+        this.loading = false;
+        this.$message.error("搜索失败,请重试");
+      }
+    },
+    // Bilibili搜索接口
+    searchBilibili(keyword) {
+      return new Promise((resolve, reject) => {
+        this.ajax.post(`https://gpt4.cocorobo.cn/get_network_search`, {
+          engine: "bilibiliNew",
+          keyword: keyword,
+          page: 1,
+          page_size: 20,
+          order: this.navActive,
+          duration: 0,
+        }).then(response => {
+          console.log(response);
+          resolve(response.data.FunctionResponse || []);
+        }).catch((error) => {
+          console.log(error);
+          resolve([]);
+        });
+      });
+    },
+    // 切换排序方式
+    navClick(value) {
+      if (this.navActive == value) {
+        return;
+      }
+      this.navActive = value;
+      if (this.searchKeyword) {
+        this.handleSearch();
+      }
+    },
+    // 打开视频链接
+    openUrl(bvid) {
+      window.open(`//www.bilibili.com/video/${bvid}`);
+    },
+    // 添加单个视频
+    addSingleVideo(item) {
+      const videoTitle = item.title.replace(/<[^>]*>?/gm, ''); // 移除HTML标签
+      const videoData = {
+        name: "链接",
+        title: item.title.replace(/<[^>]*>?/gm, ''),
+        url: `//player.bilibili.com/player.html?isOutside=true&bvid=${item.bvid}`,
+        id: new Date().getTime(),
+        bvid: item.bvid,
+        type: 75  // B站视频使用type 75
+      };
+      this.$emit('addVideo', videoData);
+      this.$message.success('加入成功');
+      // 添加成功后关闭弹窗
+      this.handleClose();
+    },
+    // 检查视频是否被选中
+    isVideoSelected(item) {
+      return this.selectedVideos.some(v => v.bvid === item.bvid);
+    },
+    // 切换视频选择状态
+    toggleVideoSelection(item) {
+      const index = this.selectedVideos.findIndex(v => v.bvid === item.bvid);
+      if (index > -1) {
+        this.selectedVideos.splice(index, 1);
+      } else {
+        this.selectedVideos.push(item);
+      }
+    },
+    // 批量添加选中的视频
+    batchAdd() {
+      if (this.selectedVideos.length === 0) {
+        this.$message.warning('请先选择要添加的视频');
+        return;
+      }
+      this.selectedVideos.forEach(video => {
+        const videoTitle = video.title.replace(/<[^>]*>?/gm, ''); // 移除HTML标签
+        const videoData = {
+          name: "链接",
+          title: video.title.replace(/<[^>]*>?/gm, ''),
+          url: `//player.bilibili.com/player.html?isOutside=true&bvid=${video.bvid}`,
+          id: new Date().getTime(),
+          bvid: video.bvid,
+          type: 75  // B站视频使用type 75
+        };
+        this.$emit('addVideo', videoData);
+      });
+      this.$message.success(`成功添加 ${this.selectedVideos.length} 个视频`);
+      this.selectedVideos = [];
+      // 批量添加成功后关闭弹窗
+      this.handleClose();
+    },
+    // 清空选择
+    clearSelection() {
+      this.selectedVideos = [];
+    },
+    // 更新总页数
+    updateTotalPages() {
+      this.totalPages = Math.ceil(this.data.length / this.pageSize);
+    },
+    // 上一页
+    prevPage() {
+      if (this.currentPage > 1) {
+        this.currentPage--;
+      }
+    },
+    // 下一页
+    nextPage() {
+      if (this.currentPage < this.totalPages) {
+        this.currentPage++;
+      }
+    }
+  },
+  computed: {
+    // 获取当前页的数据
+    paginatedData() {
+      const start = (this.currentPage - 1) * this.pageSize;
+      const end = start + this.pageSize;
+      return this.data.slice(start, end);
+    }
+  }
+};
+</script>
+
+<style scoped>
+.dialog_diy>>>.el-dialog {
+    height: auto;
+    margin: 50px auto 0 !important;
+}
+
+.dialog_diy>>>.el-dialog__header {
+    background: #454545 !important;
+    padding: 15px 20px;
+}
+
+.dialog_diy>>>.el-dialog__body {
+    height: calc(100% - 124px);
+    box-sizing: border-box;
+    padding: 0px;
+}
+
+.dialog_diy>>>.el-dialog__title {
+    color: #fff;
+}
+
+.dialog_diy>>>.el-dialog__headerbtn {
+    top: 19px;
+}
+
+.dialog_diy>>>.el-dialog__headerbtn .el-dialog__close {
+    color: #fff;
+}
+
+.dialog_diy>>>.el-dialog__headerbtn .el-dialog__close:hover {
+    color: #fff;
+}
+
+.dialog_diy>>>.el-dialog__body,
+.dialog_diy>>>.el-dialog__footer {
+    background: #fafafa;
+}
+
+/* 搜索和筛选区域 */
+.search-filter-box {
+    display: flex;
+    align-items: center;
+    gap: 15px;
+    margin-bottom: 15px;
+    padding: 10px 0;
+}
+
+.search-item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    gap: 10px;
+}
+
+.search-label {
+    min-width: 80px;
+    font-size: 14px;
+    color: #606266;
+    text-align: right;
+}
+
+.search-input-wrapper {
+    flex: 1;
+    position: relative;
+}
+
+.inputC>>>.el-input__inner {
+    padding-right: 35px;
+    height: 40px;
+    line-height: 40px;
+}
+
+.search_img {
+    width: 20px;
+    height: 20px;
+    position: absolute;
+    right: 10px;
+    top: 50%;
+    transform: translateY(-50%);
+    cursor: pointer;
+    z-index: 10;
+}
+
+.search_img>img {
+    width: 100%;
+    height: 100%;
+}
+
+.filter-item {
+    width: 150px;
+}
+
+.sort-select {
+    width: 100%;
+}
+
+.add-item {
+    width: 80px;
+}
+
+.Box {
+    width: 100%;
+    height: 500px;
+    overflow: auto;
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-start;
+    flex-wrap: wrap;
+    padding: 5px;
+    box-sizing: border-box;
+}
+
+.video_box {
+    width: calc(50% - 10px);
+    margin-right: 10px;
+    display: flex;
+    flex-direction: column;
+    margin-bottom: 15px;
+    cursor: pointer;
+}
+
+.video_box2 {
+    box-shadow: 0px 0px 4px 2px #d2d2d282;
+    padding: 10px;
+    box-sizing: border-box;
+    border-radius: 5px;
+    position: relative;
+    background: #fff;
+    cursor: pointer;
+    transition: all 0.2s;
+    border: 2px solid transparent;
+}
+
+.video_box2:hover {
+    box-shadow: 0px 0px 8px 4px #d2d2d282;
+    transform: translateY(-2px);
+}
+
+.video_box2.selected {
+    border: 2px solid #409eff;
+    /* box-shadow: 0px 0px 8px 4px rgba(64, 158, 255, 0.3); */
+}
+
+.video-checkbox {
+    position: absolute;
+    top: 8px;
+    right: 8px;
+    z-index: 10;
+    background: rgba(255, 255, 255, 0.95);
+    padding: 2px;
+    border-radius: 4px;
+}
+
+.video-thumbnail {
+    width: 100%;
+    height: 120px;
+    background: #f0f0f0;
+    border-radius: 4px;
+    margin-bottom: 8px;
+    margin-top: 5px;
+}
+
+.video_box>.detail {
+    color: #cecece;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 3;
+    line-clamp: 3;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    margin: 0 0 5px;
+}
+
+.video_box>.name {
+    color: #000;
+    margin: 5px 0 8px 0;
+    font-weight: bold;
+    font-size: 14px;
+    min-height: 22px;
+    max-height: 44px;
+    line-height: 22px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: block;
+    white-space: normal;
+    word-break: break-all;
+    /* 强制换行成两行,如果内容过多则省略号,且不会折成一行 */
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+    width: calc(100% - 20px);
+}
+
+.video-info {
+    display: flex;
+    flex-direction: column;
+    gap: 5px;
+    margin-bottom: 8px;
+    font-size: 12px;
+    color: #909399;
+}
+
+.video_box>.author {
+    margin: 0;
+    width: 100%;
+    display: block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.video-info .duration {
+    margin: 0;
+    width: 100%;
+    display: block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.video_box>.tag {
+    display: flex;
+    flex-wrap: nowrap;
+    margin-top: auto;
+    width: calc(100% - 24px);
+}
+
+.video_box>.tag>span {
+    background: #eef3fb;
+    color: #0061ff;
+    padding: 5px 10px;
+    border-radius: 15px;
+    font-size: 0.9em;
+    margin: 0 5px 0 0;
+    max-width: 100%;
+    overflow: hidden;
+    box-sizing: border-box;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    text-align: center;
+}
+
+.external-link {
+    position: absolute;
+    bottom: 10px;
+    right: 10px;
+    width: 24px;
+    height: 24px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    border-radius: 4px;
+    transition: background 0.2s;
+}
+
+.external-link:hover {
+    background: rgba(0, 0, 0, 0.05);
+}
+
+.external-link svg {
+    width: 16px;
+    height: 16px;
+}
+
+.no_data {
+    height: 500px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+}
+
+.pagination-box {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin: 20px 0;
+    gap: 15px;
+}
+
+.page-info {
+    font-size: 14px;
+    color: #606266;
+}
+
+
+@media screen and (max-width: 1080px) {
+    .video_box {
+        width: calc(100% / 3 - 10px) !important;
+    }
+}
+
+@media screen and (max-width: 760px) {
+    .video_box {
+        width: calc(100% / 2 - 10px) !important;
+    }
+}
+</style>
+

+ 308 - 0
src/components/pages/pptEasy/dialog/VideoUploadDialog.vue

@@ -0,0 +1,308 @@
+<template>
+	<div>
+		<el-dialog
+			:center="true"
+			:visible.sync="show"
+			:close-on-click-modal="false"
+			:modal="true"
+			width="auto"
+			height="auto"
+			:append-to-body="true"
+			class="dialog"
+		>
+			<div class="box">
+				<div class="b_head">
+					<span>选择一种视频来源</span>
+					<svg
+						@click="close()"
+						t="1748587270371"
+						class="icon"
+						viewBox="0 0 1024 1024"
+						version="1.1"
+						xmlns="http://www.w3.org/2000/svg"
+						p-id="5023"
+						width="200"
+						height="200"
+					>
+						<path
+							d="M0 0h1024v1024H0z"
+							fill="#FF0033"
+							fill-opacity="0"
+							p-id="5024"
+						></path>
+						<path
+							d="M240.448 168l2.346667 2.154667 289.92 289.941333 279.253333-279.253333a42.666667 42.666667 0 0 1 62.506667 58.026666l-2.133334 2.346667-279.296 279.210667 279.274667 279.253333a42.666667 42.666667 0 0 1-58.005333 62.528l-2.346667-2.176-279.253333-279.253333-289.92 289.962666a42.666667 42.666667 0 0 1-62.506667-58.005333l2.154667-2.346667 289.941333-289.962666-289.92-289.92a42.666667 42.666667 0 0 1 57.984-62.506667z"
+							fill="#111111"
+							p-id="5025"
+						></path>
+					</svg>
+				</div>
+				<div class="b_main">
+					<div class="video-option" @click="handleLocalUpload">
+						<div class="option-icon">
+							<img src="../../../../assets/icon/ppt/videoIcon.svg" alt="本地文件" />
+						</div>
+						<div class="option-text">从本地文件上传</div>
+					</div>
+
+					<div class="video-option" @click="handleBilibiliSearch">
+						<div class="option-icon">
+							<img src="../../../../assets/icon/ppt/vbilibili.svg" alt="Bilibili" />
+						</div>
+						<div class="option-text">从Bilibili检索</div>
+					</div>
+				</div>
+			</div>
+		</el-dialog>
+		<!-- 隐藏的文件输入 -->
+		<input
+			ref="fileInput"
+			type="file"
+			accept="video/*"
+			style="display: none"
+			@change="handleFileChange"
+		/>
+	</div>
+</template>
+
+<script>
+import "../../../../common/aws-sdk-2.235.1.min.js";
+
+export default {
+	data() {
+		return {
+			show: false,
+			data: null,
+		};
+	},
+	computed: {},
+	methods: {
+		open(data) {
+			this.data = data;
+			this.show = true;
+		},
+		close() {
+			this.show = false;
+			this.init();
+		},
+		init() {
+			this.data = null;
+		},
+		// 处理本地文件上传
+		handleLocalUpload() {
+			this.$refs.fileInput.click();
+		},
+		// 处理文件选择
+		handleFileChange(event) {
+			const file = event.target.files[0];
+			if (file) {
+				// 验证文件类型
+				if (!file.type.startsWith('video/')) {
+					this.$message.error('请选择视频文件');
+					event.target.value = "";
+					return;
+				}
+				// 上传文件
+				this.uploadVideo(file);
+			}
+			// 重置input,以便可以再次选择同一个文件
+			event.target.value = "";
+		},
+		// 上传视频文件
+		uploadVideo(file) {
+			const uploadId = new Date().getTime();
+			const fileName = file.name;
+			
+			// 初始化进度信息
+			this.$emit("uploadProgress", {
+				id: uploadId,
+				fileName: fileName,
+				progress: 0,
+				loaded: 0,
+				total: file.size,
+				status: 'uploading'
+			});
+			
+			var credentials = {
+				accessKeyId: "AKIATLPEDU37QV5CHLMH",
+				secretAccessKey: "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"
+			};
+			window.AWS.config.update(credentials);
+			window.AWS.config.region = "cn-northwest-1";
+
+			var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } });
+			var _this = this;
+
+			var params = {
+				Key:
+					file.name.split(".")[0] +
+					new Date().getTime() +
+					"." +
+					file.name.split(".")[file.name.split(".").length - 1],
+				ContentType: file.type,
+				Body: file,
+				"Access-Control-Allow-Credentials": "*",
+				ACL: "public-read"
+			};
+			var options = {
+				partSize: 2048 * 1024 * 1024,
+				queueSize: 2,
+				leavePartsOnError: true
+			};
+			bucket
+				.upload(params, options)
+				.on("httpUploadProgress", function(evt) {
+					// 更新上传进度
+					const progress = Math.round((evt.loaded * 100) / evt.total);
+					_this.$emit("uploadProgress", {
+						id: uploadId,
+						fileName: fileName,
+						progress: progress,
+						loaded: evt.loaded,
+						total: evt.total,
+						status: 'uploading'
+					});
+				})
+				.send(function(err, data) {
+					if (err) {
+						// 上传失败,更新进度状态
+						_this.$emit("uploadProgress", {
+							id: uploadId,
+							fileName: fileName,
+							progress: 0,
+							loaded: 0,
+							total: file.size,
+							status: 'error'
+						});
+						_this.$message.error("上传失败");
+					} else {
+						// 上传成功,触发事件传递文件信息
+						_this.$emit("uploadLocalVideo", {
+							id: uploadId,
+							file: file,
+							url: data.Location,
+							name: file.name
+						});
+						_this.close();
+					}
+				});
+		},
+		// 处理Bilibili搜索
+		handleBilibiliSearch() {
+			// 触发Bilibili搜索事件
+			this.$emit("searchBilibili");
+			this.close();
+		},
+	},
+};
+</script>
+
+<style scoped>
+.dialog >>> .el-dialog {
+	width: 600px !important;
+	border-radius: 8px;
+	padding: 0;
+	background-color: #fff;
+	overflow: hidden;
+}
+
+.dialog >>> .el-dialog__body {
+	width: 600px !important;
+	height: auto;
+	flex-shrink: 0;
+	padding: 0;
+	box-sizing: border-box;
+	overflow: auto;
+}
+
+.dialog >>> .el-dialog__header {
+	display: none !important;
+}
+
+.box {
+	width: 100%;
+	min-height: 300px;
+	background: #fafafa;
+	border-radius: 15px;
+	box-shadow: 0px 6px 30px 5px rgba(0, 0, 0, 0.05),
+		0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 8px 10px -5px rgba(0, 0, 0, 0.08);
+}
+
+.b_head {
+	width: 100%;
+	height: 50px;
+	border-radius: 15px 15px 0 0;
+	background: #fff;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	box-sizing: border-box;
+	padding: 0 20px;
+}
+
+.b_head > span {
+	font-size: 18px;
+	font-weight: bold;
+	color: #000;
+}
+
+.b_head > svg {
+	width: 20px;
+	height: 20px;
+	cursor: pointer;
+}
+
+.b_main {
+	width: 100%;
+	min-height: 250px;
+	background: #fff;
+	padding: 40px 20px;
+	box-sizing: border-box;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	gap: 40px;
+}
+
+.video-option {
+	width: 200px;
+	height: 200px;
+	background: #fff;
+	border-radius: 12px;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	cursor: pointer;
+	transition: all 0.3s;
+	box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.video-option:hover {
+	transform: translateY(-5px);
+	box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.15);
+}
+
+.option-icon {
+	width: 120px;
+	height: 120px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	margin-bottom: 20px;
+}
+
+.option-icon img {
+	width: 100%;
+	height: 100%;
+	object-fit: contain;
+}
+
+.option-text {
+	font-size: 16px;
+	font-weight: 500;
+	color: #333;
+	text-align: center;
+}
+</style>
+

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio