Pārlūkot izejas kodu

添加搜索功能

lex-xin 6 mēneši atpakaļ
vecāks
revīzija
0af90a451d

+ 3 - 2
src/api/cloudTextbooks.api.ts

@@ -25,10 +25,11 @@ export const getLessonCoursewareSubjectList_gym = () => {
 }
 }
 
 
 // 课程详情列表
 // 课程详情列表
-export const getLessonCoursewareCourseList_gym = (id: string) => {
+export const getLessonCoursewareCourseList_gym = (params: { id: string; search?: string; abortController: AbortController }) => {
    return httpAxios_gym.axioseRquest({
    return httpAxios_gym.axioseRquest({
+      signal: params.abortController.signal,
       method: "get",
       method: "get",
-      url: "/api-teacher/lessonCourseware/getLessonCoursewareCourseList/" + id
+      url: `/api-teacher/lessonCourseware/getLessonCoursewareCourseList/${params.id}?search=${params.search || ""}`
    })
    })
 }
 }
 
 

BIN
src/img/cloudTextbooks/classlist-active.png


BIN
src/img/cloudTextbooks/classlist.png


BIN
src/img/cloudTextbooks/icon-img.png


BIN
src/img/cloudTextbooks/icon-menu.png


BIN
src/img/cloudTextbooks/icon-sin-arrow.png


BIN
src/img/cloudTextbooks/icon-song.png


BIN
src/img/cloudTextbooks/icon-video.png


BIN
src/img/cloudTextbooks/search-active.png


BIN
src/img/cloudTextbooks/search.png


BIN
src/img/coursewarePlay/arrow-up.png


BIN
src/img/coursewarePlay/sousuo.png


+ 114 - 2
src/views/cloudTextbooks/chooseDialog.vue

@@ -6,7 +6,18 @@
 <template>
 <template>
    <div class="chooseDialog">
    <div class="chooseDialog">
       <div class="close" @click="close"></div>
       <div class="close" @click="close"></div>
-      <div class="chooseCon" v-loading="loading">
+      <div class="chooseHeader" v-if="isShowTabs">
+         <img
+            key="classlist"
+            v-if="chooseType === 'classlist'"
+            src="@/img/cloudTextbooks/classlist-active.png"
+            @click="handleChangeType('classlist')"
+         />
+         <img v-else src="@/img/cloudTextbooks/classlist.png" @click="handleChangeType('classlist')" />
+         <img key="search" v-if="chooseType === 'search'" src="@/img/cloudTextbooks/search-active.png" @click="handleChangeType('search')" />
+         <img v-else src="@/img/cloudTextbooks/search.png" @click="handleChangeType('search')" />
+      </div>
+      <div class="chooseCon" v-loading="loading" v-show="chooseType === 'classlist'">
          <img class="imgMid" src="@/img/cloudTextbooks/shu.png" />
          <img class="imgMid" src="@/img/cloudTextbooks/shu.png" />
          <div class="chooseBox" v-for="(listDetails, index) in listDetailData" :key="index">
          <div class="chooseBox" v-for="(listDetails, index) in listDetailData" :key="index">
             <div
             <div
@@ -36,14 +47,47 @@
             </div>
             </div>
          </div>
          </div>
       </div>
       </div>
+
+      <div class="chooseCon" v-loading="searchLoading" v-show="chooseType === 'search'">
+         <div class="chooseBox chooseBoxSearch">
+            <div class="chooseList">
+               <myInput
+                  class="queryIpt"
+                  v-model="queryStr"
+                  :height="40"
+                  placeholder="请输入素材关键字"
+                  clearable
+                  @handleQuery="handleQuery"
+                  @keyup.enter="handleQuery"
+               />
+               <searchCollapse :search="tempSearch" :activeCollapse="activeCollapse" :courseList="listSearchData" @handleClick="handleCourseClick" />
+               <el-empty
+                  class="empty"
+                  v-if="!listSearchData.length && !searchLoading"
+                  :image="require('@/img/layout/empty.png')"
+                  description="暂无搜索结果"
+               />
+            </div>
+         </div>
+      </div>
    </div>
    </div>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
+import { computed, ref } from "vue"
 import { useDataDetailList } from "./useData"
 import { useDataDetailList } from "./useData"
-const { handleGetDetailList, loading, listDetailData, listData, pageNum, handlePage } = useDataDetailList()
+import myInput from "@/components/myInput"
+import searchCollapse from "./searchCollapse"
+import userStore from "@/store/modules/user"
+const { handleGetDetailList, loading, searchLoading, listDetailData, listSearchData, activeCollapse, listData, pageNum, handlePage } =
+   useDataDetailList()
 import router from "@/router"
 import router from "@/router"
+import { c } from "naive-ui"
+const userStoreHook = userStore()
 
 
+const chooseType = ref<"classlist" | "search">("classlist")
+const queryStr = ref("")
+const tempSearch = ref("")
 const props = defineProps<{
 const props = defineProps<{
    modalData: {
    modalData: {
       id: string
       id: string
@@ -55,6 +99,10 @@ const emits = defineEmits<{
 
 
 handleGetDetailList(props.modalData.id)
 handleGetDetailList(props.modalData.id)
 
 
+const isShowTabs = computed(() => {
+   return userStoreHook.roles === "GYM" ? true : false
+})
+
 function close() {
 function close() {
    emits("onClose")
    emits("onClose")
 }
 }
@@ -72,6 +120,29 @@ function handlePaly(id: string) {
       }
       }
    })
    })
 }
 }
