Prechádzať zdrojové kódy

添加曲谱相关页面

wolyshaw 3 rokov pred
rodič
commit
b90b572f66

+ 33 - 0
package-lock.json

@@ -2835,6 +2835,29 @@
       "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.26.tgz",
       "integrity": "sha512-vPV6Cq+NIWbH5pZu+V+2QHE9y1qfuTq49uNWw4f7FDEeZaDU2H2cx5jcUZOAKW7qTrUS4k6qZPbMy1x4N96nbA=="
     },
+    "@vueuse/core": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.4.1.tgz",
+      "integrity": "sha512-YRM2+wZj/XWzch44sgFaRWtTGEZ8xgTsleaGy6cuULIU1q6o9Z/XHDHqofzNXrEqEhN6EtJqM4m8puURFO0nzg==",
+      "requires": {
+        "@vueuse/metadata": "8.4.1",
+        "@vueuse/shared": "8.4.1",
+        "vue-demi": "*"
+      }
+    },
+    "@vueuse/metadata": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.4.1.tgz",
+      "integrity": "sha512-OMwadiewrAHgJAgCh5zrbJMXySECB09cnEnIWRicvTWBiqBm14N1t464oeV1fhskRPEBsLSzxQmDoVBAqQh4rQ=="
+    },
+    "@vueuse/shared": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.4.1.tgz",
+      "integrity": "sha512-yzWNzvqaTGkc25fNsGkaF3pnMdXDax3EasYgPKlC4/eXSo0TqwG+xLz0Y8t6KN52x/kIHlpSwtry4LXfw7LSBA==",
+      "requires": {
+        "vue-demi": "*"
+      }
+    },
     "acorn": {
       "version": "8.7.0",
       "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.7.0.tgz",
@@ -3173,6 +3196,11 @@
       "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
       "dev": true
     },
+    "classnames": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+      "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+    },
     "clean-deep": {
       "version": "3.4.0",
       "resolved": "https://registry.npmmirror.com/clean-deep/-/clean-deep-3.4.0.tgz",
@@ -7013,6 +7041,11 @@
       "resolved": "https://registry.npmmirror.com/vue-cropper/-/vue-cropper-1.0.3.tgz",
       "integrity": "sha512-yDrZkE4H5vOiMA9WQHE+6rmXrZ1S9TMZasEPAZPKg/2I/nySHL4ECD1lNxt7+ofTPKT+9+2sQkCwagPqEqiqJg=="
     },
+    "vue-demi": {
+      "version": "0.12.5",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz",
+      "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q=="
+    },
     "vue-eslint-parser": {
       "version": "8.0.1",
       "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz",

+ 2 - 0
package.json

@@ -22,7 +22,9 @@
   },
   "dependencies": {
     "@vant/use": "^1.3.6",
+    "@vueuse/core": "^8.4.1",
     "browserslist": "^4.20.2",
+    "classnames": "^2.3.1",
     "clean-deep": "^3.4.0",
     "dayjs": "^1.10.7",
     "loaders.css": "^0.1.2",

+ 35 - 0
src/router/routes-student.ts

@@ -111,6 +111,41 @@ export default [
         meta: {
           title: '老师风采'
         }
+      },
+      {
+        path: '/music-songbook',
+        component: () => import('@/student/music/songbook'),
+        meta: {
+          title: '乐谱库'
+        }
+      },
+      {
+        path: '/music-album',
+        component: () => import('@/student/music/album'),
+        meta: {
+          title: '专辑'
+        }
+      },
+      {
+        path: '/music-album-detail/:id',
+        component: () => import('@/student/music/album-detail'),
+        meta: {
+          title: '专辑详情'
+        }
+      },
+      {
+        path: '/music-list',
+        component: () => import('@/student/music/list'),
+        meta: {
+          title: '曲谱列表'
+        }
+      },
+      {
+        path: '/music-search',
+        component: () => import('@/student/music/search'),
+        meta: {
+          title: '搜索结果'
+        }
       }
     ]
   },

BIN
src/student/music/album-detail/header-bg.png


+ 79 - 0
src/student/music/album-detail/index.module.less

