Ver Fonte

1.提交ai对话中ai主动发送课程的代码

jzp há 3 dias atrás
pai
commit
9b1deab516

+ 7 - 0
src/api/fastGpt/fastGptRole.js

@@ -23,6 +23,13 @@ export function getAllRoleType() {
   })
 }
 
+export function getAllCourseList() {
+  return request({
+    url: '/fastGpt/fastGptRole/getAllCourseList',
+    method: 'get'
+  })
+}
+
 
 // 查询应用详细getAllRoleType
 export function getFastGptRole(roleId) {

+ 401 - 3
src/views/fastGpt/fastGptRole/fastGptRoleUpdate.vue

@@ -72,6 +72,95 @@
         </div>
       </el-form-item>
 
+      <el-form-item label="AI是否发送新客先导课" prop="sendCourseStatus">
+        <el-switch
+          v-model="form.sendCourseStatus"
+          :active-value="1"
+          :inactive-value="0"
+          active-text="是"
+          inactive-text="否"
+          active-color="#13ce66"
+          inactive-color="#ff4949"
+        />
+        <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+          <i class="el-icon-info"></i>
+          AI与客户聊天中是否发送新客先导课,默认关闭,开启后,与客户聊天会发送新客先导课
+        </div>
+      </el-form-item>
+
+      <el-form-item label="新客先导课" prop="courseId">
+        <el-popover
+          placement="bottom"
+          width="300"
+          trigger="click"
+          :visible="coursePopoverVisible"
+          @show="onCoursePopoverShow"
+        >
+          <div class="course-select-popover">
+            <el-input
+              v-if="courseFilterable"
+              placeholder="输入关键字过滤"
+              v-model="courseFilterText"
+              size="mini"
+              clearable
+              class="course-filter-input"
+            >
+            </el-input>
+
+            <div class="course-list-container" :style="{ height: courseListHeight + 'px' }">
+              <div
+                ref="courseVirtualList"
+                class="course-virtual-list"
+                @scroll="handleCourseScroll"
+                :style="{ height: courseListHeight + 'px', overflow: 'auto' }"
+              >
+                <div
+                  class="course-virtual-content"
+                  :style="{
+                    height: courseTotalHeight + 'px',
+                    position: 'relative',
+                    paddingTop: courseOffsetY + 'px'
+                  }"
+                >
+                  <div
+                    v-for="(item, index) in courseVisibleItems"
+                    :key="`${item.videoId}-${index}`"
+                    :style="{
+                      height: courseItemHeight + 'px',
+                      display: 'flex',
+                      alignItems: 'center',
+                      padding: '0 10px'
+                    }"
+                    :class="[
+                      'course-virtual-item',
+                      {
+                        'is-selected': form.courseId === item.videoId,
+                        'is-disabled': item.disabled
+                      }
+                    ]"
+                    @click="handleCourseItemClick(item)"
+                  >
+                    <span class="course-label" :title="item.title">{{ item.title }}</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <el-button slot="reference" style="min-width: 150px; max-width: 300px;">
+            {{ form.courseId ? getCourseTitle(form.courseId) : '请选择课程' }}
+            <i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+        </el-popover>
+
+        <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+          <i class="el-icon-info"></i>
+          选择课程:客户添加聊天后会根据聊天内容发送这节课程
+        </div>
+      </el-form-item>
+
+
+
       <el-form-item label="客服头像" prop="avatar">
         <ImageUpload v-model="form.avatar" type="image" :num="1" :width="150" :height="150" style="margin-top: 1%;" />
       </el-form-item>
@@ -237,7 +326,7 @@
 </template>
 
 <script>