+
+function handleCourseClick(value: any) {
+   router.push({
+      name: "coursewarePlay",
+      params: {
+         id: props.modalData.id
+      },
+      query: {
+         source: "search",
+         search: queryStr.value,
+         materialId: value.id
+      }
+   })
+}
+
+function handleChangeType(type: "classlist" | "search") {
+   chooseType.value = type
+}
+
+function handleQuery() {
+   tempSearch.value = queryStr.value
+   handleGetDetailList(props.modalData.id, true, queryStr.value)
+}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -93,6 +164,18 @@ function handlePaly(id: string) {
          background-size: cover;
          background-size: cover;
       }
       }
    }
    }
+
+   .chooseHeader {
+      position: absolute;
+      top: -7px;
+      left: 54px;
+      img {
+         width: 131px;
+         height: 49px;
+         margin-right: 12px;
+      }
+   }
+
    .chooseCon {
    .chooseCon {
       width: 100%;
       width: 100%;
       height: 100%;
       height: 100%;
@@ -234,6 +317,35 @@ function handlePaly(id: string) {
             }
             }
          }
          }
       }
       }
+      .chooseBoxSearch {
+         width: 100%;
+
+         .chooseList {
+            padding: 20px 80px;
+            display: flex;
+            flex-direction: column;
+
+            .courseCollapse {
+               margin-top: 16px;
+               flex: 1;
+               overflow-y: auto;
+               overflow-x: hidden;
+               &::-webkit-scrollbar {
+                  display: none;
+               }
+            }
+
+            &:deep(.empty) {
+               position: absolute;
+               top: 50%;
+               left: 50%;
+               transform: translate(-50%, -50%);
+               .el-empty__image {
+                  width: 360px;
+               }
+            }
+         }
+      }
    }
    }
 }
 }
 </style>
 </style>

+ 2 - 0
src/views/cloudTextbooks/searchCollapse/index.ts

@@ -0,0 +1,2 @@
+import searchCollapse from "./searchCollapse.vue"
+export default searchCollapse

+ 351 - 0
src/views/cloudTextbooks/searchCollapse/searchCollapse.vue