@@ -0,0 +1,79 @@
+.detail {
+  background: url(./header-bg.png) no-repeat top center;
+  background-attachment: fixed;
+}
+.img {
+  width: 94px;
+  height: 94px;
+  margin-right: 18px;
+  position: relative;
+  > img,
+  > div {
+    position: absolute;
+    border-radius: 10px;
+    overflow: hidden;
+  }
+  &::before {
+    content: '';
+    width: 80px;
+    height: 80px;
+    border-radius: 9px;
+    background-color: var(--music-list-item-background-color);
+    box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.08);
+    position: absolute;
+    right: -6px;
+    top: 8px;
+  }
+}
+.detailContent {
+  background-color: white;
+  padding: 0 14px;
+  border-radius: 17px 17px 0px 0px;
+  .main {
+    padding-top: 24px;
+    padding-bottom: 28px;
+    display: flex;
+  }
+  .favoriteContaineer {
+    border: none;
+    color: var(--music-list-item-mate-color);
+    :global(.van-button__text) {
+      display: flex;
+      align-items: center;
+    }
+    > span {
+      display: inline-block;
+      line-height: 16px;
+      margin-top: 1px;
+    }
+  }
+  .favorite {
+    font-size: 16px;
+    margin-right: 5px;
+  }
+  .content {
+    flex: 1;
+    > h4 {
+      color: var(--music-list-item-title-color);
+      font-size: 14px;
+      height: 20px;
+      line-height: 20px;
+      margin-top: 7px;
+    }
+    > p {
+      margin-top: 6px;
+      font-size: 12px;
+      color: var(--music-list-item-desc-color);
+      line-height: 17px;
+      height: 51px;
+    }
+  }
+}
+.footerBar {
+  padding: 13px 0;
+  display: flex;
+  justify-content: space-between;
+  > footer {
+    margin-top: 0;
+  }
+}

+ 101 - 0
src/student/music/album-detail/index.tsx

@@ -0,0 +1,101 @@
+import { defineComponent, reactive, ref } from 'vue'
+import { useRoute } from 'vue-router'
+import request from '@/helpers/request'
+import ColHeader from '@/components/col-header'
+import { Button, Icon, Image, List, Sticky } from 'vant'
+import classNames from 'classnames'
+import Footer from '../album/footer'
+import FavoriteIcon from '../album/favorite.svg'
+import FavoritedIcon from '../album/favorited.svg'
+import styles from './index.module.less'
+import Item from '../list/item'
+
+export default defineComponent({
+  name: 'AlbumDetail',
+  setup() {
+    const params = reactive({
+      search: '',
+      page: 1
+    })
+    const albumDetail = ref<any>(null)
+    const data = ref<any>(null)
+    const loading = ref(false)
+    const finished = ref(false)
+    const isError = ref(false)
+    const route = useRoute()
+    const FetchList = async () => {
+      if (loading.value) {
+        return
+      }
+      loading.value = true
+      isError.value = false
+      try {
+        const res = await request.post('/api-student/music/album/detail', {
+          data: { id: route.params.id, ...params }
+        })
+        const { musicSheetList, ...rest } = res.data
+        console.log(rest)
+        albumDetail.value = rest
+        data.value = musicSheetList
+        params.page = musicSheetList.pageNo + 1
+        finished.value = musicSheetList.pageNo >= musicSheetList.totalPage
+      } catch (error) {
+        isError.value = true
+      }
+      loading.value = false
+    }
+    return () => {
+      const isFavorite = albumDetail.value?.favorite
+      return (
+        <div class={styles.detail}>
+          <Sticky class={styles.header}>
+            <ColHeader
+              class={styles.header}
+              background="transparent"
+              color="#fff"
+              title="专辑详情"
+              backIconColor="white"
+              border={false}
+              isFixed={false}
+            />
+            <div class={styles.detailContent}>
+              <div class={classNames(styles.main, 'van-hairline--bottom')}>
+                <Image
+                  class={styles.img}
+                  src={albumDetail.value?.albumCoverUrl}
+                />
+                <div class={styles.content}>
+                  <h4>{albumDetail.value?.albumName}</h4>
+                  <p>{albumDetail.value?.albumDesc}</p>
+                </div>
+              </div>
+              <div class={styles.footerBar}>
+                <Footer
+                  musicSheetCount={albumDetail.value?.musicSheetCount}
+                  albumFavoriteCount={albumDetail.value?.albumFavoriteCount}
+                />
+                <Button class={styles.favoriteContaineer}>
+                  <Icon
+                    class={styles.favorite}
+                    name={isFavorite ? FavoritedIcon : FavoriteIcon}
+                  />{' '}
+                  <span>{isFavorite ? '已' : ''}收藏</span>
+                </Button>
+              </div>
+            </div>
+          </Sticky>
+          <List
+            loading={loading.value}
+            finished={finished.value}
+            finished-text="没有更多了"
+            onLoad={FetchList}
+          >
+            {data.value && data.value.rows.length
+              ? data.value.rows.map(item => <Item data={item} />)
+              : null}
+          </List>
+        </div>
+      )
+    }
+  }
+})