-import { listFastGptRole, getFastGptRole, delFastGptRole, addFastGptRole, updateFastGptRole, exportFastGptRole,getAllRoleType } from "@/api/fastGpt/fastGptRole";
+import { listFastGptRole, getFastGptRole, delFastGptRole, addFastGptRole, updateFastGptRole, exportFastGptRole,getAllRoleType,getAllCourseList } from "@/api/fastGpt/fastGptRole";
 import {allListTagGroup} from "@/api/qw/tagGroup";
 import {listTag} from "@/api/qw/tag";
 import {
@@ -264,6 +353,23 @@ export default {
       exportLoading: false,
       //AI客服类型
       typeOptions: [],
+
+      // 课程选择相关数据
+      coursePopoverVisible: false,
+      courseFilterText: '',
+      courseFilterable: true,
+      courseListHeight: 300, // 课程列表高度
+      courseItemHeight: 36, // 每个课程项的高度
+      courseStartIndex: 0,
+      courseEndIndex: 0,
+      courseScrollTop: 0,
+      courseVisibleItemCount: 0,
+      courseFlattenedNodes: [], // 扁平化的课程数据
+      courseNodeMap: new Map(), // 课程节点映射
+      courseUpdateTimer: null,
+      courseScrollRAF: null,
+      courseFilterDebounced: null,
+
       //AI模型
       modeOptions: [],
       //渠道类型
@@ -337,7 +443,11 @@ export default {
         channelType: null,
         logistics: null,
         forbidStatus: null,
+        sendCourseStatus: null,
+        courseId: null,
       },
+      // ... 其他数据
+      courseListLoaded: false,
       // 表单参数
       form: {
       },
@@ -434,9 +544,35 @@ export default {
       deep: true
     }
   },
-  created() {
-
+  computed: {
+    courseTotalHeight() {
+      return this.courseVisibleNodes.length * this.courseItemHeight;
+    },
+    courseOffsetY() {
+      return this.courseStartIndex * this.courseItemHeight;
+    },
+    courseVisibleNodes() {
+      let nodes = this.courseFlattenedNodes;
+      if (this.courseFilterText.trim()) {
+        nodes = this.applyCourseFilter(nodes);
+      }
+      return nodes;
+    },
+    courseVisibleItems() {
+      return this.courseVisibleNodes.slice(this.courseStartIndex, this.courseEndIndex);
+    }
+  },
+  beforeDestroy() {
+    // 清理定时器
+    if (this.courseUpdateTimer) clearTimeout(this.courseUpdateTimer);
+    if (this.courseScrollRAF) cancelAnimationFrame(this.courseScrollRAF);
+  },
+  async created() {
     this.handleUpdate();
+    await this.loadCourseList();
+    this.courseListLoaded = true
+    // 初始化课程选择相关数据
+    this.initCourseData();
     //客服类型
     // this.getDicts("chat_role_type").then((response) => {
     //   this.typeOptions = response.data;
@@ -457,7 +593,269 @@ export default {
       this.typeOptions = response.data;
     });
   },
+  mounted() {
+    this.$nextTick(() => {
+      // 确保 DOM 已渲染完成
+      this.initScrollListener();
+    });
+  },
   methods: {
+    // 初始化课程选择相关数据
+    initCourseData() {
+      this.courseVisibleItemCount = Math.ceil(this.courseListHeight / this.courseItemHeight) + 5;
+
+      // 防抖函数
+      this.handleCourseFilterDebounced = this.debounce(this.filterCourseTextChange, 300);
+    },
+
+    // 防抖函数
+    debounce(func, wait) {
+      let timeout;
+      return function executedFunction(...args) {
+        const later = () => {
+          clearTimeout(timeout);
+          func(...args);
+        };
+        clearTimeout(timeout);
+        timeout = setTimeout(later, wait);
+      };
+    },
+
+    // 处理课程数据
+    processCourseData(data) {
+      if (!Array.isArray(data)) {
+        this.courseFlattenedNodes = [];
+        this.courseNodeMap.clear();
+        return;
+      }
+
+      const flattened = [];
+      const nodeMap = new Map();
+
+      data.forEach((item, index) => {
+        const courseItem = {
+          videoId: item.videoId && typeof item.videoId === 'string' ? item.videoId : String(item.videoId || Math.random()),
+          title: item.title && typeof item.title === 'string' ? item.title : String(item.title || item.name || item.videoId || '未命名课程'),
+          originalData: item,
+          disabled: false
+        };
+
+        flattened.push(courseItem);
+        nodeMap.set(courseItem.videoId, courseItem);
+      });
+
+      this.courseFlattenedNodes = flattened;
+      this.courseNodeMap = nodeMap;
+    },
+
+    // 应用课程过滤
+    applyCourseFilter(nodesToFilter) {
+      const searchText = this.courseFilterText.toLowerCase().trim();
+      if (!searchText) return nodesToFilter;
+
+      return nodesToFilter.filter(node =>
+        node.title.toLowerCase().includes(searchText)
+      );
+    },
+
+    // 计算可见范围
+    calculateCourseVisibleRange() {
+      const containerHeight = this.courseListHeight;
+      const totalItems = this.courseVisibleNodes.length;
+
+      const newStartIndex = Math.floor(this.courseScrollTop / this.courseItemHeight);
+      const visibleCountInViewport = Math.ceil(containerHeight / this.courseItemHeight);
+
+      const buffer = 5;
+      this.courseStartIndex = Math.max(0, newStartIndex - buffer);
+      this.courseEndIndex = Math.min(totalItems, newStartIndex + visibleCountInViewport + buffer);
+    },
+
+    // 滚动处理
+    handleCourseScroll(e) {
+      const newScrollTop = e.target.scrollTop;
+      if (newScrollTop !== this.courseScrollTop) {
+        this.courseScrollTop = newScrollTop;
+        if (!this.courseScrollRAF) {
+          this.courseScrollRAF = requestAnimationFrame(() => {
+            this.calculateCourseVisibleRange();
+            this.courseScrollRAF = null;
+          });
+        }
+      }
+    },
+
+    // 重置滚动
+    resetCourseScroll() {
+      this.courseScrollTop = 0;
+      if (this.$refs.courseVirtualList) {
+        this.$refs.courseVirtualList.scrollTop = 0;
+      }
+      this.$nextTick(() => {
+        this.calculateCourseVisibleRange();
+      });
+    },
+
+    // 课程过滤文本变化
+    filterCourseTextChange() {
+      this.resetCourseScroll();
+    },
+
+    // 课程项点击
+    handleCourseItemClick(course) {
+      if (course.disabled) return;
+      this.form.courseId = course.videoId;
+      this.coursePopoverVisible = false;
+    },
+
+    // 课程弹出框显示
+    onCoursePopoverShow() {
+      this.$nextTick(() => {
+        this.resetCourseScroll();
+        if (this.courseFilterable && this.$refs.courseVirtualList) {
+          const inputEl = this.$refs.courseVirtualList.parentElement.querySelector('.course-filter-input input');
+          if (inputEl) inputEl.focus();
+        }
+      });
+    },
+
+    // 初始化滚动监听器
+    initScrollListener() {
+      this.$nextTick(() => {
+        const container = this.$refs.courseSelectContainer;
+        if (container) {
+          // 移除之前的事件监听器,避免重复绑定
+          container.removeEventListener('scroll', this.handleScroll);
+
+          // 添加新的滚动事件监听器
+          container.addEventListener('scroll', this.handleScroll);
+
+          // 添加初始调试信息
+          console.log('滚动监听器已初始化');
+          console.log('容器总高度:', container.scrollHeight);
+          console.log('可见高度:', container.clientHeight);
+          console.log('课程总数:', this.allCourseOptions.length);
+          console.log('当前显示:', this.displayCourseOptions.length);
+        }
+      });
+    },
+    getCourseTitle(videoId) {
+      if (!videoId) return '请选择课程';
+
+      console.log(videoId);
+      // 优先从处理后的课程数据中查找
+      const course = this.courseNodeMap.get(String(videoId));
+      if (course) {
+        return course.title;
+      }
+
+      // 如果在虚拟滚动数据中找不到,尝试从原始数据中查找
+      const originalCourse = this.allCourseOptions.find(item => item.videoId === videoId);
+      return originalCourse ? originalCourse.title : '未找到对应课程';
+    },
+    // 滚动处理函数
+    // 滚动处理函数 - 改进检测逻辑
+    // 滚动处理函数 - 改进检测逻辑
+    handleScroll(event) {
+      const container = event.target;
+      const { scrollTop, scrollHeight, clientHeight } = container;
+
+      // 更精确的滚动到底部检测
+      const threshold = 5; // 阈值调整为5px
+      if (scrollTop + clientHeight >= scrollHeight - threshold &&
+        !this.loadingMore &&
+        this.hasMore) {
+        this.loadMoreCourses();
+      }
+    },
+
+    // 修复滚动加载方法,添加更详细的日志
+    // 修复后的滚动加载方法
+    async loadMoreCourses() {
+      if (this.loadingMore || !this.hasMore) {
+        console.log('加载条件不满足:', { loadingMore: this.loadingMore, hasMore: this.hasMore });
+        return;
+      }
+
+      console.log('开始加载更多课程');
+      this.loadingMore = true;
+
+      try {
+        // 短暂延迟以避免频繁请求
+        await new Promise(resolve => setTimeout(resolve, 300));
+
+        // 使用全部课程列表
+        const sourceList = this.allCourseOptions;
+
+        // 修复分页逻辑:从 (currentPage - 1) * pageSize 开始
+        const start = (this.currentPage - 1) * this.pageSize;
+        const end = start + this.pageSize;
+        const moreCourses = sourceList.slice(start, end);
+
+        console.log('加载的课程范围:', { start, end, count: moreCourses.length });
+
+        if (moreCourses.length > 0) {
+          // 添加新课程到显示列表(注意:使用 concat 或扩展运算符避免重复添加)
+          this.displayCourseOptions = [...this.displayCourseOptions, ...moreCourses];
+          this.currentPage++; // 递增页码
+          // 检查是否还有更多课程
+          this.hasMore = end < sourceList.length;
+          console.log('更新后的课程列表长度:', this.displayCourseOptions.length, '还有更多:', this.hasMore);
+        } else {
+          // 没有更多课程时停止加载
+          this.hasMore = false;
+          console.log('没有更多课程可加载');
+        }
+      } catch (error) {
+        console.error('加载更多课程失败:', error);
+        this.hasMore = false;
+      } finally {
+        this.loadingMore = false;
+      }
+    },
+
+    async loadCourseList() {
+      try {
+        const response = await getAllCourseList();
+        if (Array.isArray(response.data)) {
+          this.courseOptions = response.data
+            .filter(item => item !== null && item !== undefined)
+            .map(item => ({
+              ...item,
+              videoId: item.videoId && typeof item.videoId === 'string' ? item.videoId : String(item.videoId || Math.random()),
+              title: item.title && typeof item.title === 'string' ? item.title : String(item.title || item.name || item.videoId || '未命名课程')
+            }));
+
+          this.allCourseOptions = [...this.courseOptions];
+          // 重置分页参数 - 从第一页开始
+          this.currentPage = 1;
+          // 默认展示前100条
+          this.displayCourseOptions = this.courseOptions.slice(0, this.pageSize);
+          this.hasMore = this.courseOptions.length > this.pageSize;
+
+          // 处理课程数据为虚拟滚动格式
+          this.processCourseData(this.courseOptions);
+        } else {
+          console.warn('getAllCourseList 返回的数据不是数组格式:', response.data);
+          this.courseOptions = [];
+          this.displayCourseOptions = [];
+          this.allCourseOptions = [];
+          this.hasMore = false;
+          this.courseFlattenedNodes = [];
+          this.courseNodeMap.clear();
+        }
+      } catch (error) {
+        console.error('课程列表加载失败:', error);
+        this.courseOptions = [];
+        this.displayCourseOptions = [];
+        this.allCourseOptions = [];
+        this.hasMore = false;
+        this.courseFlattenedNodes = [];
+        this.courseNodeMap.clear();
+      }
+    },
+
+
     /** 查询应用列表 */
     getList() {
       this.loading = true;