@@ -0,0 +1,351 @@
+<!--
+* @FileDescription: 折叠菜单
+* @Author: 黄琪勇
+* @Date:2024-04-01 18:40:50
+-->
+<template>
+   <el-collapse class="courseCollapse" accordion v-model="activeCollapseId">
+      <el-collapse-item v-for="item in props.courseList" :key="item.id" :name="item.id" :class="{ isChild: props.isChild }">
+         <template #title>
+            <div class="courseCollapseHead">
+               <div class="courseCollapseHeadArrow" v-if="!props.isChild">
+                  <img src="@/img/cloudTextbooks/icon-menu.png" />
+               </div>
+               <div class="courseCollapseHeadTit" :class="{ materialHead: item.materialList }">
+                  {{ item.name }}
+               </div>
+            </div>
+         </template>
+
+         <div class="courseCollapseCon" :class="{ courseListCon: item.materialList }">
+            <template v-if="item.materialList">
+               <div
+                  class="courseList"
+                  :class="{ isActive: activeCollapse?.id === i.id && activeCollapse?.knowledgePointId === i.knowledgePointId }"
+                  v-for="i in item.materialList"
+                  :key="i.id"
+                  @click="handleClick(i)"
+               >
+                  <div class="courseTitleCon" :class="i.typeCode || i.type">
+                     <div class="imgIcon"></div>
+                     <div class="ellipsisBox">
+                        <span v-html="formatName(i.name)"></span>
+                     </div>
+                     <div class="arrow-more"></div>
+                  </div>
+               </div>
+            </template>
+            <searchCollapse
+               v-else
+               :isChild="true"
+               :search="searchStr"
+               :courseList="item.children || []"
+               :activeCollapse="activeCollapse"
+               @handleClick="handleClick"
+            />
+         </div>
+      </el-collapse-item>
+   </el-collapse>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from "vue"
+
+type materialListType = {
+   id: string
+   type: string
+   typeCode?: string
+   name: string
+   knowledgePointId: string
+}
+type courseListType = {
+   id: string
+   name: string
+   materialList: materialListType[] | null
+   children: courseListType | null
+}[]
+
+const props = defineProps<{
+   activeCollapse: undefined | Record<string, any>
+   courseList: courseListType
+   search?: string
+   isChild?: boolean
+}>()
+const searchStr = ref(props.search)
+
+const emits = defineEmits<{
+   (e: "handleClick", value: any): void
+}>()
+watch(
+   () => props.activeCollapse,
+   () => {
+      activeCollapseId.value = filterActiveId()
+   }
+)
+watch(
+   () => props.search,
+   () => {
+      searchStr.value = props.search
+   }
+)
+
+const activeCollapseId = ref(filterActiveId())
+
+function formatName(name: string) {
+   if (!name || !searchStr.value) return name
+   const search: any = searchStr.value
+   return name.replace(search, `<span style="color: #F67146;">${search}</span>`)
+}
+
+function filterActiveId() {
+   const course = props.courseList.find(item => {
+      return (props.activeCollapse?.parentData.ids || []).includes(item.id)
+   })
+   return course?.id || ""
+   // return ""
+}
+function handleClick(value: any) {
+   emits("handleClick", value)
+}
+</script>
+
+<style lang="scss" scoped>
+.courseCollapse.el-collapse {
+   --el-collapse-border-color: #f2f2f2;
+   --el-collapse-header-height: 56px;
+   border: none;
+   & > :deep(.el-collapse-item) {
+      margin-bottom: 12px;
+      background: #fff;
+      border-radius: 12px;
+      overflow: hidden;
+      padding: 0 20px;
+      > .el-collapse-item__wrap {
+         border-bottom: none;
+      }
+      > .el-collapse-item__wrap > .el-collapse-item__content {
+         padding-bottom: 0px;
+      }
+      > .el-collapse-item__header {
+         border-bottom-color: transparent;
+         &.is-active {
+            border-bottom-color: #f2f2f2;
+         }
+      }
+      &:last-child {
+         > .el-collapse-item__wrap {
+            border-bottom: none;
+         }
+         > .el-collapse-item__header {
+            border-bottom: none;
+         }
+      }
+      .el-collapse-item__arrow {
+         // display: none;
+         svg {
+            display: none;
+         }
+         background: url("@/img/coursewarePlay/arrow-up.png") no-repeat;
+         background-size: 100% 100%;
+         transform: rotate(180deg);
+
+         &.is-active {
+            background: url("@/img/coursewarePlay/arrow-up.png") no-repeat;
+            background-size: 100% 100%;
+            transform: rotate(0deg);
+         }
+
+         // .headArrow,
+         // .headArrowActive {
+         //    width: 14px;
+         //    height: 14px;
+         // }
+         // .headArrow {
+         //    background: url("@/img/coursewarePlay/jtr.png") no-repeat;
+         //    background-size: 100% 100%;
+         // }
+         // .headArrowActive {
+         //    display: none;
+         //    background: url("@/img/coursewarePlay/jtb.png") no-repeat;
+         //    background-size: 100% 100%;
+         // }
+      }
+      &.is-active > .el-collapse-item__header {
+         > .courseCollapseHead {
+            .courseCollapseHeadTit {
+               color: #333333;
+               font-weight: 600;
+            }
+            .courseCollapseHeadArrow {
+               > .headArrow {
+                  display: none;
+               }
+               > .headArrowActive {
+                  display: block;
+               }
+            }
+         }
+      }
+      &.isChild {
+         margin-bottom: 0;
+
+         .el-collapse-item__wrap {
+            border-bottom: none;
+         }
+         .el-collapse-item__header {
+            border-bottom: none;
+         }
+         .courseCollapseHead {
+            .courseCollapseHeadTit {
+               color: #333333;
+               font-size: 15px;
+            }
+            .materialHead {
+               display: flex;
+               align-items: center;
+               &::before {
+                  content: "";
+                  display: inline-block;
+                  border-radius: 50%;
+                  width: 4px;
+                  height: 4px;
+                  background: #f67146;
+                  margin-right: 6px;
+               }
+            }
+            .courseCollapseHeadArrow {
+               .headArrow {
+                  background: url("@/img/coursewarePlay/jtr1.png") no-repeat;
+                  background-size: 100% 100%;
+               }
+            }
+         }
+      }
+   }
+   .courseCollapseHead {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      border-bottom: none;
+      .courseCollapseHeadTit {
+         text-align: left;
+         margin-left: 0;
+         flex-grow: 1;
+         font-weight: 400;
+         font-size: 16px;
+         color: #777777;
+         overflow: hidden;
+      }
+      .courseCollapseHeadArrow {
+         flex-shrink: 0;
+         font-size: 0;
+         > img {
+            width: 36px;
+            height: 40px;
+            margin-right: 8px;
+            vertical-align: middle;
+         }
+         .headArrow,
+         .headArrowActive {
+            width: 14px;
+            height: 14px;
+         }
+         .headArrow {
+            background: url("@/img/coursewarePlay/jtr.png") no-repeat;
+            background-size: 100% 100%;
+         }
+         .headArrowActive {
+            display: none;
+            background: url("@/img/coursewarePlay/jtb.png") no-repeat;
+            background-size: 100% 100%;
+         }
+      }
+   }
+
+   .courseCollapseCon {
+      padding-left: 0;
+      padding-right: 0;
+
+      &.courseListCon {
+         padding-left: 0;
+      }
+      .courseList {
+         display: flex;
+         justify-content: space-between;
+         align-items: center;
+         // padding: 0 10px 0 24px;
+         padding: 0;
+         margin-bottom: 6px;
+         cursor: pointer;
+         &.isActive {
+            background: #f0f0f0;
+            border-radius: 7px;
+            .courseTitleCon {
+               color: #f67146;
+               font-weight: 600;
+            }
+         }
+         .courseTitleCon {
+            padding: 12px 0 0 10px;
+            flex-grow: 1;
+            overflow: hidden;
+            margin-right: 8px;
+            display: flex;
+            align-items: center;
+            font-weight: 400;
+            font-size: 14px;
+            color: #333333;
+            > .ellipsisBox {
+               flex-grow: 1;
+               overflow: hidden;
+            }
+            > .imgIcon {
+               flex-shrink: 0;
+               width: 32px;
+               height: 32px;
+               margin-right: 10px;
+            }
+            &.VIDEO .imgIcon {
+               background: url("@/img/cloudTextbooks/icon-video.png") no-repeat;
+               background-size: 100% 100%;
+            }
+            &.IMG .imgIcon {
+               background: url("@/img/cloudTextbooks/icon-img.png") no-repeat;
+               background-size: 100% 100%;
+            }
+            &.SONG .imgIcon {
+               background: url("@/img/cloudTextbooks/icon-song.png") no-repeat;
+               background-size: 100% 100%;
+            }
+
+            .arrow-more {
+               flex-shrink: 0;
+               width: 12px;
+               height: 12px;
+               background: url("@/img/cloudTextbooks/icon-sin-arrow.png") no-repeat;
+               background-size: 100% 100%;
+            }
+         }
+         .iconArrow {
+            display: flex;
+            flex-shrink: 0;
+            width: 13px;
+            height: 13px;
+            > img {
+               width: 100%;
+               height: 100%;
+            }
+         }
+      }
+   }
+
+   .isChild {
+      padding: 0;
+      .courseCollapseHeadTit {
+         padding-left: 0;
+      }
+   }
+}
+</style>