+ 19 - 0
src/student/music/album/count.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>曲目数量</title>
+    <g id="页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="专辑" transform="translate(-136.000000, -236.000000)" fill="#DADADA" fill-rule="nonzero">
+            <g id="编组-18" transform="translate(14.000000, 151.000000)">
+                <g id="编组-9" transform="translate(10.000000, 10.000000)">
+                    <g id="编组-2" transform="translate(112.000000, 4.000000)">
+                        <g id="编组-8" transform="translate(0.000000, 70.000000)">
+                            <g id="曲目数量" transform="translate(0.000000, 1.000000)">
+                                <path d="M7.5,13.7325 C4.0600867,13.7276789 1.27288113,10.9399142 1.26875,7.5 C1.27356953,4.06057395 4.06057395,1.27356953 7.5,1.26875 C10.9399963,1.27219516 13.7278048,4.06000369 13.73125,7.5 C13.7264305,10.9394261 10.9394261,13.7264305 7.5,13.73125 L7.5,13.7325 Z M7.5,0 C3.35957804,0.00413422372 0.00413422372,3.35957804 0,7.5 C0.00482172375,11.6401368 3.35986324,14.9951783 7.5,15 C11.6401368,14.9951783 14.9951783,11.6401368 15,7.5 C15,3.36375 11.635,0 7.5,0 Z M5.65,2.3825 C4.01426316,2.97269069 2.75969986,4.30996224 2.275,5.98 C2.25875001,6.04 2.05625001,6.6825 2.72375,6.81375 C3.39,6.94375 3.51375,6.26875 3.56125,6.13 C3.97801767,4.92223188 4.92787577,3.97375706 6.13625,3.55875 C6.245,3.52125 6.85374999,3.22875 6.64125,2.6725 C6.42875,2.11625001 5.75125,2.3475 5.6525,2.38375 L5.6525,2.3825 L5.65,2.3825 Z M7.5,8.695 C7.0636448,8.70956018 6.65401361,8.48523833 6.43126245,8.10973895 C6.20851129,7.73423958 6.20802302,7.26720878 6.42998854,6.89124446 C6.65195405,6.51528014 7.0611153,6.29010226 7.4975,6.30375 C7.92487896,6.30330342 8.32003165,6.53089472 8.53410789,6.90079247 C8.74818412,7.27069021 8.74866062,7.72669814 8.53535789,8.09704247 C8.32205516,8.4673868 7.92737896,8.69580342 7.5,8.69625 L7.5,8.695 Z M7.5,5.0325 C6.13809049,5.0325 5.03398032,6.13642406 5.03375031,7.49833355 C5.03352037,8.86024304 6.13725766,9.96453975 7.49916709,9.96500001 C8.86107653,9.96545999 9.96555973,8.86190934 9.96625,7.5 C9.96418334,6.13898713 8.86101375,5.03637667 7.5,5.035 L7.5,5.0325 Z" id="形状"></path>
+                            </g>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 11 - 0
src/student/music/album/favorite.svg


