lex-xin 8 månader sedan
förälder
incheckning
6e6a299dec
3 ändrade filer med 545 tillägg och 32 borttagningar
  1. 183 18
      src/tenant/music/train-tool/index.module.less
  2. 361 13
      src/tenant/music/train-tool/index.tsx
  3. 1 1
      vite.config.ts

+ 183 - 18
src/tenant/music/train-tool/index.module.less

@@ -9,14 +9,13 @@
   }
 
   :global {
-
     // 选择框
     // 上拉选择 - ✅
     // 选择器 - ✅
     .van-picker {
       --van-picker-toolbar-height: 44px !important;
 
-      --k-font-primary: #FE2451;
+      --k-font-primary: #fe2451;
       --k-bg-4: #f2f2f2;
       --k-gray-1: #333333;
       --k-gray-2: #666666;
@@ -82,7 +81,7 @@
 }
 
 .musicContent {
-  position: absolute;
+  position: fixed;
   top: 0;
   height: 365px;
   width: 100%;
@@ -108,7 +107,7 @@
   .subjectName {
     font-weight: 400;
     font-size: 14px;
-    color: #FE2451;
+    color: #fe2451;
     line-height: 20px;
     padding-right: 4px;
     max-width: 60px;
@@ -120,11 +119,11 @@
   img {
     width: 9px;
     height: 5px;
-    transition: .2s ease;
+    transition: 0.2s ease;
 
     &.active {
       transform: rotate(180deg);
-      transition: .2s ease;
+      transition: 0.2s ease;
     }
   }
 }
@@ -262,9 +261,11 @@
           top: 0;
           bottom: 0;
           width: 9px;
-          background: linear-gradient(270deg,
-              rgba(0, 0, 0, 0.18) 0%,
-              rgba(255, 255, 255, 0) 100%);
+          background: linear-gradient(
+            270deg,
+            rgba(0, 0, 0, 0.18) 0%,
+            rgba(255, 255, 255, 0) 100%
+          );
         }
       }
     }
@@ -387,13 +388,85 @@
     }
   }
 }
+:global {
+  .van-dropdown-menu__bar {
+    background-color: transparent;
+    box-shadow: none;
+    padding-right: 15px;
+    padding-bottom: 10px;
+    height: 36px;
+  }
+
+  .van-dropdown-menu__title {
+    padding-left: 0;
+    color: #131415;
+
+    &:after {
+      border-color: transparent transparent rgba(0, 0, 0, 0.4)
+        rgba(0, 0, 0, 0.4);
+    }
+
+    &.van-dropdown-menu__title--active {
+      color: #fe2451;
+
+      &:after {
+        border-color: transparent transparent #fe2451 #fe2451;
+      }
+    }
+  }
+
+  .van-dropdown-item__content {
+    border-radius: 0px 0px 16px 16px;
+  }
+
+  .van-dropdown-item__option {
+    padding: 0 13px;
+    // line-height: 50px;
+    line-height: auto;
+    .van-cell__title {
+      text-align: center; 
+      padding: 0;
+      color: #333333;
+      font-size: 16px;
+      line-height: 50px;
+      // height: 50px;
+      // display: flex;
+      // align-items: center;
+      // justify-content: center;
+    }
+    .van-cell__value {
+      display: none;
+    }
+    &::after {
+      display: none;
+    }
+
+    &:last-child {
+      padding-bottom: 12px;
+    }
+    &:first-child {
+      padding-top: 12px;
+    }
+  }
+  .van-dropdown-item__option--active {
+    .van-cell__title {
+      font-weight: 600;
+      color: #FE2451;
+      text-align: center;
+      background: rgba(254, 36, 81, 0.08);
+      border-radius: 10px;
+    }
+  }
+}
 
 .musicList {
   position: relative;
   z-index: 12;
   background-color: #fff;
   border-radius: 16px;
-  min-height: 50vh; //calc(100vh - 210px - var(--header-height));
+  min-height: calc(
+    100vh - var(--header-height) - var(--bottom-train-tool-height, 0px) + 1px
+  );
 
   --van-cell-background-color: transparent;
   --van-cell-font-size: 16px;
@@ -426,9 +499,11 @@
     .van-tabs__line {
       width: 24px;
       height: 4px;
-      background: linear-gradient(90deg,
-          #ff3c5e 0%,
-          rgba(255, 118, 155, 0.5) 100%) !important;
+      background: linear-gradient(
+        90deg,
+        #ff3c5e 0%,
+        rgba(255, 118, 155, 0.5) 100%
+      ) !important;
       border-radius: 36px 36px 0px 0px;
     }
 
@@ -439,10 +514,24 @@
     .van-button--plain.van-button--primary {
       background-color: transparent;
     }
-
+    .van-sticky--fixed {
+      border-radius: 16px 16px 0 0;
+    }
     .van-sticky--fixed .van-tabs {
       background-color: #fff;
     }
+
+    .van-search {
+      padding-top: 0;
+      padding-bottom: 10px;
+      background-color: #fff !important;
+      align-items: flex-start;
+    }
+    .van-search__content {
+      background-color: #f6f6f6 !important;
+    }
+
+    
   }
 
   .alumnList {
@@ -478,6 +567,80 @@
   }
 }
 