+ 57 - 7
src/views/cloudTextbooks/useData.ts

@@ -206,31 +206,46 @@ type listDetail = {
 type listDetailType = [listDetail, listDetail]
 type listDetailType = [listDetail, listDetail]
 export const useDataDetailList = () => {
 export const useDataDetailList = () => {
    const userStoreHook = userStore()
    const userStoreHook = userStore()
+   let coursewareDetailController: AbortController
    const listData = shallowRef<listDetail[][]>([])
    const listData = shallowRef<listDetail[][]>([])
+   const listSearchData = shallowRef<any[]>([])
+   const activeCollapse = ref<any>({
+      parentData: {
+         ids: []
+      }
+   })
    const pageNum = ref<number>(0)
    const pageNum = ref<number>(0)
    const loading = ref(false)
    const loading = ref(false)
+   const searchLoading = ref(false)
    const listDetailData = computed<listDetailType>(() => {
    const listDetailData = computed<listDetailType>(() => {
       const data = listData.value[pageNum.value] || []
       const data = listData.value[pageNum.value] || []
       return [data[0] || [], data[1] || []]
       return [data[0] || [], data[1] || []]
    })
    })
-   function handleGetDetailList(id: string) {
+   function handleGetDetailList(id: string, isSearch = false, search?: string) {
       //  GYM,GYT,KLX 区分   查询详情列表
       //  GYM,GYT,KLX 区分   查询详情列表
       if (userStoreHook.roles === "GYM") {
       if (userStoreHook.roles === "GYM") {
-         handleGetDetaList_gym(id)
+         handleGetDetaList_gym(id, isSearch, search)
       } else if (userStoreHook.roles === "GYT") {
       } else if (userStoreHook.roles === "GYT") {
          handleGetDetailList_gyt(id)
          handleGetDetailList_gyt(id)
       } else if (userStoreHook.roles === "KLX") {
       } else if (userStoreHook.roles === "KLX") {
          handleGetDetailList_klx(id)
          handleGetDetailList_klx(id)
       }
       }
    }
    }
+
    function handlePage(type: "next" | "prev") {
    function handlePage(type: "next" | "prev") {
       type === "next" ? pageNum.value++ : pageNum.value--
       type === "next" ? pageNum.value++ : pageNum.value--
    }
    }
    // 获取管乐迷
    // 获取管乐迷
-   function handleGetDetaList_gym(id: string) {
-      loading.value = true
-      httpAjaxErrMsg(getLessonCoursewareCourseList_gym, id).then(res => {
-         loading.value = false
+   function handleGetDetaList_gym(id: string, isSearch = false, search?: string) {
+      if (coursewareDetailController) {
+         coursewareDetailController.abort()
+      }
+      coursewareDetailController = new AbortController()
+      if (!isSearch) loading.value = true
+      searchLoading.value = true
+      httpAjax(getLessonCoursewareCourseList_gym, { id, search, abortController: coursewareDetailController }).then(res => {
+         if (!isSearch) loading.value = false
+         searchLoading.value = false
          if (res.code === 200) {
          if (res.code === 200) {
             const data = (res.data || []).map((item: any) => {
             const data = (res.data || []).map((item: any) => {
                return {
                return {
@@ -238,7 +253,18 @@ export const useDataDetailList = () => {
                   id: item.coursewareDetailId
                   id: item.coursewareDetailId
                }
                }
             })
             })
-            listData.value = chunkArray(chunkArray(data, 7), 2)
+            if (!isSearch) {
+               listData.value = chunkArray(chunkArray(data, 7), 2)
+            }
+
+            const resultList = res.data || []
+            resultList.forEach((item: any) => {
+               item.children = item.knowledgePointList || []
+               item.id = item.coursewareDetailId
+               item.name = item.coursewareDetailName
+            })
+            listSearchData.value = filterPointList(resultList)
+            activeCollapse.value = { parentData: { ids: listSearchData.value.length > 0 ? [listSearchData.value[0].id] : [] } }
          }
          }
       })
       })
    }
    }
@@ -276,10 +302,34 @@ export const useDataDetailList = () => {
          }
          }
       })
       })
    }
    }