+ 13 - 0
src/student/music/album/favorited.svg

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="专辑详情" transform="translate(-299.000000, -245.000000)" fill="#FFC459" fill-rule="nonzero">
+            <g id="编组-9" transform="translate(276.000000, 245.000000)">
+                <g id="已收藏" transform="translate(23.000000, 0.000000)">
+                    <path d="M13.0588435,16 C12.8351907,16 12.5806892,15.9398798 12.3493243,15.8196393 L9.00224436,14.1888778 L5.63202796,15.8196393 C5.41608732,15.9323647 5.17701019,15.992485 4.92250872,15.992485 C4.59859776,15.992485 4.28239897,15.8947896 4.0278975,15.7069138 C3.55745539,15.3612224 3.31837826,14.7675351 3.42634858,14.2189379 L4.11273132,10.739479 L1.4674585,8.34218437 C1.05871372,7.93637275 0.904470402,7.35771543 1.05871372,6.80911824 L1.06642588,6.79408818 C1.25923002,6.23046092 1.73738429,5.83967936 2.31579672,5.76452906 L5.98678758,5.11072144 L7.62947887,1.8491984 C7.89169251,1.33066132 8.4315441,1 9.00224436,1 C9.59608112,1 10.151357,1.34569138 10.382722,1.85671343 L12.0254133,5.11072144 L15.6964042,5.73446894 C16.2748166,5.81713427 16.760683,6.22294589 16.9226385,6.77905812 C17.1077305,7.32014028 16.9534872,7.91382766 16.5370302,8.31963928 L16.5293181,8.32715431 L13.8994696,10.746994 L14.5627158,14.2339679 C14.6706861,14.7900802 14.4393212,15.3537074 13.9688791,15.7069138 C13.6989533,15.8947896 13.3827545,16 13.0588435,16 Z" id="路径"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 33 - 0
src/student/music/album/footer.tsx

@@ -0,0 +1,33 @@
+import { Icon } from 'vant'
+import { defineComponent } from 'vue'
+import CountIcon from './count.svg'
+import FavoriteIcon from './favorite.svg'
+import styles from './item.module.less'
+
+export default defineComponent({
+  name: 'AlbumFooter',
+  props: {
+    musicSheetCount: {
+      type: Number,
+      default: 0
+    },
+    albumFavoriteCount: {
+      type: Number,
+      default: 0
+    }
+  },
+  setup({ musicSheetCount, albumFavoriteCount }) {
+    return () => (
+      <footer class={styles.footer}>
+        <div>
+          <Icon class={styles.icon} name={CountIcon} />
+          <span>{musicSheetCount}首</span>
+        </div>
+        <div>
+          <Icon class={styles.icon} name={FavoriteIcon} />
+          <span>{albumFavoriteCount}人收藏</span>
+        </div>
+      </footer>
+    )
+  }
+})

+ 59 - 0
src/student/music/album/index.tsx

@@ -0,0 +1,59 @@
+import { defineComponent, reactive, ref } from 'vue'
+import { Sticky, List } from 'vant'
+import Search from '@/components/col-search'
+import request from '@/helpers/request'
+import Item from './item'
+
+export default defineComponent({
+  name: 'Album',
+  setup() {
+    const params = reactive({
+      search: '',
+      page: 1
+    })
+    const data = ref<any>(null)
+    const loading = ref(false)
+    const finished = ref(false)
+    const isError = ref(false)
+
+    const onSearch = (value: string) => {
+      params.page = 1
+      params.search = value
+      FetchList()
+    }
+
+    const FetchList = async () => {
+      if (loading.value) {
+        return
+      }
+      loading.value = true
+      isError.value = false
+      try {
+        const res = await request.post('/api-student/music/album/list', {
+          data: params
+        })
+        data.value = res.data
+        params.page = res.data.pageNo + 1
+        finished.value = res.data.pageNo >= res.data.totalPage
+      } catch (error) {
+        isError.value = true
+      }
+      loading.value = false
+    }
+    return () => (
+      <List
+        loading={loading.value}
+        finished={finished.value}
+        finished-text="没有更多了"
+        onLoad={FetchList}
+      >
+        <Sticky>
+          <Search showAction onSearch={onSearch} />
+        </Sticky>
+        {data.value && data.value.rows.length
+          ? data.value.rows.map(item => <Item data={item} />)
+          : null}
+      </List>
+    )
+  }
+})

+ 66 - 0
src/student/music/album/item.module.less