+
+.searchResult {
+  padding: 16px 13px 12px;
+  overflow: hidden;
+  margin-bottom: 20px;
+  border-bottom: 1px solid #f2f2f2;
+
+  .searchTitle {
+    font-size: 15px;
+    color: #333333;
+    font-weight: 600;
+    line-height: 22px;
+  }
+}
+
+.radio-group {
+  display: flex;
+  margin-top: 12px;
+  margin-bottom: 7px;
+  flex-wrap: wrap;
+
+  .radio:first-child {
+    :global {
+      .van-radio__label {
+        margin-left: 0;
+      }
+    }
+  }
+}
+
+.radio {
+  :global {
+    .van-radio__icon {
+      display: none;
+    }
+
+    .van-tag--large {
+      width: 100px;
+      height: 34px;
+      font-size: 13px;
+      text-align: center;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .van-tag {
+      box-sizing: border-box;
+      width: 31% !important;
+    }
+
+    .van-tag--default {
+      color: #333333;
+      background: #f6f6f6;
+    }
+
+    .van-tag--primary {
+      background: #fff3f5;
+    }
+  }
+}
+
+.organ-radio {
+  :global {
+    .van-tag--large {
+      width: auto;
+      padding: 0 2px;
+      margin-bottom: 8px;
+      margin-right: 8px;
+      font-size: 14px;
+    }
+  }
+}
+
 .btnGroup {
   border-top: 1px solid #f2f2f2;
   background-color: #fff;
@@ -494,9 +657,11 @@
 
       &.van-button--disabled {
         opacity: initial;
-        background: linear-gradient(270deg,
-            #ff7a93 0%,
-            #ff9daa 100%) !important;
+        background: linear-gradient(
+          270deg,
+          #ff7a93 0%,
+          #ff9daa 100%
+        ) !important;
       }
     }
   }
@@ -508,4 +673,4 @@
       padding-top: 0;
     }
   }
-}
+}

+ 361 - 13
src/tenant/music/train-tool/index.tsx

@@ -16,17 +16,19 @@ import {
   Popup,
   Dialog,
   Sticky,
-  Swipe,
-  SwipeItem,
-  Picker
+  Picker,
+  DropdownMenu,
+  DropdownItem,
+  Tag
 } from 'vant'
 import styles from './index.module.less'
 import TheSticky from '@/components/the-sticky'
 import ColHeader from '@/components/col-header'
-import { useWindowScroll, useEventListener } from '@vueuse/core'
+// import { useWindowScroll, useEventListener } from '@vueuse/core'
 import request from '@/helpers/request'
-import iconMenu from './images/icon-menu.png'
-import iconRightTop from './images/icon-right-top.png'
+// import iconMenu from './images/icon-menu.png'
+// import iconRightTop from './images/icon-right-top.png'
+import Search from '@/components/col-search'
 import iconAlbumCover from '../../images/icon-album-cover.png'
 import iconTimer from './images/icon-timer.png'
 import iconArrow from './images/icon-arrow.png'
@@ -47,6 +49,21 @@ import 'swiper/css'
 import 'swiper/css/pagination'
 import CourseItem from '../lessonCourseware/component/CourseItem'
 