+
+   function filterPointList(pointList: any[], parentData?: { ids: string[]; name: string }): any[] {
+      // 设置父级及以上id数组和父级name
+      return pointList.map(point => {
+         if (point.children) {
+            return Object.assign(point, {
+               children: filterPointList(point.children, { ids: [...(parentData?.ids || []), point.id], name: point.name })
+            })
+         } else {
+            return Object.assign(point, {
+               materialList: point.materialList.map((item: any) => {
+                  item.parentData = {
+                     ids: [...(parentData?.ids || []), point.id],
+                     name: point.name
+                  }
+                  return item
+               })
+            })
+         }
+      })
+   }
    return {
    return {
       handleGetDetailList,
       handleGetDetailList,
       loading,
       loading,
       listDetailData,
       listDetailData,
+      searchLoading,
+      listSearchData,
+      activeCollapse,
       listData,
       listData,
       pageNum,
       pageNum,
       handlePage
       handlePage

+ 25 - 2
src/views/coursewarePlay/components/courseCollapse/courseCollapse.vue

@@ -29,7 +29,8 @@
                   <div class="courseTitleCon" :class="i.typeCode || i.type">
                   <div class="courseTitleCon" :class="i.typeCode || i.type">
                      <div class="imgIcon"></div>
                      <div class="imgIcon"></div>
                      <div class="ellipsisBox">
                      <div class="ellipsisBox">
-                        <ellipsisScroll :title="i.name" />
+                        <!-- <ellipsisScroll :title="i.name" /> -->
+                        <span v-html="formatName(i.name)"></span>
                      </div>
                      </div>
                   </div>
                   </div>
                   <div class="iconArrow">
                   <div class="iconArrow">
@@ -40,7 +41,14 @@
                   </div>
                   </div>
                </div>
                </div>
             </template>
             </template>
-            <courseCollapse v-else :isChild="true" :courseList="item.children || []" :activeCollapse="activeCollapse" @handleClick="handleClick" />
+            <courseCollapse
+               v-else
+               :isChild="true"
+               :search="searchStr"
+               :courseList="item.children || []"
+               :activeCollapse="activeCollapse"
+               @handleClick="handleClick"
+            />
          </div>
          </div>
       </el-collapse-item>
       </el-collapse-item>
    </el-collapse>
    </el-collapse>
@@ -67,8 +75,10 @@ type courseListType = {
 const props = defineProps<{
 const props = defineProps<{
    activeCollapse: undefined | Record<string, any>
    activeCollapse: undefined | Record<string, any>
    courseList: courseListType
    courseList: courseListType
+   search?: string
    isChild?: boolean
    isChild?: boolean
 }>()
 }>()
+const searchStr = ref(props.search)
 
 
 const emits = defineEmits<{
 const emits = defineEmits<{
    (e: "handleClick", value: any): void
    (e: "handleClick", value: any): void
@@ -79,6 +89,13 @@ watch(
       activeCollapseId.value = filterActiveId()
       activeCollapseId.value = filterActiveId()
    }
    }
 )
 )
+watch(
+   () => props.search,
+   () => {
+      searchStr.value = props.search
+   }
+)
+
 const activeCollapseId = ref(filterActiveId())
 const activeCollapseId = ref(filterActiveId())
 
 
 function filterActiveId() {
 function filterActiveId() {
@@ -90,6 +107,12 @@ function filterActiveId() {
 function handleClick(value: any) {
 function handleClick(value: any) {
    emits("handleClick", value)
    emits("handleClick", value)
 }
 }
+
+function formatName(name: string) {
+   if (!name || !searchStr.value) return name
+   const search: any = searchStr.value
+   return name.replace(search, `<span style="color: #F67146;">${search}</span>`)
+}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>

+ 158 - 24
src/views/coursewarePlay/coursewarePlay.vue

@@ -28,12 +28,13 @@
          </div>
          </div>
       </div>
       </div>
       <div class="leftTools posTools">
       <div class="leftTools posTools">
-         <div class="posBtn" @click="handleToolClick('menu')">
+         <div class="posBtn" @click="handleToolClick('menu')" v-if="searchObj.source !== 'search'">
             <img src="@/img/coursewarePlay/menu.png" />
             <img src="@/img/coursewarePlay/menu.png" />
             <!-- <div>课程类型</div> -->
             <!-- <div>课程类型</div> -->
          </div>
          </div>
          <div class="posBtn" @click="handleToolClick('point')">
          <div class="posBtn" @click="handleToolClick('point')">
-            <img src="@/img/coursewarePlay/zhishidian.png" />
+            <img src="@/img/coursewarePlay/zhishidian.png" v-if="searchObj.source !== 'search'" />
+            <img src="@/img/coursewarePlay/sousuo.png" v-else />
             <!-- <div>知识点</div> -->
             <!-- <div>知识点</div> -->
          </div>
          </div>
          <div
          <div
@@ -101,14 +102,32 @@
             </div>
             </div>
          </div>
          </div>
       </div>
       </div>
-      <el-drawer class="elDrawer" direction="ltr" v-model="drawerShow" :show-close="false">
+      <el-drawer class="elDrawer" :class="{ elDrawerHeader: searchObj.source === 'search' }" direction="ltr" v-model="drawerShow" :show-close="false">
          <template #header="{ close }">
          <template #header="{ close }">
-            <img class="directory" src="@/img/coursewarePlay/kcml.png" />
-            <div class="tit">知识点目录</div>
+            <template v-if="searchObj.source === 'search'">
+               <myInput
+                  class="queryIpt"
+                  v-model="searchObj.queryStr"
+                  :height="36"
+                  placeholder="请输入素材关键字"
+                  clearable
+                  @handleQuery="handleQuery"
+                  @keyup.enter="handleQuery"
+               />
+            </template>
+            <template v-else>
+               <img class="directory" src="@/img/coursewarePlay/kcml.png" />
+               <div class="tit">知识点目录</div>
+            </template>
             <img class="close" @click="close" src="@/img/coursewarePlay/close.png" />
             <img class="close" @click="close" src="@/img/coursewarePlay/close.png" />
          </template>
          </template>
-         <ElScrollbar class="elScrollbar">
-            <courseCollapse :activeCollapse="activeCourseware" :courseList="coursewareList" @handleClick="handleCourseClick" />
+         <ElScrollbar class="elScrollbar" v-loading="searchObj.loading">
+            <courseCollapse
+               :activeCollapse="activeCourseware"
+               :search="searchObj.search"
+               :courseList="tempCoursewareList"
+               @handleClick="handleCourseClick"
+            />
          </ElScrollbar>
          </ElScrollbar>
       </el-drawer>
       </el-drawer>
       <el-drawer class="elDrawer elCourseMenu" direction="ltr" v-model="drawerMenuShow" :show-close="false">
       <el-drawer class="elDrawer elCourseMenu" direction="ltr" v-model="drawerMenuShow" :show-close="false">
@@ -127,12 +146,18 @@
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import videoPlay from "./videoPlay"
 import videoPlay from "./videoPlay"
-import { getLessonCourseDetail_gym, getLessonCoursewareDetail_gyt, getLessonCourseDetail_klx } from "@/api/cloudTextbooks.api"
+import {
+   getLessonCourseDetail_gym,
+   getLessonCoursewareDetail_gyt,
+   getLessonCourseDetail_klx,
+   getLessonCoursewareCourseList_gym
+} from "@/api/cloudTextbooks.api"
+import myInput from "@/components/myInput"
 import { checkWebCourse_gyt, refLevel_gym, refLevel_gyt, refLevel_klx } from "@/api/coursewarePlay.api"
 import { checkWebCourse_gyt, refLevel_gym, refLevel_gyt, refLevel_klx } from "@/api/coursewarePlay.api"
-import { httpAjaxErrMsg, httpAjaxLoadingErrMsg } from "@/plugin/httpAjax"
+import { httpAjax, httpAjaxErrMsg, httpAjaxLoadingErrMsg } from "@/plugin/httpAjax"
 import userStore from "@/store/modules/user"
 import userStore from "@/store/modules/user"
 import { useRoute, useRouter } from "vue-router"
 import { useRoute, useRouter } from "vue-router"
-import { shallowRef, ref, computed, onUnmounted, onMounted, watch, nextTick } from "vue"
+import { shallowRef, ref, computed, onUnmounted, onMounted, watch, nextTick, reactive } from "vue"
 import { ElMessageBox } from "element-plus"
 import { ElMessageBox } from "element-plus"
 import courseCollapse from "./components/courseCollapse"
 import courseCollapse from "./components/courseCollapse"
 import courseMenuCollapse from "./components/courseMenuCollapse"
 import courseMenuCollapse from "./components/courseMenuCollapse"
@@ -147,6 +172,7 @@ import useDialogConfirm from "@/hooks/useDialogConfirm"
 import LoadingBar from "@/plugin/loadingBar"
 import LoadingBar from "@/plugin/loadingBar"
 import { isPlay, penShow, toolOpen, whitePenShow } from "@/businessComponents/globalTools/globalTools"
 import { isPlay, penShow, toolOpen, whitePenShow } from "@/businessComponents/globalTools/globalTools"
 import { closeAllModalFrame } from "@/plugin/modalFrame"
 import { closeAllModalFrame } from "@/plugin/modalFrame"
+import { deepCopy } from "@/libs/tools"
 
 
 const route = useRoute()
 const route = useRoute()
 const router = useRouter()
 const router = useRouter()
@@ -157,10 +183,21 @@ const songPlayDom = ref<any>() // 曲谱对象
 const lessonTargetDetail = ref<string>("") // 阶段目标
 const lessonTargetDetail = ref<string>("") // 阶段目标
 const coursewareMenuList = shallowRef<any[]>([]) // 课程类型
 const coursewareMenuList = shallowRef<any[]>([]) // 课程类型
 const coursewareList = shallowRef<any[]>([]) // 知识点
 const coursewareList = shallowRef<any[]>([]) // 知识点
+const tempCoursewareList = shallowRef<any[]>([]) // 临时知识点
 const flattenCoursewareList = ref<any[]>([]) // 扁平化coursewareList
 const flattenCoursewareList = ref<any[]>([]) // 扁平化coursewareList
+const tempFlattenCoursewareList = ref<any[]>([]) // 临时 扁平化coursewareList
 const isCurrentCoursewareMenu = shallowRef(true) // 是否为当前选的课程类型
 const isCurrentCoursewareMenu = shallowRef(true) // 是否为当前选的课程类型
 const currentId = ref<any>(route.params.id)
 const currentId = ref<any>(route.params.id)
 const isTempAutoPlay = ref(false)
 const isTempAutoPlay = ref(false)
+let coursewareDetailController: AbortController
+const searchObj = reactive({
+   loading: false,
+   isSearch: false, // 是否搜索 标识
+   queryStr: route.query.search as any,
+   source: route.query.source as any, // 从哪里来的
+   search: route.query.search as any // 默认的搜索条件 -
+})
+
 // 选中的知识点
 // 选中的知识点
 const activeCourseware = computed<undefined | Record<string, any>>(() => {
 const activeCourseware = computed<undefined | Record<string, any>>(() => {
    return flattenCoursewareList.value[activeCoursewareIndex.value]
    return flattenCoursewareList.value[activeCoursewareIndex.value]
@@ -208,7 +245,6 @@ watch(activeCourseware, () => {
       })
       })
    showController()
    showController()
 })
 })
-getCoursewareList()
 async function getCoursewareList(id?: string) {
 async function getCoursewareList(id?: string) {
    //  GYM,GYT,KLX 区分   查询接口
    //  GYM,GYT,KLX 区分   查询接口
    const LessonCoursewareDetailApi = {
    const LessonCoursewareDetailApi = {
@@ -251,7 +287,60 @@ async function getCoursewareList(id?: string) {
       }
       }
    })
    })
 }
 }