@@ -0,0 +1,66 @@
+.album {
+  margin: 12px 14px;
+  padding: 10px;
+  background-color: var(--music-list-item-background-color);
+  border-radius: 10px;
+  display: flex;
+  .img {
+    width: 94px;
+    height: 94px;
+    margin-right: 18px;
+    position: relative;
+    > img,
+    > div {
+      position: absolute;
+      border-radius: 10px;
+      overflow: hidden;
+    }
+    &::before {
+      content: '';
+      width: 80px;
+      height: 80px;
+      border-radius: 9px;
+      background-color: var(--music-list-item-background-color);
+      box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.08);
+      position: absolute;
+      right: -6px;
+      top: 8px;
+    }
+  }
+  .content {
+    flex: 1;
+    > h4 {
+      color: var(--music-list-item-title-color);
+      font-size: 14px;
+      height: 20px;
+      line-height: 20px;
+    }
+    > p {
+      margin-top: 6px;
+      font-size: 12px;
+      color: var(--music-list-item-desc-color);
+      line-height: 17px;
+      height: 34px;
+    }
+  }
+}
+
+.footer {
+  margin-top: 11px;
+  display: flex;
+  > div {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 12px;
+    color: var(--music-list-item-mate-color);
+    margin-right: 18px;
+    .icon {
+      margin-right: 5px;
+    }
+    span {
+      display: block;
+      margin-top: 1px;
+    }
+  }
+}

+ 29 - 0
src/student/music/album/item.tsx

@@ -0,0 +1,29 @@
+import { Image } from 'vant'
+import { defineComponent } from 'vue'
+import Footer from './footer'
+import styles from './item.module.less'
+
+export default defineComponent({
+  name: 'AlbumItem',
+  props: {
+    data: {
+      type: Object,
+      default: {}
+    }
+  },
+  setup({ data }) {
+    return () => (
+      <div class={styles.album}>
+        <Image class={styles.img} src={data.albumCoverUrl} />
+        <div class={styles.content}>
+          <h4 class="van-ellipsis">{data.albumName}</h4>
+          <p class="van-multi-ellipsis--l2">{data.albumDesc}</p>
+          <Footer
+            musicSheetCount={data.musicSheetCount}
+            albumFavoriteCount={data.albumFavoriteCount}
+          />
+        </div>
+      </div>
+    )
+  }
+})

BIN
src/student/music/list/icons/init-user-icon.png


BIN
src/student/music/list/icons/music-icon.png


+ 59 - 0
src/student/music/list/index.tsx

@@ -0,0 +1,59 @@
+import { defineComponent, reactive, ref } from 'vue'
+import { Sticky, List } from 'vant'
+import Search from '@/components/col-search'
+import request from '@/helpers/request'
+import Item from './item'
+
+export default defineComponent({
+  name: 'MusicList',
+  setup() {
+    const params = reactive({
+      search: '',
+      page: 1
+    })
+    const data = ref<any>(null)
+    const loading = ref(false)
+    const finished = ref(false)
+    const isError = ref(false)
+
+    const onSearch = (value: string) => {
+      params.page = 1
+      params.search = value
+      FetchList()
+    }
+
+    const FetchList = async () => {
+      if (loading.value) {
+        return
+      }
+      loading.value = true
+      isError.value = false
+      try {
+        const res = await request.post('/api-student/music/sheet/list', {
+          data: params
+        })
+        data.value = res.data
+        params.page = res.data.pageNo + 1
+        finished.value = res.data.pageNo >= res.data.totalPage
+      } catch (error) {
+        isError.value = true
+      }
+      loading.value = false
+    }
+    return () => (
+      <List
+        loading={loading.value}
+        finished={finished.value}
+        finished-text="没有更多了"
+        onLoad={FetchList}
+      >
+        <Sticky>
+          <Search showAction onSearch={onSearch} />
+        </Sticky>
+        {data.value && data.value.rows.length
+          ? data.value.rows.map(item => <Item data={item} />)
+          : null}
+      </List>
+    )
+  }
+})

+ 78 - 0
src/student/music/list/item.module.less