+enum courseEmnu {
+  PERCUSSION_SINGLE = '打击乐',
+  FLUTE_SINGLE = '长笛',
+  SAX_SINGLE = '萨克斯',
+  CLARINET_SINGLE = '单簧管',
+  TRUMPET_SINGLE = '小号',
+  TROMBONE_SINGLE = '长号',
+  HORN_SINGLE = '圆号',
+  BARITONE_TUBA_SINGLE = '上低音号-大号',
+  MUSIC_THEORY = '乐理',
+  INSTRUMENTAL_ENSEMBLE = '合奏',
+  EUPHONIUM_SINGLE = '上低音号',
+  TUBA_SINGLE = '大号'
+}
+
 export default defineComponent({
   name: 'train-tool',
   setup() {
@@ -59,7 +76,7 @@ export default defineComponent({
     const route = useRoute()
     const router = useRouter()
     const background = ref<string>('rgba(55, 205, 177, 0)')
-    const color = ref<string>('#fff')
+    // const color = ref<string>('#fff')
     const state = reactive({
       userId: '',
       details: {} as any,
@@ -73,7 +90,7 @@ export default defineComponent({
       isError: false,
       list: [] as any,
       popupStatus: false,
-      selectMember: {} as any, // 购买的月份
+      // selectMember: {} as any, // 购买的月份
       ensembleCounts: false,
       musicCounts: false,
       subjectCounts: false,
@@ -91,7 +108,34 @@ export default defineComponent({
       teacherSubjectIndex: 0,
       subjectList: [] // 声部列表
     })
+    // const params = reactive({
+    //   keyword: (route.query.search as string) || '',
+    //   subjectType: subjectType,
+    //   page: 1,
+    //   subjectId: null,
+    //   albumId: route.query.albumId,
+    //   albumName: '',
+    //   level: '',
+    //   type: '',
+    //   title: title
+    // })
+    const searchObj = ref<any>({
+      COURSEWARE: {},
+      SUBJECT: {},
+      MUSIC: {},
+      ENSEMBLE: {}
+    })
+    const searchRef = ref()
     const params = reactive({
+      keyword: null as any,
+      // subjectType: '',
+      subjectId: null,
+      // albumId: route.query.albumId,
+      // albumName: '',
+      level: '',
+      type: '',
+      courseTypeCode: '',
+      // title: '',
       page: 1,
       rows: 20
     })
@@ -108,6 +152,44 @@ export default defineComponent({
       }
     })
 
+    const isSearchStatus = computed(() => {
+      const obj = searchObj.value[state.activeTab]
+      let status = false
+      if (state.activeTab === 'COURSEWARE') {
+        if (obj.courseTypeList && obj.courseTypeList.length > 0) {
+          status = true
+        }
+      } else {
+        if (obj.subjects && obj.subjects.length > 0) {
+          status = true
+        }
+        if (obj.levelList && obj.levelList.length > 0) {
+          status = true
+        }
+        if (obj.typeList && obj.typeList.length > 0) {
+          status = true
+        }
+      }
+      return status
+    })
+
+    const courseTypeCodeList = computed(() => {
+      const obj =
+        state.activeTab === 'COURSEWARE' ? searchObj.value[state.activeTab] : []
+      const list = obj.courseTypeList || []
+      const temp = list?.map((item: any) => {
+        return {
+          text: courseEmnu[item],
+          value: item
+        }
+      })
+      temp.unshift({
+        text: '全部教材',
+        value: ''
+      })
+      return temp
+    })
+
     const getDetails = async () => {
       state.loadingAlbum = true
       try {
@@ -179,6 +261,23 @@ export default defineComponent({
       state.loadingAlbum = false
     }
 
+    const getSelectCondition = async (type: string) => {
+      // 判断是否已经查询过数据
+      if(Object.keys(searchObj.value[type]).length > 0) {
+        return
+      }
+      const { data } = await request.post(
+        `${apiSuffix.value}/tenantAlbumMusic/selectCondition`,
+        {
+          data: {
+            subjectType: type,
+            tenantAlbumId: state.details.id
+          }
+        }
+      )
+      searchObj.value[type] = data || {}
+    }
+
     watch(
       () => state.details,
       () => {
@@ -208,9 +307,12 @@ export default defineComponent({
       state.loading = true
       state.isError = false
       const tempParams = {
+        ...params,
+      //   level: '',
+      // type: '',
+      // subjectId: params.subjectId
         albumId: state.details.id || null,
-        subjectType: state.activeTab,
-        ...params
+        subjectType: state.activeTab
       } as any
 
       // 老师端默认查询声部
@@ -284,6 +386,23 @@ export default defineComponent({
       }
     }
 
+    // 获取信息
+    const activeTypeParams = async () => {
+      if (state.activeTab === 'COURSEWARE') {
+        await getSelectCondition(state.activeTab)
+      } else if (['SUBJECT', 'MUSIC', 'ENSEMBLE'].includes(state.activeTab)) {
+        await getSelectCondition(state.activeTab)
+      }
+    }
+
+    const onSearch = (value?: string) => {
+      params.page = 1
+      // state.finished = false
+      params.keyword = value
+      state.list = []
+      FetchList()
+    }
+
     onMounted(async () => {
       // useEventListener(document, 'scroll', evt => {
       //   const { y } = useWindowScroll()
@@ -318,10 +437,16 @@ export default defineComponent({
       state.loadingAlbum = true
       await getDetails()
       await FetchList()
-      getSubjectList()
+      //
+      if (baseState.platformType === 'TEACHER') {
+        getSubjectList()
+      }
+
+      activeTypeParams()
       state.loadingAlbum = false
       state.loading = false
 
+      console.log(state.activeTab, 'activeTab')
       // 为了处理 swiper 会不显示的问题
       document.body.scrollIntoView()
       window.scrollTo(1, 0)
@@ -533,6 +658,11 @@ export default defineComponent({
                         pagination={{ clickable: true }}
                         // onTransitionEnd={(swiper: any) => {}} onSlideChange
                         onSlideChange={(swiper: any) => {
+                          params.subjectId = null;
+                          params.keyword = null;
+                          params.level = ''
+                          params.type = ''
+                          params.courseTypeCode = ''
                           state.details = state.albumList[swiper.activeIndex]
                           // 等tab渲染完了之后再切换 不然tab会自动重新赋值
                           nextTick(() => {
@@ -613,7 +743,7 @@ export default defineComponent({
                 </div>
               </div>
 
-              <div class={styles.musicList}>
+              <div class={[styles.musicList, 'musicList']}>
                 <Sticky position="top" offsetTop={state.heightV}>
                   <Tabs
                     color="var(--van-primary)"
@@ -622,7 +752,14 @@ export default defineComponent({
                     shrink
                     v-model:active={state.activeTab}
                     onClick-tab={val => {
+                      params.subjectId = null;
+                      params.keyword = null;
+                      params.level = ''
+                      params.type = ''
+                      params.courseTypeCode = ''
+
                       state.activeTab = val.name
+                      activeTypeParams()
                       params.page = 1
                       state.list = []
                       FetchList()
@@ -641,6 +778,214 @@ export default defineComponent({
                       <Tab title="合奏云练" name="ENSEMBLE"></Tab>
                     )}
                   </Tabs>
+                  <Search
+                    placeholder={state.activeTab === 'COURSEWARE' ? '请输入教材关键词' : '请输入曲谱关键词'}
+                    class={styles.search}
+                    onSearch={onSearch}
+                    type="tenant"
+                    v-slots={{
+                      left: () =>
+                        isSearchStatus.value && (
+                          <DropdownMenu zIndex={2999}>
+                            {state.activeTab === 'COURSEWARE' ? (
+                              <DropdownItem
+                                teleport="body"
+                                onOpen={() => {
+                                  const targetElement: any = document.querySelector('.musicList')
+                                  const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY;
+                                  // 设置距离顶部的像素值
+                                  window.scrollTo({
+                                    top: targetPosition - state.heightV + 1,
+                                    behavior: 'smooth'
+                                  })
+                                }}
+                                v-model={params.courseTypeCode}
+                                options={courseTypeCodeList.value}
+                                onUpdate:modelValue={() => {
+                                  onSearch()
+                                }}
+                              />
+                            ) : (
+                              <DropdownItem
+                                onOpen={() => {
+                                  const targetElement: any = document.querySelector('.musicList')
+                                  const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY;
+  
+                                  window.scrollTo({
+                                    top: targetPosition - state.heightV + 1,
+                                    behavior: 'smooth'
+                                  })
+                                }}
+                                teleport="body"
+                                titleClass={
+                                  params.subjectId ||
+                                  params.type ||
+                                  params.level
+                                    ? styles.titleActive
+                                    : ''
+                                }
+                                title="筛选"
+                                ref={searchRef}
+                              >
+                                <div
+                                  class={styles.searchResult}
+                                  style={{
+                                    maxHeight: '45vh',
+                                    overflowY: 'auto'
+                                  }}
+                                >
+                                  {searchObj.value[state.activeTab].subjects &&
+                                    searchObj.value[state.activeTab].subjects.length > 0 && (
+                                      <>
+                                        <div class={styles.searchTitle}>
+                                          声部
+                                        </div>
+                                        <div
+                                          class={[
+                                            styles['radio-group'],
+                                            styles.radio,
+                                            styles['organ-radio']
+                                          ]}
+                                        >
+                                          {searchObj.value[state.activeTab].subjects.map(
+                                            (subject: any) => {
+                                              const isActive =
+                                                subject.id === params.subjectId
+                                              const type = isActive
+                                                ? 'primary'
+                                                : 'default'
+                                              return (
+                                                <Tag
+                                                  size="large"
+                                                  plain={isActive}
+                                                  type={type}
+                                                  round
+                                                  onClick={() => {
+                                                    params.subjectId =
+                                                      subject.id
+                                                  }}
+                                                >
+                                                  {subject.name}
+                                                </Tag>
+                                              )
+                                            }
+                                          )}
+                                        </div>
+                                      </>
+                                    )}
+                                  {searchObj.value[state.activeTab].levelList &&
+                                    searchObj.value[state.activeTab].levelList.length > 0 && (
+                                      <>
+                                        <div class={styles.searchTitle}>
+                                          级别
+                                        </div>
+                                        <div
+                                          class={[
+                                            styles['radio-group'],
+                                            styles.radio,
+                                            styles['organ-radio']
+                                          ]}
+                                        >
+                                          {searchObj.value[state.activeTab].levelList.map(
+                                            (subject: any) => {
+                                              const isActive =
+                                                subject.id === params.level
+                                              const type = isActive
+                                                ? 'primary'
+                                                : 'default'
+                                              return (
+                                                <Tag
+                                                  size="large"
+                                                  plain={isActive}
+                                                  type={type}
+                                                  round
+                                                  onClick={() => {
+                                                    params.level = subject.id
+                                                  }}
+                                                >
+                                                  {subject.value}
+                                                </Tag>
+                                              )
+                                            }
+                                          )}
+                                        </div>
+                                      </>
+                                    )}
+                                  {searchObj.value[state.activeTab].typeList &&
+                                    searchObj.value[state.activeTab].typeList.length > 0 && (
+                                      <>
+                                        <div class={styles.searchTitle}>
+                                          类型
+                                        </div>
+                                        <div
+                                          class={[
+                                            styles['radio-group'],
+                                            styles.radio,
+                                            styles['organ-radio']
+                                          ]}
+                                        >
+                                          {searchObj.value[state.activeTab].typeList.map(
+                                            (subject: any) => {
+                                              const isActive =
+                                                subject.id === params.type
+                                              const type = isActive
+                                                ? 'primary'
+                                                : 'default'
+                                              return (
+                                                <Tag
+                                                  size="large"
+                                                  plain={isActive}
+                                                  type={type}
+                                                  round
+                                                  onClick={() => {
+                                                    params.type = subject.id
+                                                  }}
+                                                >
+                                                  {subject.value}
+                                                </Tag>
+                                              )
+                                            }
+                                          )}
+                                        </div>
+                                      </>
+                                    )}
+                                </div>
+
+                                <div class={['btnGroup', 'btnMore']}>
+                                  <Button
+                                    class={styles.resetting}
+                                    type="primary"
+                                    plain
+                                    round
+                                    onClick={() => {
+                                      params.subjectId = null
+                                      params.level = ''
+                                      params.type = ''
+                                    }}
+                                  >
+                                    重 置
+                                  </Button>
+
+                                  <Button
+                                    class={styles.confirm}
+                                    type="primary"
+                                    color="linear-gradient( 270deg, #FF204B 0%, #FE5B71 100%)"
+                                    round
+                                    block
+                                    onClick={() => {
+                                      onSearch()
+                                      searchRef.value?.toggle()
+                                    }}
+                                  >
+                                    确 认
+                                  </Button>
+                                </div>
+                              </DropdownItem>
+                            )}
+                          </DropdownMenu>
+                        )
+                    }}
+                  />
                 </Sticky>
 
                 <div
@@ -735,7 +1080,10 @@ export default defineComponent({
                 </div>
               </div>
               {baseState.platformType === 'STUDENT' && state.buy != '1' && (
-                <TheSticky position="bottom">
+                <TheSticky
+                  position="bottom"
+                  varName="--bottom-train-tool-height"
+                >
                   <div class={styles.btnGroup}>
                     <Button
                       round

+ 1 - 1
vite.config.ts

@@ -12,7 +12,7 @@ function resolve(dir: string) {
 // https://vitejs.dev/config/
 // https://github.com/vitejs/vite/issues/1930 .env
 // const proxyUrl = 'https://online.colexiu.com/'
-const proxyUrl = 'https://test.colexiu.com/'
+const proxyUrl = 'https://dev.colexiu.com/'
 // const proxyUrl = 'http://192.168.3.14:8000/'
 export default defineConfig({
   base: './',