-getCoursewareMenuList()
+
+async function getLessCoursewareList(id?: string) {
+   //  GYM,GYT,KLX 区分   查询接口
+   const LessonCoursewareDetailApi = {
+      // GYT: getLessonCoursewareCourseList_gym
+      GYM: getLessonCoursewareCourseList_gym
+      // KLX: getLessonCourseDetail_klx
+   }
+   if (coursewareDetailController) {
+      coursewareDetailController.abort()
+   }
+   coursewareDetailController = new AbortController()
+   searchObj.loading = true
+   await httpAjax(LessonCoursewareDetailApi["GYM"], {
+      id: id || (route.params.id as string),
+      search: searchObj.queryStr,
+      abortController: coursewareDetailController
+   }).then(res => {
+      searchObj.loading = false
+      if (res.code === 200) {
+         const result = res.data || []
+         for (let i = 0; i < result.length; i++) {
+            const itemResult = result[i]
+            itemResult.name = itemResult.coursewareDetailName
+            itemResult.id = itemResult.coursewareDetailId
+            itemResult.lessonTargetDesc = itemResult.lessonTargetDesc ? itemResult.lessonTargetDesc.replace(/\n/g, "<br />") : ""
+            itemResult.children = itemResult.knowledgePointList || []
+            itemResult.knowledgePointList = []
+         }
+         handlePointList(result, searchObj.isSearch)
+
+         // 初始化检查事项
+         const item = flattenCoursewareList.value[activeCoursewareIndex.value]
+         const parentId = item ? item.parentData.ids[0] : ""
+         if (parentId) {
+            const parentItem = coursewareList.value.find((item: any) => item.id === parentId)
+            if (parentItem) {
+               lessonTargetDetail.value = parentItem.lessonTargetDesc
+            }
+         }
+      }
+
+      // 标识
+      searchObj.isSearch = false
+   })
+}
+
+// 处理是从搜索过来的,还是
+if (route.query.source === "search") {
+   getLessCoursewareList()
+} else {
+   getCoursewareList()
+   getCoursewareMenuList()
+}
 function getCoursewareMenuList(id?: string) {
 function getCoursewareMenuList(id?: string) {
    //  GYM,GYT,KLX 区分   查询接口
    //  GYM,GYT,KLX 区分   查询接口
    const LessonCoursewareMenuDetailApi = {
    const LessonCoursewareMenuDetailApi = {
@@ -270,18 +359,23 @@ function getCoursewareMenuList(id?: string) {
    })
    })
 }
 }
 let flattenCoursewareListData: any = [] // 临时扁平化数据
 let flattenCoursewareListData: any = [] // 临时扁平化数据