@@ -0,0 +1,78 @@
+.item {
+  background-color: var(--music-list-item-background-color);
+  margin: 10px 14px;
+  padding: 10px;
+  border-radius: 9px;
+  .header {
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid var(--music-list-item-border-color);
+    padding-bottom: 12px;
+    .mate {
+      display: flex;
+      flex: 1;
+      align-items: center;
+      .icon {
+        width: 40px;
+        height: 40px;
+      }
+      .info {
+        margin-left: 14px;
+        > h4 {
+          color: var(--music-list-item-title-color);
+          font-size: 14px;
+          font-weight: 600;
+        }
+        > p {
+          color: var(--music-list-item-mate-color);
+          line-height: 17px;
+        }
+      }
+    }
+    .btn {
+      width: 54px;
+      height: 22px;
+      font-size: 12px;
+      border-radius: 11px;
+      padding: 0;
+      border: none;
+      &.vip {
+        background-color: var(--music-list-item-vip-bg);
+        color: var(--music-list-item-vip-color);
+      }
+      &.free {
+        background-color: var(--music-list-item-free-bg);
+        color: var(--music-list-item-free-color);
+      }
+      &.charge {
+        background-color: var(--music-list-item-charge-bg);
+        color: var(--music-list-item-charge-color);
+      }
+    }
+  }
+  .footer {
+    display: flex;
+    padding-top: 8px;
+    align-items: center;
+    justify-content: space-between;
+    .user {
+      display: flex;
+      align-items: center;
+      padding: 0 10px;
+      .userIcon {
+        width: 20px;
+        height: 20px;
+        margin-right: 8px;
+      }
+    }
+    .favorite {
+      font-size: 16px;
+    }
+    .tags {
+      display: flex;
+      align-items: center;
+      --van-tag-default-color: #fff1de;
+      --van-tag-text-color: #ff8c00;
+    }
+  }
+}

+ 67 - 0
src/student/music/list/item.tsx

@@ -0,0 +1,67 @@
+import { defineComponent } from 'vue'
+import { Button, Icon, Image, Tag } from 'vant'
+import classNames from 'classnames'
+import MusicIcon from './icons/music-icon.png'
+import InitUserIcon from './icons/init-user-icon.png'
+import FavoriteIcon from '../album/favorite.svg'
+import styles from './item.module.less'
+
+const chargeTypes = {
+  CHARGE: '点播',
+  FREE: '免费',
+  VIP: 'VIP'
+}
+
+export default defineComponent({
+  name: 'MusicItem',
+  props: {
+    data: {
+      type: Object,
+      default: {}
+    }
+  },
+  setup({ data }) {
+    return () => (
+      <div class={styles.item}>
+        <header class={styles.header}>
+          <div class={styles.mate}>
+            <Image src={MusicIcon} round class={styles.icon} />
+            <div class={styles.info}>
+              <h4>{data.musicSheetName}</h4>
+              <p>{data.composer}</p>
+            </div>
+          </div>
+          <div class={styles.buttons}>
+            <Button
+              class={classNames(
+                styles.btn,
+                styles[data.chargeType.toLocaleLowerCase()]
+              )}
+            >
+              {chargeTypes[data.chargeType]}
+              <Icon name="arrow" />
+            </Button>
+          </div>
+        </header>
+        <footer class={styles.footer}>
+          <div class={styles.user}>
+            <Image
+              round
+              src={data.addUserAvatar || InitUserIcon}
+              class={styles.userIcon}
+            />
+            <p>{data.addName}</p>
+            <div class={styles.tags}>
+              {(data.subjectNames || '').split(',').map(item => (
+                <Tag>{item}</Tag>
+              ))}
+            </div>
+          </div>
+          <div class={styles.icons}>
+            <Icon class={styles.favorite} name={FavoriteIcon} />
+          </div>
+        </footer>
+      </div>
+    )
+  }
+})

+ 37 - 0
src/student/music/search/index.module.less

@@ -0,0 +1,37 @@
+.search {
+  --van-cell-background-color: transparent;
+  --van-cell-font-size: 16px;
+  --van-cell-text-color: #333;
+  --van-cell-value-color: #999;
+  --van-cell-icon-size: 10px;
+  .title {
+    padding-top: 16px;
+    :global(.van-cell__value) {
+      font-size: 12px;
+    }
+  }
+  .keywords {
+    margin-top: 10px;
+    padding: 0 14px;
+    padding-bottom: 10px;
+    display: flex;
+    align-items: center;
+    .content {
+      flex: 1;
+      overflow: hidden;
+      overflow-x: auto;
+      display: flex;
+      .searchKeyword {
+        --van-tag-default-color: white;
+        --van-tag-text-color: #333;
+        font-size: 14px;
+        padding: 4px 10px;
+        margin-right: 5px;
+      }
+    }
+
+    .remove {
+      font-size: 16px;
+    }
+  }
+}

+ 83 - 0
src/student/music/search/index.tsx