-function handlePointList(pointList: any[]) {
-   // 重置数据
-   coursewareTotalTime.value = 0
-   coursewareList.value = filterPointList(pointList)
-   // 如果url里面有materialId 代表指定资料播放
-   if (route.query.materialId) {
-      const index = flattenCoursewareListData.findIndex((item: any) => {
-         return route.query.materialId === item.id + "" && route.query.knowledgePointId === item.knowledgePointId + ""
-      })
-      index > -1 && (activeCoursewareIndex.value = index)
+function handlePointList(pointList: any[], isSearch?: boolean) {
+   const list = filterPointList(pointList)
+   if (!isSearch) {
+      // 重置数据
+      coursewareTotalTime.value = 0
+      coursewareList.value = list
+      // 如果url里面有materialId 代表指定资料播放
+      if (route.query.materialId) {
+         const index = flattenCoursewareListData.findIndex((item: any) => {
+            return route.query.materialId === item.id + "" && route.query.knowledgePointId === item.knowledgePointId + ""
+         })
+         index > -1 && (activeCoursewareIndex.value = index)
+      }
+      flattenCoursewareList.value = flattenCoursewareListData
    }
    }
-   flattenCoursewareList.value = flattenCoursewareListData
+   tempCoursewareList.value = list
+   tempFlattenCoursewareList.value = flattenCoursewareListData
 }
 }
 function filterPointList(pointList: any[], parentData?: { ids: string[]; name: string }): any[] {
 function filterPointList(pointList: any[], parentData?: { ids: string[]; name: string }): any[] {
    // 设置父级及以上id数组和父级name
    // 设置父级及以上id数组和父级name
@@ -291,7 +385,7 @@ function filterPointList(pointList: any[], parentData?: { ids: string[]; name: s
             children: filterPointList(point.children, { ids: [...(parentData?.ids || []), point.id], name: point.name })
             children: filterPointList(point.children, { ids: [...(parentData?.ids || []), point.id], name: point.name })
          })
          })
       } else {
       } else {
-         coursewareTotalTime.value += point.totalMaterialTimeSecond
+         // coursewareTotalTime.value += point.totalMaterialTimeSecond
          return Object.assign(point, {
          return Object.assign(point, {
             materialList: point.materialList.map((item: any) => {
             materialList: point.materialList.map((item: any) => {
                item.parentData = {
                item.parentData = {
@@ -312,9 +406,24 @@ function handleChangeCourseware(index: -1 | 1) {
    if (newIndex < 0 || newIndex > flattenCoursewareList.value.length - 1) {
    if (newIndex < 0 || newIndex > flattenCoursewareList.value.length - 1) {
       return
       return
    }
    }
+
+   const item = flattenCoursewareList.value[activeCoursewareIndex.value]
+   const newItem = flattenCoursewareList.value[newIndex]
+   const parentId = item ? item.parentData.ids[0] : ""
+   const newParentId = newItem ? newItem.parentData.ids[0] : ""
+   if (parentId !== newParentId) {
+      const parentItem = coursewareList.value.find((item: any) => item.id === parentId)
+      if (parentItem) {
+         lessonTargetDetail.value = parentItem.lessonTargetDesc
+      }
+   }
+
    activeCoursewareIndex.value = newIndex
    activeCoursewareIndex.value = newIndex
 }
 }
 function handleCourseClick(value: any) {
 function handleCourseClick(value: any) {
+   // 选择之后初始化数据
+   coursewareList.value = deepCopy(tempCoursewareList.value)
+   flattenCoursewareList.value = deepCopy(tempFlattenCoursewareList.value)
    activeCoursewareIndex.value = flattenCoursewareList.value.findIndex((item: any) => {
    activeCoursewareIndex.value = flattenCoursewareList.value.findIndex((item: any) => {
       return value.id === item.id && value.knowledgePointId === item.knowledgePointId
       return value.id === item.id && value.knowledgePointId === item.knowledgePointId
    })
    })
@@ -423,6 +532,7 @@ function handleToolClick(type: string) {
    if (type === "menu") {
    if (type === "menu") {
       drawerMenuShow.value = true
       drawerMenuShow.value = true
    } else if (type === "point") {
    } else if (type === "point") {
+      tempCoursewareList.value = deepCopy(coursewareList.value)
       drawerShow.value = true
       drawerShow.value = true
    }
    }
 }
 }
@@ -548,6 +658,13 @@ function onTitleTip(type: "phaseGoals" | "checkItem", text: string) {
    })
    })
    handleVideoPause()
    handleVideoPause()
 }
 }
+
+function handleQuery() {
+   //
+   // handleGetDetailList(props.modalData.id, true, queryStr.value)
+   searchObj.isSearch = true
+   getLessCoursewareList()
+}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -761,6 +878,18 @@ function onTitleTip(type: "phaseGoals" | "checkItem", text: string) {
                opacity: $opacity-hover;
                opacity: $opacity-hover;
             }
             }
          }
          }
+
+         .el-input {
+            margin-right: 10px;
+            font-size: 14px;
+         }
+
+         .btnSelect {
+            width: 56px;
+            height: 26px;
+            line-height: 26px;
+            font-size: 14px;
+         }
       }
       }
       .el-drawer__body {
       .el-drawer__body {
          padding: 0;
          padding: 0;
@@ -776,6 +905,11 @@ function onTitleTip(type: "phaseGoals" | "checkItem", text: string) {
          }
          }
       }
       }
    }
    }
+   &:deep(.elDrawerHeader.el-drawer) {
+      .el-drawer__header {
+         padding: 0 10px;
+      }
+   }
 
 
    &:deep(.elCourseMenu.el-drawer) {
    &:deep(.elCourseMenu.el-drawer) {
       width: 363px !important;
       width: 363px !important;