@@ -0,0 +1,83 @@
+import { Sticky, Cell, Tag, Icon } from 'vant'
+import { defineComponent, ref } from 'vue'
+import Search from '@/components/col-search'
+import { useLocalStorage } from '@vueuse/core'
+import AlbumItem from '../album/item'
+import MusicItem from '../list/item'
+import styles from './index.module.less'
+import classNames from 'classnames'
+import request from '@/helpers/request'
+
+export default defineComponent({
+  name: 'MusicSearch',
+  setup() {
+    const loading = ref(false)
+    const keyword = ref('')
+    const tagVisibility = ref(false)
+    const words = useLocalStorage<string[]>('music-search', [])
+    const FetchList = async () => {
+      loading.value = true
+      await request.post('/api-student/music/sheet/albumAndSheetList', {})
+    }
+    const onSearch = val => {
+      keyword.value = val
+      const indexOf = words.value.indexOf(val)
+      if (indexOf > -1) {
+        words.value.splice(indexOf, 1)
+      }
+      if (val) {
+        words.value.unshift(val)
+        words.value.length = Math.min(words.value.length, 5)
+      }
+    }
+    return () => {
+      return (
+        <div class={styles.search}>
+          <Sticky>
+            <Search
+              modelValue={keyword.value}
+              showAction
+              onSearch={onSearch}
+              onFilter={() => (tagVisibility.value = true)}
+            />
+          </Sticky>
+          {words.value.length > 0 && (
+            <div class={classNames(styles.keywords, 'van-hairline--bottom')}>
+              <div class={styles.content}>
+                {words.value.map(item => (
+                  <Tag
+                    round
+                    class={styles.searchKeyword}
+                    key={item}
+                    onClick={() => onSearch(item)}
+                  >
+                    {item}
+                  </Tag>
+                ))}
+              </div>
+              <Icon
+                class={styles.remove}
+                name="delete-o"
+                onClick={() => (words.value = [])}
+              />
+            </div>
+          )}
+          <Cell
+            class={styles.title}
+            title="专辑"
+            is-link
+            to="/music-album"
+            value="更多"
+          />
+          <Cell
+            class={styles.title}
+            title="曲谱"
+            is-link
+            to="/music-list"
+            value="更多"
+          />
+        </div>
+      )
+    }
+  }
+})

+ 16 - 0
src/student/music/search/select-tag.tsx

@@ -0,0 +1,16 @@
+import request from '@/helpers/request'
+import { useAsyncState } from '@vueuse/core'
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'SelectTag',
+  setup() {
+    const { isLoading, state } = useAsyncState(
+      request.post('/api-student/MusicTag/tree', {
+        data: {}
+      }),
+      null
+    )
+    return () => {}
+  }
+})

+ 53 - 0
src/student/music/songbook/album.tsx

@@ -0,0 +1,53 @@
+import { defineComponent } from 'vue'
+import { Image, Skeleton } from 'vant'
+import request from '@/helpers/request'
+import { useAsyncState } from '@vueuse/core'
+import styles from './index.module.less'
+import classNames from 'classnames'
+
+export default defineComponent({
+  name: 'Songbook',
+  setup() {
+    const { isLoading, state } = useAsyncState(
+      request.post('/api-student/music/album/list', {
+        data: {}
+      }),
+      null
+    )
+    return () => (
+      <>
+        {state.value && state.value.data.rows.length ? (
+          <div class={styles.albumContainer}>
+            {state.value.data.rows.map(item => (
+              <div class={styles.album} key={item.id}>
+                <div class={styles.main}>
+                  <Image class={styles.img} src={item.albumCoverUrl} />
+                  <p class={styles.favorite}>
+                    <span>{item.albumFavoriteCount}</span>人收藏
+                  </p>
+                </div>
+                <h4 class={classNames(styles.title, 'van-ellipsis')}>
+                  {item.albumName}
+                </h4>
+              </div>
+            ))}
+          </div>
+        ) : null}
+        {isLoading.value && (
+          <div style={{ display: 'flex' }}>
+            {Array.from({ length: 4 }).map(() => (
+              <Skeleton
+                style={{ width: '96px', flexDirection: 'column' }}
+                avatar
+                avatar-shape="square"
+                avatar-size="96px"
+                title
+                titleWidth="100%"
+              />
+            ))}
+          </div>
+        )}
+      </>
+    )
+  }
+})

+ 75 - 0
src/student/music/songbook/index.module.less

@@ -0,0 +1,75 @@
+.songbook {
+  --van-cell-background-color: transparent;
+  --van-cell-font-size: 16px;
+  --van-cell-text-color: #333;
+  --van-cell-value-color: #999;
+  --van-cell-icon-size: 10px;
+
+  --favorite-bg: rgba(67, 67, 67, 0.3);
+  --favorite-color: #63ffe1;
+  --favorite-text-color: white;
+}
+.title {
+  padding-top: 16px;
+  :global(.van-cell__value) {
+    font-size: 12px;
+  }
+}
+.albumContainer {
+  width: 100%;
+  overflow: hidden;
+  overflow-x: auto;
+  display: flex;
+  padding: 0 16px;
+  box-sizing: border-box;
+  flex-wrap: nowrap;
+  .album {
+    margin-right: 20px;
+    // overflow: hidden;
+    width: 94px;
+    flex-shrink: 0;
+    .main {
+      position: relative;
+      .favorite {
+        position: absolute;
+        bottom: 8px;
+        text-align: center;
+        transform: translateX(50%);
+        background-color: var(--favorite-bg);
+        color: var(--favorite-text-color);
+        padding: 2px 7px;
+        border-radius: 10px;
+        font-size: 11px;
+        > span {
+          color: var(--favorite-color);
+        }
+      }
+    }
+    .img {
+      width: 94px;
+      height: 94px;
+      position: relative;
+      > img,
+      > div {
+        position: absolute;
+        border-radius: 10px;
+        overflow: hidden;
+      }
+      &::before {
+        content: '';
+        width: 80px;
+        height: 80px;
+        border-radius: 9px;
+        background-color: #fff;
+        box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.08);
+        position: absolute;
+        right: -6px;
+        top: 8px;
+      }
+    }
+    .title {
+      width: 94px;
+      text-align: center;
+    }
+  }
+}

+ 35 - 0
src/student/music/songbook/index.tsx

@@ -0,0 +1,35 @@
+import { defineComponent } from 'vue'
+import { Cell, Sticky } from 'vant'
+import Search from '@/components/col-search'
+import Album from './album'
+import List from './list'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'Songbook',
+  setup() {
+    return () => (
+      <div class={styles.songbook}>
+        <Sticky>
+          <Search showAction />
+        </Sticky>
+        <Cell
+          class={styles.title}
+          title="专辑"
+          is-link
+          to="/music-album"
+          value="更多"
+        />
+        <Album />
+        <Cell
+          class={styles.title}
+          title="曲谱"
+          is-link
+          to="/music-list"
+          value="更多"
+        />
+        <List />
+      </div>
+    )
+  }
+})

+ 26 - 0
src/student/music/songbook/list.tsx

@@ -0,0 +1,26 @@
+import { defineComponent } from 'vue'
+import { Image } from 'vant'
+import request from '@/helpers/request'
+import { useAsyncState } from '@vueuse/core'
+import Item from '../list/item'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'Songbook',
+  setup() {
+    const { isLoading, state } = useAsyncState(
+      request.post('/api-student/music/sheet/list', {
+        data: {}
+      }),
+      null
+    )
+    return () =>
+      state.value && state.value.data.rows.length ? (
+        <div class={styles.listContainer}>
+          {state.value.data.rows.map(item => (
+            <Item data={item} />
+          ))}
+        </div>
+      ) : null
+  }
+})

+ 14 - 0
src/styles/index.less

@@ -135,3 +135,17 @@ body {
   transform: translateZ(0);
   -webkit-transform: translateZ(0);
 }
+
+:root {
+  --music-list-item-background-color: #fff;
+  --music-list-item-title-color: #333;
+  --music-list-item-desc-color: #333;
+  --music-list-item-mate-color: #6a6a6a;
+  --music-list-item-border-color: #f1f1f1;
+  --music-list-item-vip-bg: #fff1cd;
+  --music-list-item-vip-color: #ff6c00;
+  --music-list-item-free-bg: #fff1e7;
+  --music-list-item-free-color: #ff4700;
+  --music-list-item-charge-bg: #e1f0ff;
+  --music-list-item-charge-color: #0086ff;
+}

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov