Browse Source

添加声部、声部分类、乐器、曲目、曲目分类、项目曲目页面

yuanliang 1 year ago
parent
commit
d4187d9c46
26 changed files with 2786 additions and 587 deletions
  1. 4 2
      src/router/constant.ts
  2. 14 4
      src/utils/constant.ts
  3. 2 2
      src/views/content-manage/music-manage/component/music-list.tsx
  4. 6 6
      src/views/content-manage/music-manage/modal/music-operation.tsx
  5. 1 1
      src/views/educational-manage/component/category-list.tsx
  6. 1 1
      src/views/educational-manage/model/addMaterial.tsx
  7. 1 1
      src/views/knowledge-manage/knowledge-list/index.tsx
  8. 65 227
      src/views/music-library/api.ts
  9. 51 77
      src/views/music-library/music-sheet/component/music-list.tsx
  10. 194 0
      src/views/music-library/music-sheet/component/music-sheet-categories-list.tsx
  11. 29 28
      src/views/music-library/music-sheet/index.tsx
  12. 115 105
      src/views/music-library/music-sheet/modal/music-operation.tsx
  13. 116 0
      src/views/music-library/music-sheet/modal/save-categroy-dialog.tsx
  14. 143 133
      src/views/music-library/music-sheet/modal/use-project.tsx
  15. 7 0
      src/views/music-library/project-music-sheet/index.tsx
  16. 240 0
      src/views/music-library/project-music-sheet/module/cooleshow-edu/addMusic.tsx
  17. 349 0
      src/views/music-library/project-music-sheet/module/cooleshow-edu/cooleshow-edu.tsx
  18. 133 0
      src/views/system-manage/subject-manage/api.ts
  19. 8 0
      src/views/system-manage/subject-manage/index.module.less
  20. 56 0
      src/views/system-manage/subject-manage/index.tsx
  21. 286 0
      src/views/system-manage/subject-manage/instrument/instrument-list.tsx
  22. 149 0
      src/views/system-manage/subject-manage/instrument/modal/instrument-save.tsx
  23. 194 0
      src/views/system-manage/subject-manage/subject-categorize/categorize-list.tsx
  24. 103 0
      src/views/system-manage/subject-manage/subject-categorize/modal/categorize-save.tsx
  25. 192 0
      src/views/system-manage/subject-manage/subject/modal/subject-save.tsx
  26. 327 0
      src/views/system-manage/subject-manage/subject/subject-list.tsx

+ 4 - 2
src/router/constant.ts

@@ -15,7 +15,9 @@ export const asyncRoutes = {
     employeeManage: () => import('@/views/system-manage/employee-manage/employee-tab'), // 员工管理
     protocolManage: () => import('@/views/system-manage/protocol-manage/index'), // 协议管理
     paramSettings: () => import('@/views/system-manage/param-settings/index'), // 参数设置
-    songManage: () => import('@/views/system-manage/song-manage/index'), // 声部管理
+    // songManage: () => import('@/views/system-manage/song-manage/index'), // 声部管理
+    songManage: () => import('@/views/system-manage/subject-manage/index'), // 声部管理
+    instrumentManger: () => import('@/views/system-manage/subject-manage/instrument/instrument-list'), // 乐器管理
     songSetting: () => import('@/views/system-manage/song-setting/index'), // 声部基础配置
     appVersion: () => import('@/views/system-manage/app-version/index'), // app版本控制
     systemHoliday: () => import('@/views/system-manage/system-holiday/index'), // 节假日设置
@@ -65,6 +67,6 @@ export const asyncRoutes = {
     smsCodeMessage: () => import('@/views/sms-code-message/index'), // 短信验证码
     musicSheet: () => import('@/views/music-library/music-sheet/index'), // 曲谱管理
     projectMusicSheetManager: () => import('@/views/music-library/project-music-sheet/index'), // 曲谱管理
-    instrumentManger: () => import('@/views/system-manage/instrument-manage/index'), // 应用管理
+    // subjectCategorizeManage: () => import('@/views/system-manage/instrument-manage/index'), // 声部分类管理
 
 }

+ 14 - 4
src/utils/constant.ts

@@ -244,8 +244,18 @@ export const musicSheetSourceType = {
   PERSON: '个人'
 } as any
 
-// 审核版本
-export const releaseCandidate = {
-  YES: '是',
-  NO: '否'
+// 声部管理
+// 默认谱面
+export const defaultScore = {
+  STAVE: '五线谱',
+  FIRST: '首调',
+  JIAN: '固定值'
+} as any
+
+// 曲目适用项目
+export const musicSheetUseApp = {
+  MEC: '管乐迷',
+  jMEDU: '管乐团',
+  COOLESHOW: '酷乐秀',
+  COOLESHOW_EDU: '音乐数字乐堂'
 } as any

+ 2 - 2
src/views/content-manage/music-manage/component/music-list.tsx

@@ -18,13 +18,13 @@ import {
 } from 'naive-ui'
 import { defineComponent, onMounted, reactive, ref, watch } from 'vue'
 import { musicSheetPage, musicSheetRemove, musicSheetStatus, musicTagPage } from '../../api'
-import { getMusicSheetCategories } from '@/views/music-categrory/api'
 import MusicOperation from '../modal/music-operation'
 import { subjectBasicConfigPage } from '@/views/system-manage/api'
 import { accompanimentTypeArray, audioTypeArray } from '@/utils/searchArray'
 import MusicPreView from '../modal/musicPreView'
 import TheTooltip from '@/components/TheTooltip'
 import { filterPointCategory } from '@/views/teaching-manage/unit-test'
+import {musicSheetCategoriesPage} from "@views/music-library/api";
 
 export default defineComponent({
   name: 'content-flash',
@@ -268,7 +268,7 @@ export default defineComponent({
     // 获取分类
     const getMusicSheetCategorieList = async () => {
       try {
-        const { data } = await getMusicSheetCategories({ enable: true })
+        const { data } = await musicSheetCategoriesPage({ enable: true })
         state.musicSheetCategories = filterPointCategory(data, 'musicSheetCategoriesList')
       } catch (e) {}
     }

+ 6 - 6
src/views/content-manage/music-manage/modal/music-operation.tsx

@@ -271,7 +271,7 @@ export default defineComponent({
       xmlFileUrl: null, // XML
       midiUrl: null, // mid
       metronomeUrl: null, // 伴奏 根据mp3Type 是否是为包含节拍器
-      musicSheetName: null, // 曲目名称
+      name: null, // 曲目名称
       musicTag: [] as any, // 曲目标签
       composer: null, // 音乐人
       playSpeed: null, // 曲谱速度
@@ -282,7 +282,7 @@ export default defineComponent({
       auditVersion: null as any, // 审核版本
       accompanimentType: null, // 伴奏类型
       sortNumber: null, // 排序
-      titleImg: null, // 曲谱封面
+      musicCover: null, // 曲谱封面
       remark: null, // 曲谱描述
       background: [] as any, // 原音
       musicSheetCategoriesId: null,
@@ -450,7 +450,7 @@ export default defineComponent({
           forms.xmlFileUrl = data.xmlFileUrl
           forms.midiUrl = data.midiUrl
           forms.metronomeUrl = data.metronomeUrl
-          forms.musicSheetName = data.musicSheetName
+          forms.name = data.musicSheetName
           forms.musicTag = data.musicTag?.split(',')
           forms.composer = data.composer
           forms.playSpeed = data.playSpeed
@@ -461,7 +461,7 @@ export default defineComponent({
           forms.auditVersion = Number(data.auditVersion)
           forms.accompanimentType = data.accompanimentType
           forms.sortNumber = data.sortNumber
-          forms.titleImg = data.titleImg
+          forms.musicCover = data.titleImg
           forms.remark = data.remark
           forms.status = data.status
           forms.musicSheetCategoriesId = data.musicSheetCategoriesId
@@ -632,7 +632,7 @@ export default defineComponent({
                 }
               ]}
             >
-              <NInput v-model:value={forms.musicSheetName} placeholder="请输入曲目名称" />
+              <NInput v-model:value={forms.name} placeholder="请输入曲目名称" />
             </NFormItemGi>
             {/* <NFormItemGi
               label="曲目标签"
@@ -798,7 +798,7 @@ export default defineComponent({
               <UploadFile
                 accept=".jpg,.jpeg,.png"
                 tips="请上传大小2M以内的JPG、PNG图片"
-                v-model:fileList={forms.titleImg}
+                v-model:fileList={forms.musicCover}
                 cropper
                 bucketName="cloud-coach"
                 options={{

+ 1 - 1
src/views/educational-manage/component/category-list.tsx

@@ -38,7 +38,7 @@ export default defineComponent({
     const searchForm = reactive({
       keyword: '',
       times: null as any,
-      operatorId: '' //创建人
+      operatorKeyword: '' //创建人
     })
 
     const columns = (): DataTableColumn[] => {

+ 1 - 1
src/views/educational-manage/model/addMaterial.tsx

@@ -63,7 +63,7 @@ export default defineComponent({
           })
           if (saveModel.type === 'SONG') {
             musicOpentions.music = {
-              musicSheetName: res.data.contentDesc
+              name: res.data.contentDesc
             }
           }
           console.log('🚀 ~ saveModel', saveModel)

+ 1 - 1
src/views/knowledge-manage/knowledge-list/index.tsx

@@ -47,7 +47,7 @@ export default defineComponent({
       keyword: null,
       time: null,
       courseTypeCode: null,
-      operatorId: null,
+      operatorKeyword: null,
       enableFlag: null
     })
     const dialog = useDialog()

+ 65 - 227
src/views/music-library/api.ts

@@ -1,100 +1,44 @@
 import request from '@/utils/request'
 
 /**
- * @description: 帮助中心列表
- */
-export const helpCenterContentPage = (params: object) => {
-  return request({
-    url: '/cbs-app/helpCenterContent/page',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 帮助中心-启用/停用
- */
-export const helpCenterContentStatus = (params: any) => {
-  return request({
-    url: '/cbs-app/helpCenterContent/status/' + params.id,
-    method: 'post'
-  } as any)
-}
-
-
-/**
- * @description: 帮助中心添加
- */
-export const helpCenterContentSave = (params: object) => {
-  return request({
-    url: '/cbs-app/helpCenterContent/save',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 帮助中心修改
- */
-export const helpCenterContentUpdate = (params: object) => {
-  return request({
-    url: '/cbs-app/helpCenterContent/update',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 帮助中心删除
- */
-export const helpCenterContentRemove = (params: object) => {
-  return request({
-    url: '/cbs-app/helpCenterContent/remove',
-    method: 'post',
-    data: params,
-    requestType: 'form'
-  } as any)
-}
-
-/**
- * @description: 帮助中心分类列表
+ * @description: 乐谱标签列表
  */
-export const helpCenterCatalogPage = (params: object) => {
+export const musicTagPage = (params: object) => {
   return request({
-    url: '/cbs-app/helpCenterCatalog/page',
+    url: '/cbs-app/musicTag/page',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 帮助中心分类添加
+ * @description: 乐谱标签添加
  */
-export const helpCenterCatalogSave = (params: object) => {
+export const musicTagSave = (params: object) => {
   return request({
-    url: '/cbs-app/helpCenterCatalog/save',
+    url: '/cbs-app/musicTag/save',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 帮助中心分类更新
+ * @description: 乐谱标签更新
  */
-export const helpCenterCatalogUpdate = (params: object) => {
+export const musicTagUpdate = (params: object) => {
   return request({
-    url: '/cbs-app/helpCenterCatalog/update',
+    url: '/cbs-app/musicTag/update',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 帮助中心分类删除
+ * @description: 乐谱标签删除
  */
-export const helpCenterCatalogRemove = (params: object) => {
+export const musicTagRemove = (params: object) => {
   return request({
-    url: '/cbs-app/helpCenterCatalog/remove',
+    url: '/cbs-app/musicTag/remove',
     method: 'post',
     data: params,
     requestType: 'form'
@@ -102,89 +46,55 @@ export const helpCenterCatalogRemove = (params: object) => {
 }
 
 /**
- * @description: 资讯列表
- */
-export const sysNewsInformationPage = (params: object) => {
-  return request({
-    url: '/cbs-app/sysNewsInformation/page',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 资讯添加
- */
-export const sysNewsInformationSave = (params: object) => {
-  return request({
-    url: '/cbs-app/sysNewsInformation/save',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 资讯更新
- */
-export const sysNewsInformationUpdate = (params: object) => {
-  return request({
-    url: '/cbs-app/sysNewsInformation/update',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 资讯删除
+ * @description: 乐谱标签启用/停用
  */
-export const sysNewsInformationRemove = (params: object) => {
+export const musicTagState = (params: any) => {
   return request({
-    url: '/cbs-app/sysNewsInformation/remove',
+    url: '/cbs-app/musicTag/state/' + params.id,
     method: 'post',
-    data: params,
     requestType: 'form'
   } as any)
 }
 
 /**
- * @description: 公告列表
+ * @description: 乐谱列表
  */
-export const sysNoticePage = (params: object) => {
+export const musicSheetPage = (params: object) => {
   return request({
-    url: '/cbs-app/sysNotice/page',
+    url: '/cbs-app/musicSheet/page',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 公告添加
+ * @description: 乐谱添加
  */
-export const sysNoticeSave = (params: object) => {
+export const musicSheetSave = (params: object) => {
   return request({
-    url: '/cbs-app/sysNotice/save',
+    url: '/cbs-app/musicSheet/save',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 公告更新
+ * @description: 乐谱更新
  */
-export const sysNoticeUpdate = (params: object) => {
+export const musicSheetUpdate = (params: object) => {
   return request({
-    url: '/cbs-app/sysNotice/update',
+    url: '/cbs-app/musicSheet/update',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 公告删除
+ * @description: 乐谱删除
  */
-export const sysNoticeRemove = (params: object) => {
+export const musicSheetRemove = (params: object) => {
   return request({
-    url: '/cbs-app/sysNotice/remove',
+    url: '/cbs-app/musicSheet/remove',
     method: 'post',
     data: params,
     requestType: 'form'
@@ -192,168 +102,96 @@ export const sysNoticeRemove = (params: object) => {
 }
 
 /**
- * @description: 公告启用/停用
+ * @description: 乐谱详情
  */
-export const sysNoticeStatus = (params?: any) => {
+export const musicSheetDetail = (params?: any) => {
   return request({
-    url: '/cbs-app/sysNotice/status/' + params.id,
-    method: 'post',
+    url: '/cbs-app/musicSheet/detail/' + params.id,
+    method: 'get',
     requestType: 'form'
   } as any)
 }
 
-/**
- * @description: APP按钮管理列表
- */
-export const sysMenuButtonPage = (params: object) => {
-  return request({
-    url: '/cbs-app/sysMenuButton/page',
-    method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: APP按钮管理添加
- */
-export const sysMenuButtonSave = (params: object) => {
-  return request({
-    url: '/cbs-app/sysMenuButton/save',
-    method: 'post',
-    data: params
-  } as any)
-}
 
 /**
- * @description: APP按钮管理更新
+ * @description: 乐谱-启用-停用
  */
-export const sysMenuButtonUpdate = (params: object) => {
+export const musicSheetStatus = (params?: any) => {
   return request({
-    url: '/cbs-app/sysMenuButton/update',
+    url: '/cbs-app/musicSheet/status',
     method: 'post',
+    requestType: 'form',
     data: params
   } as any)
 }
 
-/**
- * @description: APP按钮管理删除
- */
-export const sysMenuButtonRemove = (params: object) => {
-  return request({
-    url: '/cbs-app/sysMenuButton/remove',
-    method: 'post',
-    data: params,
-    requestType: 'form'
-  } as any)
-}
 
-/**
- * @description: 乐谱标签列表
- */
-export const musicTagPage = (params: object) => {
-  return request({
-    url: '/cbs-app/musicTag/page',
-    method: 'post',
-    data: params
-  } as any)
-}
+//---
 
-/**
- * @description: 乐谱标签添加
- */
-export const musicTagSave = (params: object) => {
-  return request({
-    url: '/cbs-app/musicTag/save',
-    method: 'post',
-    data: params
-  } as any)
-}
 
 /**
- * @description: 乐谱标签更新
+ * @description: 曲谱分类
+ * @param params
  */
-export const musicTagUpdate = (params: object) => {
+export const musicSheetCategoriesPage = (params: object) => {
   return request({
-    url: '/cbs-app/musicTag/update',
+    url: '/cbs-app/musicSheetCategories/page',
     method: 'post',
     data: params
   } as any)
 }
-
 /**
- * @description: 乐谱标签删除
+ * @description: 曲谱分类树
+ * @param params
  */
-export const musicTagRemove = (params: object) => {
+export const musicSheetCategoriesQueryTree = (params: object) => {
   return request({
-    url: '/cbs-app/musicTag/remove',
-    method: 'post',
+    url: '/cbs-app/musicSheetCategories/queryTree',
+    method: 'get',
     data: params,
-    requestType: 'form'
+    params
   } as any)
 }
 
 /**
- * @description: 乐谱标签启用/停用
+ * @description: 曲谱分类添加
  */
-export const musicTagState = (params: any) => {
+export const musicSheetCategoriesSave = (params: object) => {
   return request({
-    url: '/cbs-app/musicTag/state/' + params.id,
-    method: 'post',
-    requestType: 'form'
-  } as any)
-}
-
-/**
- * @description: 乐谱列表
- */
-export const musicSheetPage = (params: object) => {
-  return request({
-    url: '/cbs-app/musicSheet/page',
+    url: '/cbs-app/musicSheetCategories/save',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 乐谱添加
+ * @description: 曲谱分类更新
  */
-export const musicSheetSave = (params: object) => {
+export const musicSheetCategoriesUpdate = (params: object) => {
   return request({
-    url: '/cbs-app/musicSheet/save',
+    url: '/cbs-app/musicSheetCategories/update',
     method: 'post',
     data: params
   } as any)
 }
 
 /**
- * @description: 乐谱更新
+ * @description: 曲谱分类删除
  */
-export const musicSheetUpdate = (params: object) => {
+export const musicSheetCategoriesRemove = (id: object) => {
   return request({
-    url: '/cbs-app/musicSheet/update',
+    url: '/cbs-app/musicSheetCategories/remove?id=' + id,
     method: 'post',
-    data: params
-  } as any)
-}
-
-/**
- * @description: 乐谱删除
- */
-export const musicSheetRemove = (params: object) => {
-  return request({
-    url: '/cbs-app/musicSheet/remove',
-    method: 'post',
-    data: params,
-    requestType: 'form'
   } as any)
 }
 
 /**
- * @description: 乐谱详情
+ *  @description: 曲谱分类详情
+ * @param params
  */
-export const musicSheetDetail = (params?: any) => {
+export const musicSheetCategoriesDetail = (params?: any) => {
   return request({
-    url: '/cbs-app/musicSheet/detail/' + params.id,
+    url: '/cbs-app/musicSheetCategories/detail/' + params.id,
     method: 'get',
     requestType: 'form'
   } as any)
@@ -361,13 +199,13 @@ export const musicSheetDetail = (params?: any) => {
 
 
 /**
- * @description: 乐谱-启用-停用
+ *  @description: 曲谱分类详情
+ * @param params
  */
-export const musicSheetStatus = (params?: any) => {
+export const musicSheetCategoriesChangeStatus = (params?: any) => {
   return request({
-    url: '/cbs-app/musicSheet/status',
-    method: 'post',
-    requestType: 'form',
-    data: params
+    url: '/cbs-app/musicSheetCategories/detail/' + params.id,
+    method: 'get',
+    requestType: 'form'
   } as any)
 }

+ 51 - 77
src/views/music-library/music-sheet/component/music-list.tsx

@@ -2,19 +2,19 @@ import SaveForm from '@/components/save-form'
 import Pagination from '@/components/pagination'
 import {NButton, NCascader, NDataTable, NFormItem, NIcon, NImage, NInput, NModal, NSelect, NSpace, NTag, useDialog, useMessage} from 'naive-ui'
 import {defineComponent, onMounted, reactive, ref, watch} from 'vue'
-import {musicSheetPage, musicSheetRemove, musicSheetStatus, musicTagPage} from '../../api'
-import {getMusicSheetCategories} from '@/views/music-categrory/api'
+import {musicSheetCategoriesPage, musicSheetCategoriesQueryTree, musicSheetPage, musicSheetRemove, musicSheetStatus, musicTagPage} from '../../api'
 import MusicOperation from '../modal/music-operation'
 import {subjectPage} from '@/views/system-manage/api'
 import MusicPreView from '../modal/musicPreView'
 import {filterPointCategory} from '@/views/teaching-manage/unit-test'
 import UseProject from "@views/music-library/music-sheet/modal/use-project";
-import {filterClientType, getMapValueByKey} from "@/utils/filters";
-import {getValueForKey} from "@/utils/searchArray";
-import {musicSheetSourceType, musicSheetType} from "@/utils/constant";
+import {getMapValueByKey} from "@/utils/filters";
+import {musicSheetUseApp, musicSheetSourceType, musicSheetType} from "@/utils/constant";
+import {getSelectDataFromObj} from "@/utils/objectUtil";
+import {sysApplicationPage} from "@views/menu-manage/api";
 
 export default defineComponent({
-  name: 'content-flash',
+  name: 'music-list',
   props: ['searchId'],
   setup(props, {emit}) {
     const dialog = useDialog()
@@ -28,17 +28,15 @@ export default defineComponent({
       },
       searchForm: {
         keyword: null,
-        musicTag: null,
-        musicSubject: null,
-        audioType: null,
-        accompanimentType: null,
-        status: null,
-        topFlag: null,
-        author: null,
-        belongTo: null,
-        app: null,
-        authorFrom: null,
-        musicSheetCategoriesId: props.searchId
+        musicSheetType: null,
+        subjectId: null, //声部ID
+        sourceType: null, //来源类型/作者属性(PLATFORM: 平台; ORG: 机构; PERSON: 个人
+        composer: null, //作曲人/音乐人
+        userId: null, //所属人
+        useAppId: null, //适用项目ID
+        status: null, //曲目状态(0:停用,1:启用)
+        appAuditFlag: null, //是否审核版本
+        categoriesId: null, //是否审核版本
       },
       dataList: [] as any,
       subjectList: [] as any,
@@ -279,12 +277,11 @@ export default defineComponent({
       try {
         state.loading = true
         const {data} = await musicSheetPage({...state.pagination, ...state.searchForm})
-        state.loading = false
         state.pagination.pageTotal = Number(data.total)
         state.dataList = data.rows || []
       } catch {
-        state.loading = false
       }
+      state.loading = false
     }
 
     // 获取标签
@@ -304,14 +301,14 @@ export default defineComponent({
     // 获取分类
     const getMusicSheetCategorieList = async () => {
       try {
-        const {data} = await getMusicSheetCategories({enable: true})
+        const {data} = await musicSheetCategoriesQueryTree({enable: true})
         state.musicSheetCategories = filterPointCategory(data, 'musicSheetCategoriesList')
       } catch (e) {
       }
     }
 
     // 获取声部
-    const getSubjectList = async () => {
+    const initSubjectList = async () => {
       try {
         const {data} = await subjectPage({page: 1, rows: 999})
         const tempList = data.rows || []
@@ -324,6 +321,20 @@ export default defineComponent({
       }
     }
 
+    // app列表
+    const initUseAppList = async () => {
+      try {
+        const {data} = await sysApplicationPage({page: 1, rows: 999})
+        const tempList = data.rows || []
+        tempList.forEach((item: any) => {
+          item.label = item.appName
+          item.value = item.id
+        })
+        state.showUseProjectData = tempList
+      } catch {
+      }
+    }
+
     const saveForm = ref()
     const onSubmit = () => {
       state.pagination.page = 1
@@ -344,12 +355,12 @@ export default defineComponent({
 
     onMounted(() => {
       // getTagList()
-      console.log()
       if (props.searchId) {
-        state.searchForm.musicSheetCategoriesId = props.searchId || null
+        state.searchForm.categoriesId = props.searchId || null
       }
 
-      getSubjectList()
+      initSubjectList()
+      initUseAppList()
       getList()
       getMusicSheetCategorieList()
     })
@@ -370,21 +381,18 @@ export default defineComponent({
                   clearable
               />
             </NFormItem>
-            <NFormItem label="分类" path="musicSheetCategoriesId">
-              <NCascader
-                  valueField="id"
-                  labelField="name"
-                  children-field="musicSheetCategoriesList"
-                  placeholder="请选择分类"
-                  v-model:value={state.searchForm.musicSheetCategoriesId}
-                  options={state.musicSheetCategories}
+            <NFormItem label="曲目类型" path="musicSheetType">
+              <NSelect
+                  placeholder="请选择曲目类型"
+                  v-model:value={state.searchForm.musicSheetType}
+                  options={getSelectDataFromObj(musicSheetType)}
                   clearable
               />
             </NFormItem>
             <NFormItem label="声部" path="musicSubject">
               <NSelect
                   placeholder="请选择声部"
-                  v-model:value={state.searchForm.musicSubject}
+                  v-model:value={state.searchForm.subjectId}
                   options={state.subjectList}
                   clearable
               />
@@ -392,64 +400,30 @@ export default defineComponent({
             <NFormItem label="作者属性" path="authorFrom">
               <NSelect
                   placeholder="请选择作者属性"
-                  v-model:value={state.searchForm.authorFrom}
-                  options={
-                    [
-                      {
-                        label: '平台',
-                        value: 'PLATFORM'
-                      },
-                      {
-                        label: '机构',
-                        value: 'TENANT'
-                      },
-                      {
-                        label: '个人',
-                        value: 'SELF'
-                      }
-                    ] as any
-                  }
+                  v-model:value={state.searchForm.sourceType}
+                  options={getSelectDataFromObj(musicSheetSourceType)}
                   clearable
               />
             </NFormItem>
             <NFormItem label="音乐人" path="author">
               <NInput
                   placeholder="请选择音乐人"
-                  v-model:value={state.searchForm.author}
+                  v-model:value={state.searchForm.composer}
                   clearable
               />
             </NFormItem>
             <NFormItem label="所属人" path="author">
               <NInput
                   placeholder="请选择所属人"
-                  v-model:value={state.searchForm.belongTo}
+                  v-model:value={state.searchForm.userId}
                   clearable
               />
             </NFormItem>
-            <NFormItem label="使用项目" path="app">
+            <NFormItem label="用项目" path="app">
               <NSelect
-                  placeholder="请选择使用项目"
-                  v-model:value={state.searchForm.app}
-                  options={
-                    [
-                      {
-                        label: '管乐迷',
-                        value: 'MEC'
-                      },
-                      {
-                        label: '管乐团',
-                        value: 'jMEDU'
-                      },
-                      {
-                        label: '酷乐秀',
-                        value: 'COOLESHOW'
-                      },
-                      {
-                        label: '音乐数字乐堂',
-                        value: 'COOLESHOW-EDU'
-                      }
-                    ] as any
-                  }
+                  placeholder="请选择适用项目"
+                  v-model:value={state.searchForm.useAppId}
+                  options={state.showUseProjectData}
                   clearable
               />
             </NFormItem>
@@ -472,9 +446,9 @@ export default defineComponent({
                   }
               />
             </NFormItem>
-            <NFormItem label="审核版本" path="status">
+            <NFormItem label="审核版本" path="appAuditFlag">
               <NSelect
-                  v-model={[state.searchForm.status, 'value']}
+                  v-model={[state.searchForm.appAuditFlag, 'value']}
                   placeholder="请选择审核版本"
                   clearable
                   options={

+ 194 - 0
src/views/music-library/music-sheet/component/music-sheet-categories-list.tsx

@@ -0,0 +1,194 @@
+import {DataTableColumn, NButton, NDataTable, NModal, NSpace, useDialog, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, reactive} from 'vue'
+import {musicSheetCategoriesQueryTree, musicSheetCategoriesRemove} from "@views/music-library/api";
+import SaveCategroyDialog from "@views/music-library/music-sheet/modal/save-categroy-dialog";
+
+type RowData = {
+  id: string
+  name: string
+  coverImg: string
+  musicSheetNum: string
+  updateTime: string
+  enable: boolean
+  musicSheetCategoriesList?: RowData[]
+}
+export default defineComponent({
+  emits: ['setTabName'],
+  name: 'music-category',
+  setup() {
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+      },
+      dataList: [] as any,
+      showSaveDialog: false,
+      saveMode: 'add',
+      saveRowData: null as any,
+    })
+
+    const dialog = useDialog()
+    const message = useMessage()
+
+    const getList = async () => {
+      try {
+        state.loading = true
+        const body = {
+          page: state.pagination.page,
+          rows: state.pagination.rows,
+        }
+        const {data} = await musicSheetCategoriesQueryTree(body)
+        state.dataList = data || []
+      } catch (e) {
+      }
+      state.loading = false
+    }
+
+    onMounted(() => {
+      getList()
+    })
+
+    const columns = (): DataTableColumn[] => {
+      return [
+        {
+          title: '编号',
+          key: 'id'
+        },
+        {
+          title: '分类名称',
+          key: 'name'
+        },
+        {
+          title: '曲目数量',
+          key: 'musicSheetNum'
+        },
+        {
+          title: '最后更新时间',
+          key: 'updateTime'
+        },
+        {
+          title: '更新人',
+          key: 'operatorName'
+        },
+
+        {
+          title: '操作',
+          key: 'operation',
+          fixed: 'right',
+          render(row: any) {
+            return (
+                <NSpace>
+                  <NButton
+                      //v-auth="musicSheetCategories/switching1608067671668359169"
+                      type="error"
+                      size="small"
+                      text
+                      onClick={() => onRemove(row)}
+                  >
+                    删除
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      onClick={() => {
+                        state.saveRowData = row
+                        state.saveMode = 'edit'
+                        state.showSaveDialog = true
+                      }}
+                      //v-auth="musicSheetCategories/update1608067835686617090"
+                  >
+                    修改
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      onClick={() => {
+                        state.saveRowData = row
+                        state.showSaveDialog = true
+                        state.saveMode = 'add'
+                      }}
+                      //v-auth="musicSheetCategories/save1608067543331045378"
+                  >
+                    添加子分类
+                  </NButton>
+                </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    const onSubmit = () => {
+      state.pagination.page = 1
+      getList()
+    }
+
+    // 删除
+    const onRemove = async (row: any) => {
+      dialog.warning({
+        title: '警告',
+        content: "是否确认删除'" + row.name + "'?",
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          const res = await musicSheetCategoriesRemove(row.id)
+          if (res.data) {
+            onSubmit()
+            message.success('删除成功')
+          }
+        }
+      })
+    }
+    return () => (
+        <div class="system-menu-container">
+          <div class={['section-container']}>
+            <NSpace style={{paddingBottom: '12px'}}>
+              <NButton
+                  type="primary"
+                  //v-auth="musicSheetCategories/save1608067543331045378"
+                  onClick={() => {
+                    state.saveMode = 'add'
+                    state.showSaveDialog = true
+                    state.saveRowData = null
+                  }}
+              >
+                新增分类
+              </NButton>
+            </NSpace>
+
+            <NDataTable
+                // scrollX={1000}
+                loading={state.loading}
+                columns={columns()}
+                data={state.dataList}
+                rowKey={(row: any) => row.id}
+                childrenKey="musicSheetCategoriesList"
+            ></NDataTable>
+          </div>
+          <NModal
+              v-model:show={state.showSaveDialog}
+              preset="dialog"
+              showIcon={false}
+              title={state.saveRowData ? '修改曲谱分类' : '新增曲谱分类'}
+              style={{width: '550px'}}
+          >
+            <SaveCategroyDialog
+                onGetList={() => {
+                  state.showSaveDialog = false
+                  getList()
+                }}
+                onClose={() => {
+                  state.showSaveDialog = false
+                }}
+                list={state.dataList}
+                saveMode={state.saveMode}
+                actvieRow={state.saveRowData}
+            ></SaveCategroyDialog>
+          </NModal>
+        </div>
+    )
+  }
+})

+ 29 - 28
src/views/music-library/music-sheet/index.tsx

@@ -1,10 +1,10 @@
-import { NTabPane, NTabs } from 'naive-ui'
-import { defineComponent, h, reactive, resolveDynamicComponent } from 'vue'
+import {NTabPane, NTabs} from 'naive-ui'
+import {defineComponent, reactive} from 'vue'
 import MusicList from './component/music-list'
-import TagList from './component/tag-list'
-import CategroryList from '@/views/music-categrory/index'
-import { useRoute } from 'vue-router'
-import { getTabsCache, setTabsCaches } from '@/hooks/use-async'
+import {useRoute} from 'vue-router'
+import {getTabsCache, setTabsCaches} from '@/hooks/use-async'
+import MusicSheetCategoriesList from "@views/music-library/music-sheet/component/music-sheet-categories-list";
+
 export default defineComponent({
   name: 'city-manage',
   setup() {
@@ -28,31 +28,32 @@ export default defineComponent({
     }
     return () => {
       return (
-        <div class="system-menu-container">
-          <h2>曲目管理</h2>
+          <div class="system-menu-container">
+            <h2>曲目管理</h2>
 
-          <div class={['section-container']} style="padding-top: 0">
-            <NTabs
-              type="line"
-              size="large"
-              v-model:value={state.tabName}
-              onUpdate:value={(val: any) => setTabs(val)}
-            >
-              <NTabPane name="MusicList" tab="曲目列表"
-                  //v-auth="musicSheet/page1602301588206350338"
-              >
-                <MusicList searchId={state.searchId} />
-              </NTabPane>
-              <NTabPane
-                name="CategroryList"
-                tab="曲目分类管理"
-                //v-auth="/musicCategrory1607664813521346561"
+            <div class={['section-container']} style="padding-top: 0">
+              <NTabs
+                  type="line"
+                  size="large"
+                  v-model:value={state.tabName}
+                  onUpdate:value={(val: any) => setTabs(val)}
               >
-                <CategroryList onSetTabName={setTabName} />
-              </NTabPane>
-            </NTabs>
+                <NTabPane name="MusicList" tab="曲目列表"
+                    //v-auth="musicSheet/page1602301588206350338"
+                >
+                  <MusicList searchId={state.searchId}/>
+                </NTabPane>
+                <NTabPane
+                    name="CategroryList"
+                    tab="曲目分类管理"
+                    //v-auth="/musicCategrory1607664813521346561"
+                >
+                  <MusicSheetCategoriesList onSetTabName={setTabName}/>
+                  {/*<TagList />*/}
+                </NTabPane>
+              </NTabs>
+            </div>
           </div>
-        </div>
       )
     }
   }

+ 115 - 105
src/views/music-library/music-sheet/modal/music-operation.tsx

@@ -1,32 +1,13 @@
-import {
-  NForm,
-  NFormItem,
-  NInput,
-  NSelect,
-  NSpace,
-  NButton,
-  useMessage,
-  NRadioGroup,
-  NRadio,
-  NGrid,
-  NFormItemGi,
-  NInputNumber,
-  NGi,
-  useDialog,
-  NCascader,
-  NAlert,
-  NInputGroup,
-  NInputGroupLabel, NCheckbox
-} from 'naive-ui'
 import type {SelectOption} from 'naive-ui'
+import {NAlert, NButton, NCascader, NCheckbox, NForm, NFormItemGi, NGi, NGrid, NInput, NInputNumber, NRadio, NRadioGroup, NSelect, NSpace, useDialog, useMessage} from 'naive-ui'
 import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
 import {musicSheetDetail, musicSheetSave, musicSheetUpdate} from '../../api'
 import UploadFile from '@/components/upload-file'
 import styles from './index.module.less'
 import deepClone from '@/utils/deep.clone'
 import axios from 'axios'
-import {Checkbox, CheckboxGroup} from "vant";
-import {releaseCandidate} from "@/utils/constant";
+import {CheckboxGroup} from "vant";
+import {musicSheetSourceType, musicSheetType} from "@/utils/constant";
 import {getSelectDataFromObj} from "@/utils/objectUtil";
 
 /**
@@ -99,8 +80,7 @@ export const onlyVisible = (xml: any, partIndex: any) => {
           break
         }
       }
-      const nextNoteDuration = nextNote?.getElementsByTagName('duration')[0]
-      return nextNoteDuration
+      return nextNote?.getElementsByTagName('duration')[0]
     }
     Array.from(notes).forEach((note, i) => {
       const graces = note.getElementsByTagName('grace')
@@ -270,35 +250,35 @@ export default defineComponent({
   setup(props, {slots, attrs, emit}) {
     const forms = reactive({
       graduals: {} as any, // 渐变速度
-      audioType: 'MP3', // 播放类型
-      mp3Type: 'MP3', // 是否含节拍器
+      playMode: 'MP3', // 播放类型
       xmlFileUrl: null, // XML
       midiUrl: null, // mid
       metronomeUrl: null, // 伴奏 根据mp3Type 是否是为包含节拍器
-      musicSheetName: null, // 曲目名称
+      name: null, // 曲目名称
       musicTag: [] as any, // 曲目标签
       composer: null, // 音乐人
       playSpeed: null, // 曲谱速度
       showFingering: null as any, // 是否显示指法
       canEvaluate: null as any, // 是否评测
-      musicSubject: null as any, // 可用声部
+      subjectIds: null as any, // 可用声部
       notation: null as any, // 能否转和简谱
       auditVersion: null as any, // 审核版本
-      accompanimentType: 1, // 伴奏类型
+      audioType: 'HOMEMODE', // 伴奏类型
       sortNumber: null, // 排序
-      titleImg: null, // 曲谱封面
+      musicCover: null, // 曲谱封面
       remark: null, // 曲谱描述
       background: [] as any, // 原音
       musicSheetCategoriesId: null,
       status: false,
-      musicSheetType: 'SINGLE' as 'SINGLE' | 'CONCERT',
-      author: null, //音乐人
-      authorFrom: undefined,
-      belongTo: undefined,
-      speed: undefined,
-      playMetronome: 1,
+      musicSheetType: null, // 曲目类型
+      sourceType: undefined,
+      userId: null, // 所属人
+      isPlayBeat: 1, // 是否播放节拍器
       playStyle: undefined, // 播放方式
-      releaseCandidate: 'NO' // 是否审核版本
+      appAuditFlag: false, // 是否审核版本
+      midiFileUrl: null, // 伴奏文件
+      musicalInstrumentIds: null,  //可用乐器
+      musicCategoryId: null,  //曲目分类
     })
     const state = reactive({
       tagList: [...props.tagList] as any, // 标签列表
@@ -460,22 +440,20 @@ export default defineComponent({
         try {
           const {data} = await musicSheetDetail({id: detail.id})
           forms.audioType = data.audioType
-          forms.mp3Type = data.mp3Type
+          forms.playMode = data.playMode
           forms.xmlFileUrl = data.xmlFileUrl
           forms.midiUrl = data.midiUrl
           forms.metronomeUrl = data.metronomeUrl
-          forms.musicSheetName = data.musicSheetName
+          forms.name = data.name
           forms.musicTag = data.musicTag?.split(',')
           forms.composer = data.composer
           forms.playSpeed = data.playSpeed
           forms.showFingering = Number(data.showFingering)
           forms.canEvaluate = Number(data.canEvaluate)
-          forms.musicSubject = data.musicSubject ? Number(data.musicSubject) : null
           forms.notation = Number(data.notation)
           forms.auditVersion = Number(data.auditVersion)
-          forms.accompanimentType = data.accompanimentType
           forms.sortNumber = data.sortNumber
-          forms.titleImg = data.titleImg
+          forms.musicCover = data.musicCover
           forms.remark = data.remark
           forms.status = data.status
           forms.musicSheetCategoriesId = data.musicSheetCategoriesId
@@ -510,11 +488,11 @@ export default defineComponent({
               label-placement="left"
               label-width="130"
           >
-            <NAlert showIcon={false} style={{marginBottom:"12px" }}>曲目信息</NAlert>
+            <NAlert showIcon={false} style={{marginBottom: "12px"}}>曲目信息</NAlert>
             <NGrid cols={2}>
               <NFormItemGi
                   label="曲目名称"
-                  path="musicSheetName"
+                  path="name"
                   rule={[
                     {
                       required: true,
@@ -522,11 +500,16 @@ export default defineComponent({
                     }
                   ]}
               >
-                <NInput v-model:value={forms.musicSheetName} placeholder="请输入曲目名称"/>
+                <NInput
+                    v-model:value={forms.name}
+                    placeholder="请输入曲目名称"
+                    maxlength={120}
+                    showCount
+                />
               </NFormItemGi>
               <NFormItemGi
                   label="音乐人"
-                  path="author"
+                  path="composer"
                   rule={[
                     {
                       required: true,
@@ -534,7 +517,8 @@ export default defineComponent({
                     }
                   ]}
               >
-                <NInput v-model:value={forms.author} placeholder="请输入音乐人"
+                <NInput v-model:value={forms.composer}
+                        placeholder="请输入音乐人名称"
                         showCount
                         maxlength={14}
                 />
@@ -551,7 +535,7 @@ export default defineComponent({
                     v-model:value={forms.remark}
                 />
               </NFormItemGi>
-              <NFormItemGi label="曲谱封面" path="titleImg"
+              <NFormItemGi label="曲目封面" path="musicCover"
                            rule={[
                              {
                                required: true
@@ -559,10 +543,11 @@ export default defineComponent({
                            ]}>
                 <UploadFile
                     accept=".jpg,.jpeg,.png"
-                    tips="请上传大小2M以内的JPG、PNG图片"
-                    v-model:fileList={forms.titleImg}
+                    tips="请上传大小1M以内的JPG、PNG图片"
+                    size={1}
+                    v-model:fileList={forms.musicCover}
                     cropper
-                    bucketName="cloud-coach"
+                    bucketName="cbs"
                     options={{
                       autoCrop: true, //是否默认生成截图框
                       enlarge: 2, //  图片放大倍数
@@ -570,7 +555,7 @@ export default defineComponent({
                       fixedBox: true, //是否固定截图框大认生成截图框宽度
                       autoCropHeight: 200, //默认生成截图小 不允许改变
                       previewsCircle: false, //预览图是否是原圆形
-                      title: '曲封面'
+                      title: '曲封面'
                     }}
                 />
               </NFormItemGi>
@@ -586,15 +571,17 @@ export default defineComponent({
                     }
                   ]}
               >
-                <NRadioGroup v-model:value={forms.musicSheetType}>
-                  <NRadio value="SINGLE">独奏</NRadio>
-                  <NRadio value="CONCERT">合奏</NRadio>
-                </NRadioGroup>
+                <NSelect
+                    placeholder="请选择曲目类型"
+                    v-model:value={forms.musicSheetType}
+                    options={getSelectDataFromObj(musicSheetType)}
+                    clearable
+                />
               </NFormItemGi>
 
               <NFormItemGi
                   label="作者属性"
-                  path="authorFrom"
+                  path="sourceType"
                   rule={[
                     {
                       required: true,
@@ -603,24 +590,8 @@ export default defineComponent({
                   ]}
               >
                 <NSelect
-                    v-model:value={forms.authorFrom}
-                    options={
-                      [
-                        {
-                          label: '平台',
-                          value: 'PALTFORM'
-                        },
-                        {
-                          label: '机构',
-                          value: 'TENANT'
-                        },
-                        {
-                          label: '个人',
-                          value: 'SELF'
-                        }
-                      ] as any
-                    }
-                    filterable
+                    v-model:value={forms.sourceType}
+                    options={getSelectDataFromObj(musicSheetSourceType)}
                     clearable
                     placeholder="请选择作者属性"
                 />
@@ -630,7 +601,7 @@ export default defineComponent({
             <NGrid cols={2}>
               <NFormItemGi
                   label="所属人"
-                  path="belongTo"
+                  path="userId"
                   rule={[
                     {
                       required: true,
@@ -639,12 +610,12 @@ export default defineComponent({
                   ]}
               >
                 <NSelect
-                    v-model:value={forms.belongTo}
+                    v-model:value={forms.userId}
                     options={
                       [
                         {
                           label: '小A',
-                          value: '小A'
+                          value: '小B'
                         }
                       ] as any
                     }
@@ -653,25 +624,47 @@ export default defineComponent({
                     placeholder="请选择曲目所属人"
                 />
               </NFormItemGi>
-              <NFormItemGi label="速度" path="speed">
+              <NFormItemGi label="速度" path="playSpeed">
                 <NInputNumber
                     placeholder="请输入速度"
-                    v-model:value={forms.speed}
+                    v-model:value={forms.playSpeed}
+                    style="width:100%"
                 />
               </NFormItemGi>
             </NGrid>
             <NGrid cols={2}>
               <NFormItemGi label="审核版本" path="speed">
                 <NSelect
-                    options={getSelectDataFromObj(releaseCandidate)}
-                    v-model:value={forms.releaseCandidate}
-                    defaultValue={'NO'}
+                    options={
+                      [
+                        {
+                          label: '是',
+                          value: true
+                        },
+                        {
+                          label: '否',
+                          value: false
+                        }
+                      ] as any
+                    }
+                    v-model:value={forms.appAuditFlag}
+                />
+              </NFormItemGi>
+              <NFormItemGi label="曲目分类" path="musicSheetCategories">
+                <NCascader
+                    valueField="id"
+                    labelField="name"
+                    children-field="musicSheetCategoriesList"
+                    placeholder="请选择分类"
+                    v-model:value={forms.musicSheetCategoriesId}
+                    options={state.musicSheetCategories}
+                    clearable
                 />
               </NFormItemGi>
             </NGrid>
-            <NAlert showIcon={false} style={{marginBottom:"12px" }}>曲目上传</NAlert>
+            <NAlert showIcon={false} style={{marginBottom: "12px"}}>曲目上传</NAlert>
             <NGrid cols={2}>
-              <NFormItemGi label="播放模式" path="audioType"
+              <NFormItemGi label="播放模式" path="playMode"
                            rule={[
                              {
                                required: true,
@@ -680,12 +673,12 @@ export default defineComponent({
                            ]}
               >
                 <NRadioGroup
-                    v-model:value={forms.audioType}
+                    v-model:value={forms.playMode}
                     onUpdateValue={(value: string | number | boolean) => {
                       if (value === 'MP3') {
-                        forms.mp3Type = 'MP3'
+                        forms.playMode = 'MP3'
                       } else {
-                        forms.mp3Type = 'MIDI'
+                        forms.playMode = 'MIDI'
                       }
                     }}
                 >
@@ -693,7 +686,7 @@ export default defineComponent({
                   <NRadio value="MIDI">MIDI</NRadio>
                 </NRadioGroup>
               </NFormItemGi>
-              {forms.mp3Type === 'MP3' && (
+              {forms.playMode === 'MP3' && (
                   <NFormItemGi
                       label="伴奏类型"
                       path="audioType"
@@ -704,19 +697,19 @@ export default defineComponent({
                       ]}
                   >
                     <NRadioGroup
-                        v-model:value={forms.accompanimentType}
+                        v-model:value={forms.audioType}
                     >
-                      <NRadio value={1}>自制伴奏</NRadio>
-                      <NRadio value={2}>普通伴奏</NRadio>
+                      <NRadio value={'HOMEMODE'}>自制伴奏</NRadio>
+                      <NRadio value={'COMMON'}>普通伴奏</NRadio>
                     </NRadioGroup>
                   </NFormItemGi>
               )}
             </NGrid>
             <NGrid cols={2}>
-              {forms.mp3Type === 'MP3' && (
+              {forms.playMode === 'MP3' && (
                   <NFormItemGi
                       label="上传伴奏"
-                      path="xmlFileUrl"
+                      path="midiFileUrl"
                       rule={[
                         {
                           required: true,
@@ -726,7 +719,7 @@ export default defineComponent({
                   >
                     <UploadFile
                         size={10}
-                        v-model:fileList={forms.xmlFileUrl}
+                        v-model:fileList={forms.midiFileUrl}
                         tips="仅支持上传.mp3/.aac格式文件"
                         listType="image"
                         accept=".mp3,.aac"
@@ -736,10 +729,10 @@ export default defineComponent({
                     />
                   </NFormItemGi>
               )}
-              {forms.mp3Type === 'MIDI' && (
+              {forms.playMode === 'MIDI' && (
                   <NFormItemGi
                       label="上传MIDI"
-                      path="xmlFileUrl"
+                      path="midiFileUrl"
                       rule={[
                         {
                           required: true,
@@ -749,7 +742,7 @@ export default defineComponent({
                   >
                     <UploadFile
                         size={10}
-                        v-model:fileList={forms.xmlFileUrl}
+                        v-model:fileList={forms.midiFileUrl}
                         tips="仅支持上传.MIDI格式文件"
                         listType="image"
                         accept=".mp3,.aac"
@@ -792,7 +785,7 @@ export default defineComponent({
                            ]}
               >
                 <NSelect
-                    v-model:value={forms.musicSubject}
+                    v-model:value={forms.subjectIds}
                     options={props.subjectList}
                     multiple
                     filterable
@@ -800,7 +793,7 @@ export default defineComponent({
                     placeholder="请选择可用声部"
                 />
               </NFormItemGi>
-              { (forms.mp3Type === 'MP3' || forms.musicSheetType === 'SINGLE') && (
+              {(forms.playMode === 'MP3' || forms.musicSheetType === 'SINGLE') && (
                   <NFormItemGi
                       label="页面渲染声轨"
                       path="audioType"
@@ -819,7 +812,7 @@ export default defineComponent({
                     </CheckboxGroup>
                   </NFormItemGi>
               )}
-              {forms.mp3Type === 'MIDI' && forms.musicSheetType === 'CONCERT' && (
+              {forms.playMode === 'MIDI' && forms.musicSheetType === 'CONCERT' && (
                   <NFormItemGi
                       label="用户可切换声轨"
                       path="audioType"
@@ -842,6 +835,23 @@ export default defineComponent({
             </NGrid>
 
             <NGrid cols={2}>
+              <NFormItemGi label="可用乐器" path="audioType"
+                           rule={[
+                             {
+                               required: true,
+                               message: '请选择可用乐器'
+                             }
+                           ]}
+              >
+                <NSelect
+                    options={
+                      [] as any
+                    }
+                    v-model:value={forms.musicalInstrumentIds}
+                />
+              </NFormItemGi>
+            </NGrid>
+            <NGrid cols={2}>
               <NFormItemGi label="是否播放节拍器" path="audioType"
                            rule={[
                              {
@@ -851,13 +861,13 @@ export default defineComponent({
                            ]}
               >
                 <NRadioGroup
-                    v-model:value={forms.playMetronome}
+                    v-model:value={forms.isPlayBeat}
                 >
                   <NRadio value={1}>是</NRadio>
                   <NRadio value={0}>否</NRadio>
                 </NRadioGroup>
               </NFormItemGi>
-              {forms.playMetronome && (
+              {forms.isPlayBeat && (
                   <NFormItemGi label="播放方式" path="audioType"
                                rule={[
                                  {
@@ -876,7 +886,7 @@ export default defineComponent({
               )}
             </NGrid>
             {/* 只有播放类型为mp3时才会有原音 */}
-            {forms.audioType === 'MP3' && (
+            {forms.playMode === 'MP3' && (
                 <>
                   {forms.background.map((item: any, index: number) => (
                       <>
@@ -954,7 +964,7 @@ export default defineComponent({
 
           {props.type !== 'preview' &&
               (
-                  <NSpace justify="end">
+                  <NSpace justify="end" style="padding-top:12px">
                     <NButton type="default" onClick={() => emit('close')}>
                       取消
                     </NButton>

+ 116 - 0
src/views/music-library/music-sheet/modal/save-categroy-dialog.tsx

@@ -0,0 +1,116 @@
+import {NButton, NCascader, NForm, NFormItem, NInput, NSpace, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, reactive, ref} from 'vue'
+import {filterPointCategory} from '@/views/teaching-manage/unit-test'
+import {musicSheetCategoriesSave, musicSheetCategoriesUpdate} from "@views/music-library/api";
+
+export default defineComponent({
+  emits: ['close', 'getList'],
+  props: ['actvieRow', 'saveMode', 'list'],
+  name: 'save-categroy-dialog',
+  setup(props, {emit}) {
+    const message = useMessage()
+    const formRef = ref()
+    const parentList = ref([
+      {name: '顶级分类', id: 0},
+      ...filterPointCategory(props.list, 'musicSheetCategoriesList')
+    ] as any)
+    const state = reactive({
+      loading: false,
+      dataList: [] as any,
+      btnLoading: false
+    })
+    const forms = reactive({
+      id: '',
+      name: '',
+      enable: false,
+      parentId: '0'
+    } as any)
+
+    const onSubmit = async () => {
+      formRef.value?.validate(async (errors: any) => {
+        if (errors) {
+          message.error(errors)
+          return;
+        }
+        try {
+          if(props.saveMode==='add'){
+            const {data} = await musicSheetCategoriesSave({...forms})
+            emit('getList')
+            message.success('添加成功')
+          }else {
+            const {data} = await musicSheetCategoriesUpdate({...forms})
+            emit('getList')
+            message.success('修改成功')
+          }
+        } catch (error) {
+        }
+      })
+    }
+
+    onMounted(() => {
+      if (props.saveMode === 'add') {
+        // 添加子分类
+        if (props.actvieRow && props.actvieRow.id) {
+          forms.parentId = props?.actvieRow.id + ''
+        }
+      } else {
+        for (let key in props?.actvieRow) {
+          forms[key] = props?.actvieRow[key]
+        }
+      }
+    })
+    return () => (
+        <div>
+          <NForm ref={formRef} model={forms}>
+            {props.saveMode === 'add' && props.actvieRow && (
+                <NFormItem label="父级分类" path="parentId">
+                  <NCascader
+                      placeholder="请选择父级分类"
+                      v-model:value={forms.parentId}
+                      options={parentList.value}
+                      checkStrategy="all"
+                      expandTrigger="hover"
+                      childrenField="musicSheetCategoriesList"
+                      valueField="id"
+                      labelField="name"
+                      disabled={true}
+                  />
+                </NFormItem>
+            )}
+
+            <NFormItem
+                label="分类名称"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入分类名称',
+                    trigger: ['blur', 'input']
+                  }
+                ]}
+                path="name"
+            >
+              <NInput
+                  v-model:value={forms.name}
+                  placeholder="请输入分类名称"
+                  maxlength={25}
+                  clearable
+              ></NInput>
+            </NFormItem>
+          </NForm>
+          <NSpace justify="end">
+            <NButton type="default" onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton
+                type="primary"
+                onClick={() => onSubmit()}
+                loading={state.btnLoading}
+                disabled={state.btnLoading}
+            >
+              确认
+            </NButton>
+          </NSpace>
+        </div>
+    )
+  }
+})

+ 143 - 133
src/views/music-library/music-sheet/modal/use-project.tsx

@@ -22,8 +22,8 @@ export default defineComponent({
   },
   setup(props, {emit}) {
     const state = reactive({
-      tabName: '管乐迷',
-      selectApp: '', //选择的APP
+      tabName: null as any,
+      selectApp: [] as any, //选择的APP
       showTabMec: true,
       showTabJmedu: true,
 
@@ -70,13 +70,17 @@ export default defineComponent({
                 rule={[
                   {
                     required: true,
-                    message: '请选择播放类型'
+                    message: '请选择适用应用'
                   }
                 ]}
             >
               <NCheckboxGroup
                   onUpdateValue={(value) => {
-                    state.selectApp = value.toString();
+                    state.selectApp = value;
+                    if (!state.tabName) {
+                      // 没有值,将第一个作为选中对象
+                      state.tabName = value.toString()
+                    }
                   }}
               >
                 <NCheckbox value="MEC">管乐迷</NCheckbox>
@@ -90,136 +94,142 @@ export default defineComponent({
                 v-model:value={state.tabName}
                 onUpdate:value={(val: any) => setTabs(val)}
             >
-              <NTabPane
-                  name="管乐迷"
-                  tab="管乐迷"
-              >
-                <NFormItem
-                    label="曲目分类"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="是否收费"
-                    path="musicSheetType"
-                >
-                  <NSelect
-                      clearable
-                      options={
-                        [
-                          {
-                            label: '是',
-                            value: 1
-                          },
-                          {
-                            label: '否',
-                            value: 0
-                          }
-                        ] as any
-                      }
+              {state.selectApp.includes('MEC') &&
+                  <NTabPane
+                      tab="管乐迷"
+                      name="MEC"
                   >
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="排序值"
-                    path="musicSheetType"
-                >
-                  <NInput></NInput>
-                </NFormItem>
-              </NTabPane>
-              <NTabPane
-                  name="管乐团"
-                  tab="管乐团"
-              >
-                <NFormItem
-                    label="分类"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="排序值"
-                    path="musicSheetType"
-                >
-                  <NInput></NInput>
-                </NFormItem>
-              </NTabPane>
-
-              <NTabPane
-                  name="酷乐秀"
-                  tab="酷乐秀"
-              >
-                <NFormItem
-                    label="可用途径"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="曲目标签"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="是否收费"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="曲目价格"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="是否置顶"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="精品乐谱"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="排序值"
-                    path="musicSheetType"
-                >
-                  <NInput></NInput>
-                </NFormItem>
-              </NTabPane>
-
-              <NTabPane
-                  name="音乐数字课堂"
-                  tab="音乐数字课堂"
-              >
-                <NFormItem
-                    label="曲目分类"
-                    path="musicSheetType"
-                >
-                  <NSelect>
-                  </NSelect>
-                </NFormItem>
-                <NFormItem
-                    label="排序值"
-                    path="musicSheetType"
-                >
-                  <NInput></NInput>
-                </NFormItem>
-              </NTabPane>
+                      <NFormItem
+                          label="曲目分类"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="是否收费"
+                          path="musicSheetType"
+                      >
+                          <NSelect
+                              clearable
+                              options={
+                                [
+                                  {
+                                    label: '是',
+                                    value: 1
+                                  },
+                                  {
+                                    label: '否',
+                                    value: 0
+                                  }
+                                ] as any
+                              }
+                          >
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="排序值"
+                          path="musicSheetType"
+                      >
+                          <NInput></NInput>
+                      </NFormItem>
+                  </NTabPane>
+              }
+              {state.selectApp.includes('JMEDU') &&
+                  <NTabPane
+                      tab="管乐团"
+                      name="JMEDU"
+                  >
+                      <NFormItem
+                          label="分类"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="排序值"
+                          path="musicSheetType"
+                      >
+                          <NInput></NInput>
+                      </NFormItem>
+                  </NTabPane>
+              }
+              {state.selectApp.includes('COOLESHOW') &&
+                  <NTabPane
+                      tab="酷乐秀"
+                      name="COOLESHOW"
+                  >
+                      <NFormItem
+                          label="可用途径"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="曲目标签"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="是否收费"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="曲目价格"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="是否置顶"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="精品乐谱"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="排序值"
+                          path="musicSheetType"
+                      >
+                          <NInput></NInput>
+                      </NFormItem>
+                  </NTabPane>
+              }
+              {state.selectApp.includes('COOLESHOW_EDU') &&
+                  <NTabPane
+                      tab="音乐数字课堂"
+                      name="COOLESHOW_EDU"
+                  >
+                      <NFormItem
+                          label="曲目分类"
+                          path="musicSheetType"
+                      >
+                          <NSelect>
+                          </NSelect>
+                      </NFormItem>
+                      <NFormItem
+                          label="排序值"
+                          path="musicSheetType"
+                      >
+                          <NInput></NInput>
+                      </NFormItem>
+                  </NTabPane>
+              }
             </NTabs>
           </NForm>
           <NSpace justify="end">

+ 7 - 0
src/views/music-library/project-music-sheet/index.tsx

@@ -4,6 +4,7 @@ import { useRoute } from 'vue-router'
 import { getTabsCache, setTabsCaches } from '@/hooks/use-async'
 import Mec from "@views/music-library/project-music-sheet/module/mec/mec";
 import Jmedu from "@views/music-library/project-music-sheet/module/jmedu/jmedu";
+import CooleshowEdu from "@views/music-library/project-music-sheet/module/cooleshow-edu/cooleshow-edu";
 export default defineComponent({
   name: 'project-music-sheet',
   setup() {
@@ -47,6 +48,12 @@ export default defineComponent({
               >
                 <Jmedu></Jmedu>
               </NTabPane>
+              <NTabPane
+                  name="cooleshow-edu"
+                  tab="音乐数字课堂"
+              >
+                <CooleshowEdu></CooleshowEdu>
+              </NTabPane>
             </NTabs>
           </div>
         </div>

+ 240 - 0
src/views/music-library/project-music-sheet/module/cooleshow-edu/addMusic.tsx

@@ -0,0 +1,240 @@
+import {defineComponent, onMounted, reactive, ref} from "vue";
+import SaveForm from "@components/save-form";
+import {NButton, NDataTable, NDatePicker, NDescriptions, NDescriptionsItem, NFormItem, NImage, NInput, NModal, NSelect, NSpace, NStep, NSteps} from "naive-ui";
+import Pagination from "@components/pagination";
+import TheTooltip from "@components/TheTooltip";
+
+export default defineComponent({
+  name: 'project-music-sheet-mec',
+  setup(props, {slots, attrs, emit}) {
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      searchForm: {
+        keyword: null,
+        subjectType: null,
+        musicSubject: null,
+        status: null
+      },
+      subjectList: [],
+      showAdd: false,
+      currentStep: 1,
+      dataList: []
+    })
+
+    onMounted(() => {
+    })
+
+    const saveForm = ref()
+
+    const onSearch = () => {
+      saveForm.value?.submit()
+    }
+    const onBtnReset = () => {
+      saveForm.value?.reset()
+    }
+
+    const onSubmit = () => {
+
+    }
+
+    const columns = (): any => {
+      return [
+        {
+          title: '曲目信息',
+          key: 'id',
+          render: (row: any) => (
+              <>
+                <NDescriptions labelPlacement="left" column={1}>
+                  <NDescriptionsItem label="曲目名称">
+                    <TheTooltip content={row.musicSheetName}/>{' '}
+                  </NDescriptionsItem>
+                  <NDescriptionsItem label="曲目编号">{row.id}</NDescriptionsItem>
+                </NDescriptions>
+              </>
+          )
+        },
+        {
+          title: '封面图',
+          key: 'titleImg',
+          render(row: any) {
+            return <NImage width={60} height={60} src={row.titleImg}/>
+          }
+        },
+        {
+          title: '声部',
+          key: 'titleImg',
+        },
+        {
+          title: '曲目名称',
+          key: 'titleImg',
+        },
+        {
+          title: '音乐人',
+          key: 'titleImg',
+        },
+        {
+          title: '曲目类型',
+          key: 'titleImg',
+        },
+        {
+          title: '作者属性',
+          key: 'titleImg',
+        },
+        {
+          title: '所属人',
+          key: 'titleImg',
+        },
+        {
+          title: '操作',
+          key: 'operation',
+          fixed: 'right',
+          render(row: any) {
+            return (
+                <NSpace>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="musicSheet/status1612431726029942786"
+                      // onClick={() => onChangeStatus(row)}
+                  >
+                    {row.status ? '停用' : '启用'}
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="musicSheet/update1602302618558099458"
+                      onClick={() => {
+                        // state.visiableMusic = true
+                        // state.musicOperation = 'edit'
+                        // state.musicData = row
+                      }}
+                  >
+                    修改
+                  </NButton>
+                </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    const updateCurrent = (val: any) => {
+      state.currentStep = val
+    }
+
+    return () => {
+      return (
+          <div class="system-menu-container">
+            <NSpace vertical size="medium">
+              <NSteps current={state.currentStep} onUpdateCurrent={updateCurrent} style={"margin-bottom: 20px;"}>
+                <NStep
+                    title="选择分类"
+                    description=""
+                >
+                </NStep>
+                <NStep
+                    title="选择分类下曲目"
+                    description=""
+                >
+                </NStep>
+                <NStep
+                    title="设置曲目信息"
+                    description=""
+                >
+                </NStep>
+              </NSteps>
+            </NSpace>
+            {state.currentStep === 1 && (
+                <SaveForm>
+                  <NFormItem label="关键词" path="keyword">
+                    <NSelect></NSelect>
+                  </NFormItem>
+                </SaveForm>
+            )}
+            {state.currentStep === 2 && (
+                <div class="system-menu-container">
+                  <SaveForm>
+                    <NFormItem label="关键词" path="keyword">
+                      <NSelect></NSelect>
+                    </NFormItem>
+                    <NFormItem label="曲目类型" path="keyword">
+                      <NSelect></NSelect>
+                    </NFormItem>
+                    <NFormItem label="声部" path="keyword">
+                      <NSelect></NSelect>
+                    </NFormItem>
+                    <NFormItem label="曲目来源" path="keyword">
+                      <NSelect></NSelect>
+                    </NFormItem>
+                    <NFormItem>
+                      <NSpace>
+                        <NButton type="primary" onClick={onSearch}>
+                          搜索
+                        </NButton>
+                        <NButton type="default" onClick={onBtnReset}>
+                          重置
+                        </NButton>
+                      </NSpace>
+                    </NFormItem>
+                  </SaveForm>
+                  <NDataTable
+                      loading={state.loading}
+                      columns={columns()}
+                      data={state.dataList}
+                  ></NDataTable>
+                  <Pagination
+                      v-model:page={state.pagination.page}
+                      v-model:pageSize={state.pagination.rows}
+                      v-model:pageTotal={state.pagination.pageTotal}
+                      // onList={getList}
+                      sync
+                      saveKey="music-list"
+                  ></Pagination>
+                </div>
+            )}
+            {state.currentStep === 3 && (
+                <div class="system-menu-container" style={"margin-top: 15px;"}>
+                  <NDataTable
+                      loading={state.loading}
+                      columns={columns()}
+                      data={state.dataList}
+                  ></NDataTable>
+                </div>
+            )}
+            <NSpace justify="end">
+              <NButton type="default" onClick={() => {
+                if (state.currentStep > 1) {
+                  state.currentStep = state.currentStep - 1;
+                } else {
+                  emit('close')
+                }
+              }}>
+                {state.currentStep === 1 ? '取消' : '上一步'}
+              </NButton>
+              <NButton
+                  type="primary"
+                  onClick={() => {
+                    if (state.currentStep < 3) {
+                      state.currentStep = state.currentStep + 1;
+                    } else {
+                      onSubmit()
+                    }
+                  }}
+                  // loading={btnLoading.value}
+                  // disabled={btnLoading.value}
+              >
+                {state.currentStep === 3 ? '确定' : '下一步'}
+              </NButton>
+            </NSpace>
+          </div>
+      )
+    }
+  }
+})

+ 349 - 0
src/views/music-library/project-music-sheet/module/cooleshow-edu/cooleshow-edu.tsx

@@ -0,0 +1,349 @@
+import {defineComponent, onMounted, reactive, ref} from "vue";
+import SaveForm from "@components/save-form";
+import {NButton, NDataTable, NDatePicker, NDescriptions, NDescriptionsItem, NFormItem, NImage, NInput, NModal, NSelect, NSpace} from "naive-ui";
+import Pagination from "@components/pagination";
+import TheTooltip from "@components/TheTooltip";
+import AddMusic from "@views/music-library/project-music-sheet/module/jmedu/addMusic";
+
+export default defineComponent({
+  name: 'project-music-sheet-jmedu',
+
+  setup() {
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      searchForm: {
+        keyword: null,
+        subjectType: null,
+        musicSubject: null,
+        status: null
+      },
+      subjectList: [],
+      showAdd: false
+    })
+
+    onMounted(() => {
+    })
+
+    const saveForm = ref()
+
+    const onSearch = () => {
+      saveForm.value?.submit()
+    }
+    const onBtnReset = () => {
+      saveForm.value?.reset()
+    }
+
+    const onSubmit = () => {
+
+    }
+
+    const columns = (): any => {
+      return [
+        {
+          title: '曲目信息',
+          key: 'id',
+          render: (row: any) => (
+              <>
+                <NDescriptions labelPlacement="left" column={1}>
+                  <NDescriptionsItem label="曲目名称">
+                    <TheTooltip content={row.musicSheetName}/>{' '}
+                  </NDescriptionsItem>
+                  <NDescriptionsItem label="曲目编号">{row.id}</NDescriptionsItem>
+                </NDescriptions>
+              </>
+          )
+        },
+        {
+          title: '封面图',
+          key: 'titleImg',
+          render(row: any) {
+            return <NImage width={60} height={60} src={row.titleImg}/>
+          }
+        },
+        {
+          title: '曲目来源',
+          key: 'musicSheetCategoriesName',
+          render: (row: any) => (
+              <>
+                <NDescriptions labelPlacement="left" column={1}>
+                  <NDescriptionsItem label="曲目来源">
+                    <TheTooltip content={row.musicSheetCategoriesName}/>{' '}
+                  </NDescriptionsItem>
+                  <NDescriptionsItem label="所属人">
+                    {row.accompanimentType === 'HOMEMODE' ? '自制伴奏' : '普通伴奏'}
+                  </NDescriptionsItem>
+                </NDescriptions>
+              </>
+          )
+        },
+        {
+          title: '曲目类型',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '可用声部',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '分类',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '是否收费',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '上传人',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '上传时间',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '状态',
+          key: 'musicSheetCategoriesName'
+        },
+        {
+          title: '操作',
+          key: 'operation',
+          fixed: 'right',
+          render(row: any) {
+            return (
+                <NSpace>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="musicSheet/status1612431726029942786"
+                      // onClick={() => onChangeStatus(row)}
+                  >
+                    {row.status ? '停用' : '启用'}
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="musicSheet/update1602302618558099458"
+                      onClick={() => {
+                        // state.visiableMusic = true
+                        // state.musicOperation = 'edit'
+                        // state.musicData = row
+                      }}
+                  >
+                    修改
+                  </NButton>
+                </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    return () => {
+      return (
+          <div class="system-menu-container">
+            <SaveForm
+                ref={saveForm}
+                model={state.searchForm}
+                onSubmit={onSubmit}
+                saveKey="music-list"
+                onSetModel={(val: any) => (state.searchForm = val)}
+            >
+
+              <NFormItem label="关键词" path="keyword">
+                <NInput
+                    placeholder="曲目名称/编号"
+                    v-model:value={state.searchForm.keyword}
+                    clearable
+                />
+              </NFormItem>
+
+              <NFormItem label="曲目类型" path="subjectType">
+                <NSelect
+                    placeholder="请选择曲目类型"
+                    v-model:value={state.searchForm.subjectType}
+                    options={
+                      [
+                        {
+                          label: '独奏',
+                          value: '1'
+                        },
+                        {
+                          label: '合奏',
+                          value: '2'
+                        }
+                      ]
+                    }
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem label="伴奏类型" path="musicSubject">
+                <NSelect
+                />
+              </NFormItem>
+              <NFormItem label="声部" path="musicSubject">
+                <NSelect
+                    placeholder="请选择声部"
+                    v-model:value={state.searchForm.musicSubject}
+                    options={state.subjectList}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem label="状态" path="status">
+                <NSelect
+                    v-model={[state.searchForm.status, 'value']}
+                    placeholder="请选择状态"
+                    clearable
+                    options={
+                      [
+                        {
+                          label: '启用',
+                          value: true
+                        },
+                        {
+                          label: '停用',
+                          value: false
+                        }
+                      ] as any
+                    }
+                />
+              </NFormItem>
+              <NFormItem label="曲目来源" path="authorFrom">
+                <NSelect
+                    placeholder="请选择曲目来源"
+                    // v-model:value={state.searchForm.authorFrom}
+                    options={
+                      [
+                        {
+                          label: '平台',
+                          value: 'PLATFORM'
+                        },
+                        {
+                          label: '机构',
+                          value: 'TENANT'
+                        },
+                        {
+                          label: '个人',
+                          value: 'SELF'
+                        }
+                      ] as any
+                    }
+                    clearable
+                />
+              </NFormItem>
+
+              <NFormItem label="曲目类型" path="authorFrom">
+                <NSelect
+                    placeholder="请选择收费类型"
+                    // v-model:value={state.searchForm.authorFrom}
+                    options={
+                      [
+                        {
+                          label: '独奏',
+                          value: 'PLATFORM'
+                        },
+                        {
+                          label: '合奏',
+                          value: 'TENANT'
+                        }
+                      ] as any
+                    }
+                    clearable
+                />
+              </NFormItem>
+
+              <NFormItem label="所属人" path="authorFrom">
+                <NInput></NInput>
+              </NFormItem>
+              <NFormItem label="曲目分类" path="authorFrom">
+                <NSelect></NSelect>
+              </NFormItem>
+              <NFormItem label="上传时间" path="authorFrom">
+                <NDatePicker
+                    // v-model:value={state.searchForm.times}
+                    type="daterange"
+                    clearable
+                    value-format="yyyy.MM.dd"
+                    startPlaceholder="开始时间"
+                    endPlaceholder="结束时间"
+                />
+              </NFormItem>
+              <NFormItem>
+                <NSpace>
+                  <NButton type="primary" onClick={onSearch}>
+                    搜索
+                  </NButton>
+                  <NButton type="default" onClick={onBtnReset}>
+                    重置
+                  </NButton>
+                </NSpace>
+              </NFormItem>
+            </SaveForm>
+
+            <div class={['section-container']}>
+              <NSpace style={{paddingBottom: '12px'}}>
+                <NButton
+                    type="primary"
+                    //v-auth="musicSheet/save1602302550719426561"
+                    onClick={() => {
+                      state.showAdd = true
+                    }}
+                >
+                  新增
+                </NButton>
+                <NButton
+                    //v-auth="musicSheet/save1602302550719426561"
+                    onClick={() => {
+                      // state.musicOperation = 'unable'
+                      // state.musicData = {}
+                    }}
+                >
+                  批量禁用
+                </NButton>
+                <NButton
+                    //v-auth="musicSheet/save1602302550719426561"
+                    onClick={() => {
+                      // state.musicOperation = 'enable'
+                      // state.musicData = {}
+                    }}
+                >
+                  批量启用
+                </NButton>
+              </NSpace>
+
+              <NDataTable
+                  loading={state.loading}
+                  columns={columns()}
+                  // data={state.dataList}
+              ></NDataTable>
+
+              <Pagination
+                  v-model:page={state.pagination.page}
+                  v-model:pageSize={state.pagination.rows}
+                  v-model:pageTotal={state.pagination.pageTotal}
+                  // onList={getList}
+                  sync
+                  saveKey="music-list"
+              ></Pagination>
+            </div>
+
+            <NModal
+                v-model:show={state.showAdd}
+                preset="dialog"
+                showIcon={false}
+                title={'添加曲目'}
+                style={{width: '1200px',height: '800px'}}
+            >
+              <AddMusic/>
+            </NModal>
+          </div>
+      )
+    }
+  }
+})

+ 133 - 0
src/views/system-manage/subject-manage/api.ts

@@ -0,0 +1,133 @@
+import request from '@/utils/request/index'
+
+/**
+ * 乐器列表
+ */
+export const musicalInstrumentPage = (data: object) => {
+  return request({
+    url: '/cbs-app/musicalInstrument/page',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 乐器新增
+ */
+export const musicalInstrumentSave = (data: object) => {
+  return request({
+    url: '/cbs-app/musicalInstrument/save',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 乐器修改
+ */
+export const musicalInstrumentUpdate = (data: object) => {
+  return request({
+    url: '/cbs-app/musicalInstrument/update',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 乐器修改状态
+ */
+export const musicalInstrumentUpdateStatus = (data?: any) => {
+  return request({
+    url: '/cbs-app/musicalInstrument/enable?id=' + data.id,
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部分类列表
+ */
+export const subjectCategoryPage = (data: object) => {
+  return request({
+    url: '/cbs-app/subjectCategory/page',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部分类新增
+ */
+export const subjectCategorySave = (data: object) => {
+  return request({
+    url: '/cbs-app/subjectCategory/save',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部分类修改
+ */
+export const subjectCategoryUpdate = (data: object) => {
+  return request({
+    url: '/cbs-app/subjectCategory/update',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部分类修改状态
+ */
+export const subjectCategoryUpdateStatus = (data?: any) => {
+  return request({
+    url: '/cbs-app/subjectCategory/enable?id=' + data.id,
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部列表
+ */
+export const subjectPage = (data: object) => {
+  return request({
+    url: '/cbs-app/subject/page',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部新增
+ */
+export const subjectSave = (data: object) => {
+  return request({
+    url: '/cbs-app/subject/save',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部修改
+ */
+export const subjectUpdate = (data: object) => {
+  return request({
+    url: '/cbs-app/subject/update',
+    method: 'post',
+    data
+  } as any)
+}
+
+/**
+ * 声部修改状态
+ */
+export const subjectUpdateStatus = (data?: any) => {
+  return request({
+    url: '/cbs-app/subject/enable?id=' + data.id,
+    method: 'post',
+    data
+  } as any)
+}

+ 8 - 0
src/views/system-manage/subject-manage/index.module.less

@@ -0,0 +1,8 @@
+.datepicker {
+  width: 240px !important;
+  :global {
+    .n-input {
+      width: auto !important;
+    }
+  }
+}

+ 56 - 0
src/views/system-manage/subject-manage/index.tsx

@@ -0,0 +1,56 @@
+import {NTabPane, NTabs} from 'naive-ui'
+import {defineComponent, reactive} from 'vue'
+import {useRoute} from 'vue-router'
+import {getTabsCache, setTabsCaches} from '@/hooks/use-async'
+import SubjectList from "@views/system-manage/subject-manage/subject/subject-list";
+import CategorizeList from "@views/system-manage/subject-manage/subject-categorize/categorize-list";
+
+export default defineComponent({
+  name: 'subject-manage',
+  setup() {
+    const state = reactive({
+      tabName: 'Subject' as 'Subject' | 'Categorize'
+    })
+    const route = useRoute()
+    getTabsCache((val: any) => {
+      if (val.form.tabName) {
+        state.tabName = val.form.tabName
+      }
+    })
+    const setTabs = (val: any) => {
+      setTabsCaches(val, 'tabName', route)
+    }
+    return () => {
+      // const Component = resolveDynamicComponent(state.componentName)
+      return (
+          <div class="system-menu-container">
+            <h2>声部管理</h2>
+
+            <div class={['section-container']} style="padding-top: 0">
+              <NTabs
+                  type="line"
+                  size="large"
+                  v-model:value={state.tabName}
+                  onUpdate:value={(val: any) => setTabs(val)}
+              >
+                <NTabPane
+                    name="Subject"
+                    tab="声部列表"
+                    //v-auth="sysUserLoginLog/page1597883965544714242"
+                >
+                  <SubjectList />
+                </NTabPane>
+                <NTabPane
+                    name="Categorize"
+                    tab="声部分类"
+                    //v-auth="sysUserLogin/page1597884090153291778"
+                >
+                  <CategorizeList />
+                </NTabPane>
+              </NTabs>
+            </div>
+          </div>
+      )
+    }
+  }
+})

+ 286 - 0
src/views/system-manage/subject-manage/instrument/instrument-list.tsx

@@ -0,0 +1,286 @@
+import Pagination from '@/components/pagination'
+import {NButton, NDataTable, NDatePicker, NFormItem, NInput, NModal, NSelect, NSpace, NTag, useDialog, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, reactive, ref} from 'vue'
+import SaveForm from "@components/save-form";
+import {musicalInstrumentPage, musicalInstrumentUpdateStatus} from "@views/system-manage/subject-manage/api";
+import {getMapValueByKey} from "@/utils/filters";
+import {defaultScore} from "@/utils/constant";
+import {getSelectDataFromObj} from "@/utils/objectUtil";
+import InstrumentSave from "@views/system-manage/subject-manage/instrument/modal/instrument-save";
+import {filterTimes} from "@/utils/dateUtil";
+
+export default defineComponent({
+  name: 'instrument-list',
+  setup() {
+    const dialog = useDialog()
+    const message = useMessage()
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      searchForm: {
+        keyword: '',
+        timer: null,
+        startTime: null,
+        endTime: null,
+        defaultScore: null, //默认谱面
+        code: null, //编码
+        operatorKeyword: null, //操作人
+        hz: null, //评测频率
+      },
+      dataList: [] as any,
+      showSave: false,
+      saveMode: 'add',
+      rowData: {},
+    })
+
+    const saveForm = ref()
+
+    const onSubmit = () => {
+      getList()
+    }
+    const onSearch = () => {
+      saveForm.value?.submit()
+    }
+    const onChangeStatus = (row: any) => {
+      const statusStr = row.enableFlag ? '停用' : '启用'
+      dialog.warning({
+        title: '警告',
+        content: `是否${statusStr}?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          try {
+            await musicalInstrumentUpdateStatus({
+              id: row.id
+            })
+            getList()
+            message.success(`${statusStr}成功`)
+          } catch {
+          }
+        }
+      })
+    }
+    const onBtnReset = () => {
+      saveForm.value?.reset()
+    }
+
+    const columns = () => {
+      return [
+        {
+          title: '编号',
+          key: 'id'
+        },
+        {
+          title: '乐器名称',
+          key: 'name'
+        },
+        {
+          title: '乐器编码',
+          key: 'code'
+        },
+        {
+          title: '评测频率',
+          key: 'hz'
+        },
+        {
+          title: '声部分类',
+          key: 'subjectName'
+        },
+        {
+          title: '默认谱面',
+          key: 'defaultScore',
+          render(row: any) {
+            return getMapValueByKey(row.defaultScore, new Map(Object.entries(defaultScore)));
+          }
+        },
+        {
+          title: '操作人',
+          key: 'operator',
+          render(row: any) {
+            return (
+                <div>
+                  <div>{row.operatorName}</div>
+                  <div>{row.updateTime}</div>
+                </div>
+
+            )
+          }
+        },
+        {
+          title: '状态',
+          key: 'enableFlag',
+          render(row: any) {
+            return (
+                <NTag type={row.enableFlag ? 'primary' : 'default'}>{row.enableFlag ? '启用' : '停用'}</NTag>
+            )
+          }
+        },
+        {
+          title: '操作',
+          key: 'operation',
+          render(row: any) {
+            return (
+                <NSpace>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="cityFeeSetting/update1597887290159779842"
+                      onClick={() => {
+                        state.rowData = row
+                        state.showSave = true
+                        state.saveMode = 'edit'
+                      }}
+                  >
+                    修改
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="sysNotice/status1599959101026455553"
+                      onClick={() => onChangeStatus(row)}
+                  >
+                    {row.enableFlag ? '停用' : '启用'}
+                  </NButton>
+                </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    const getList = async () => {
+      try {
+        state.loading = true
+        const {data} = await musicalInstrumentPage({
+          ...state.pagination, ...state.searchForm,
+          ...filterTimes(state.searchForm.timer, ['startTime', 'endTime']),
+
+        })
+        state.loading = false
+        state.pagination.pageTotal = Number(data.total)
+        state.dataList = data.rows || []
+      } catch {
+        state.loading = false
+      }
+    }
+
+    onMounted(() => {
+      getList()
+    })
+    return () => (
+        <div class="system-menu-container">
+          <h2>乐器管理</h2>
+          <div class={['section-container']}>
+            <SaveForm
+                ref={saveForm}
+                model={state.searchForm}
+                onSubmit={onSubmit}
+                onSetModel={(val: any) => Object.assign(state.searchForm, val)}
+                saveKey="instrument-list"
+            >
+
+              <NFormItem path="keyword" label="关键字">
+                <NInput
+                    placeholder="请输入编号/名称"
+                    v-model:value={state.searchForm.keyword}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="rate" label="评测频率">
+                <NInput
+                    v-model:value={state.searchForm.hz}
+                    placeholder="请输入乐器编码"
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="defaultScore" label="默认谱面">
+                <NSelect
+                    options={getSelectDataFromObj(defaultScore)}
+                    v-model:value={state.searchForm.defaultScore}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="code" label="乐器编码">
+                <NInput
+                    v-model:value={state.searchForm.code}
+                    placeholder="请输入乐器编码"
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="operatorName" label="操作人">
+                <NInput
+                    v-model:value={state.searchForm.operatorKeyword}
+                    placeholder="请输入操作人"
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="updateTime" label="操作时间">
+                <NDatePicker v-model:value={state.searchForm.timer} type="daterange" clearable/>
+              </NFormItem>
+
+              <NFormItem>
+                <NSpace>
+                  <NButton type="primary" onClick={onSearch}>
+                    搜索
+                  </NButton>
+                  <NButton type="default" onClick={onBtnReset}>
+                    重置
+                  </NButton>
+                </NSpace>
+              </NFormItem>
+
+            </SaveForm>
+            <NSpace style={{paddingBottom: '12px'}}>
+              <NButton
+                  type="primary"
+                  //v-auth="musicSheetCategories/save1608067543331045378"
+                  onClick={() => {
+                    state.rowData = {}
+                    state.showSave = true
+                    state.saveMode = 'add'
+                  }}
+              >
+                新增乐器
+              </NButton>
+            </NSpace>
+
+            <NDataTable
+                loading={state.loading}
+                columns={columns()}
+                data={state.dataList}
+                rowKey={(row: any) => row.id}
+            ></NDataTable
+            >
+            <Pagination
+                v-model:page={state.pagination.page}
+                v-model:pageSize={state.pagination.rows}
+                v-model:pageTotal={state.pagination.pageTotal}
+                onList={getList}
+                sync
+                saveKey="login-device"
+            ></Pagination>
+          </div>
+          <NModal
+              v-model:show={state.showSave}
+              preset="dialog"
+              showIcon={false}
+              title={state.saveMode === 'add' ? '新增乐器' : '修改乐器'}
+              style={{width: '500px'}}
+          >
+            <InstrumentSave
+                type={state.saveMode}
+                data={state.rowData}
+                onClose={() => (state.showSave = false)}
+                onGetList={getList}
+            />
+          </NModal>
+        </div>
+    )
+  }
+})

+ 149 - 0
src/views/system-manage/subject-manage/instrument/modal/instrument-save.tsx

@@ -0,0 +1,149 @@
+import {NButton, NForm, NFormItem, NInput, NSelect, NSpace, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
+import {getSelectDataFromObj} from "@/utils/objectUtil";
+import {defaultScore} from "@/utils/constant";
+import {musicalInstrumentSave, musicalInstrumentUpdate} from "@views/system-manage/subject-manage/api";
+
+export default defineComponent({
+  name: 'role-operation',
+  props: {
+    type: {
+      type: String,
+      default: 'add'
+    },
+    applyList: {
+      type: Array as PropType<any>,
+      default: () => []
+    },
+    data: {
+      type: Object as PropType<any>,
+      default: () => {
+      }
+    }
+  },
+  emits: ['close', 'getList'],
+  setup(props, {slots, attrs, emit}) {
+    const forms = reactive({
+      name: null,
+      defaultScore: null,
+      code: null,
+      hz: null,
+    })
+    const btnLoading = ref(false)
+    const formsRef = ref()
+    const message = useMessage()
+
+    const onSubmit = async () => {
+      formsRef.value.validate(async (error: any) => {
+        if (error) return false
+        try {
+          btnLoading.value = true
+
+          if (props.type === 'add') {
+            await musicalInstrumentSave({...forms})
+            message.success('添加成功')
+          } else if (props.type === 'edit') {
+            await musicalInstrumentUpdate({
+              ...forms,
+              id: props.data.id
+            })
+            message.success('修改成功')
+          }
+
+          emit('close')
+          emit('getList')
+        } catch {
+        }
+        btnLoading.value = false
+      })
+    }
+
+    onMounted(async () => {
+      if (props.type === 'edit') {
+        const data = props.data
+        forms.name = data.name
+        forms.defaultScore = data.defaultScore
+        forms.code = data.code
+        forms.hz = data.hz
+      }
+    })
+
+    return () => (
+        <div style="background: #fff; padding-top: 12px">
+          <NForm model={forms} ref={formsRef} label-placement="left" label-width="auto">
+            <NFormItem
+                label="乐器名称"
+                path="name"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入声部名称'
+                  }
+                ]}
+            >
+              <NInput
+                  v-model:value={forms.name}
+                  placeholder="请输入声部名称"
+                  clearable
+                  maxlength={10}
+              ></NInput>
+            </NFormItem>
+            <NFormItem
+                label="默认谱面"
+                path="defaultScore"
+                rule={[
+                  {
+                    required: true,
+                    message: '请选择默认谱面'
+                  }
+                ]}
+            >
+              <NSelect
+                  clearable
+                  v-model:value={forms.defaultScore}
+                  placeholder="请选择默认谱面"
+                  options={getSelectDataFromObj(defaultScore)}
+              />
+            </NFormItem>
+            <NFormItem
+                label="乐器编码"
+                path="code"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入乐器编码'
+                  }
+                ]}
+            >
+              <NInput v-model:value={forms.code} placeholder="请输入乐器编码" clearable></NInput>
+            </NFormItem>
+            <NFormItem
+                label="评测频率"
+                path="hz"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入评测频率'
+                  }
+                ]}
+            >
+              <NInput
+                  v-model:value={forms.hz}
+                  placeholder="请输入评测频率"
+                  clearable
+              ></NInput>
+            </NFormItem>
+          </NForm>
+
+          <NSpace justify="end">
+            <NButton type="default" onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton type="primary" onClick={() => onSubmit()} loading={btnLoading.value}>
+              保存
+            </NButton>
+          </NSpace>
+        </div>
+    )
+  }
+})

+ 194 - 0
src/views/system-manage/subject-manage/subject-categorize/categorize-list.tsx

@@ -0,0 +1,194 @@
+import {NButton, NDataTable, NModal, NSpace, NTag, useDialog, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, reactive} from 'vue'
+import CategorizeSave from "@views/system-manage/subject-manage/subject-categorize/modal/categorize-save";
+import {subjectCategoryPage, subjectCategoryUpdateStatus} from "@views/system-manage/subject-manage/api";
+import {getTimes} from "@/utils/dateUtil";
+import Pagination from "@components/pagination";
+
+export default defineComponent({
+  name: 'category-list',
+  setup() {
+    const dialog = useDialog()
+    const message = useMessage()
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      dataList: [] as any,
+      saveMode: 'add',
+      showSaveDialog: false,
+      rowData: {},
+    })
+    const searchForm = reactive({
+      keyword: '',
+      times: null as any,
+      operatorKeyword: '' //创建人
+    })
+
+    const columns = (): any => {
+      return [
+        {
+          title: '分类编号',
+          key: 'id'
+        },
+        {
+          title: '分类名称',
+          key: 'name',
+        },
+        {
+          title: '操作人',
+          key: 'operator',
+          render(row: any) {
+            return (
+                <div>
+                  <div>{row.operatorName}</div>
+                  <div>{row.updateTime}</div>
+                </div>
+            )
+          }
+        },
+        {
+          title: '状态',
+          key: 'enableFlag',
+          render(row: any) {
+            return (
+                <NTag type={row.enableFlag ? 'primary' : 'default'}>{row.enableFlag ? '启用' : '停用'}</NTag>
+            )
+          }
+        },
+        {
+          title: '操作',
+          key: 'operation',
+          fixed: 'right',
+          width: 180,
+          render(row: any) {
+            return (
+                <NSpace>
+                  <NButton
+                      type="primary"
+                      text
+                      //v-auth="materialCategory/update1599962354053140482"
+                      onClick={() => {
+                        state.saveMode = 'edit'
+                        state.showSaveDialog = true
+                        state.rowData = row
+                      }}
+                  >
+                    修改
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="sysNotice/status1599959101026455553"
+                      onClick={() => onChangeStatus(row)}
+                  >
+                    {row.enableFlag ? '停用' : '启用'}
+                  </NButton>
+                </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    const getList = async () => {
+      try {
+        state.loading = true
+        const {times, ...reset} = searchForm
+        const body = {
+          ...reset,
+          ...getTimes(times, ['startTime', 'endTime']),
+          page: state.pagination.page,
+          rows: state.pagination.rows
+        }
+        const {data} = await subjectCategoryPage(body)
+        state.loading = false
+        state.pagination.pageTotal = Number(data.total)
+        state.dataList = data.rows || []
+      } catch {
+        state.loading = false
+      }
+    }
+
+    const onChangeStatus = (row: any) => {
+      const statusStr = row.enableFlag ? '停用' : '启用'
+      dialog.warning({
+        title: '警告',
+        content: `是否${statusStr}?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          try {
+            await subjectCategoryUpdateStatus({
+              id: row.id
+            })
+            getList()
+            message.success(`${statusStr}成功`)
+          } catch {
+          }
+        }
+      })
+    }
+
+    onMounted(() => {
+      getList()
+    })
+
+    return () => (
+        <div class="system-menu-container">
+          <div class={['section-container']}>
+            <NSpace style={{paddingBottom: '12px'}}>
+              <NButton
+                  type="primary"
+                  //v-auth="materialCategory/save1599962104022290433"
+                  onClick={() => {
+                    state.saveMode = 'add'
+                    state.showSaveDialog = true
+                  }}
+                  disabled={state.loading}
+              >
+                添加
+              </NButton>
+            </NSpace>
+            <NDataTable
+                scroll-x="1300"
+                loading={state.loading}
+                columns={columns()}
+                data={state.dataList}
+                children-key="subMaterialCategoryList"
+                default-expand-all={false}
+                row-key={(row: any) => row.id}
+            ></NDataTable>
+            <Pagination
+                v-model:page={state.pagination.page}
+                v-model:pageSize={state.pagination.rows}
+                v-model:pageTotal={state.pagination.pageTotal}
+                onList={getList}
+                sync
+                saveKey="categorize-list"
+            ></Pagination>
+          </div>
+
+          <NModal
+              v-model:show={state.showSaveDialog}
+              preset="dialog"
+              showIcon={false}
+              title={state.saveMode === 'add' ? '新增分类' : '修改分类'}
+              style={{width: '400px'}}
+          >
+            <CategorizeSave
+                type={state.saveMode}
+                data={state.rowData}
+                onClose={() => (state.showSaveDialog = false)}
+                onGetList={getList}
+            />
+
+          </NModal>
+        </div>
+    )
+  }
+})

+ 103 - 0
src/views/system-manage/subject-manage/subject-categorize/modal/categorize-save.tsx

@@ -0,0 +1,103 @@
+import {NButton, NForm, NFormItem, NInput, NSpace, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
+import {subjectCategorySave, subjectCategoryUpdate} from "@views/system-manage/subject-manage/api";
+
+export default defineComponent({
+  name: 'role-operation',
+  props: {
+    type: {
+      type: String,
+      default: 'add'
+    },
+    applyList: {
+      type: Array as PropType<any>,
+      default: () => []
+    },
+    data: {
+      type: Object as PropType<any>,
+      default: () => {
+      }
+    }
+  },
+  emits: ['close', 'getList'],
+  setup(props, {slots, attrs, emit}) {
+    const forms = reactive({
+      name: null,
+      defaultScore: null,
+      code: null,
+      img: null,
+      hz: null,
+      parentId: 0
+    })
+    const btnLoading = ref(false)
+    const formsRef = ref()
+    const message = useMessage()
+
+    const onSubmit = async () => {
+      formsRef.value.validate(async (error: any) => {
+        if (error) return false
+        try {
+          btnLoading.value = true
+
+          if (props.type === 'add') {
+            await subjectCategorySave({...forms})
+            message.success('添加成功')
+          } else if (props.type === 'edit') {
+            await subjectCategoryUpdate({
+              ...forms,
+              id: props.data.id
+            })
+            message.success('修改成功')
+          }
+
+          emit('close')
+          emit('getList')
+        } catch {
+        }
+        btnLoading.value = false
+      })
+    }
+
+    onMounted(async () => {
+      if (props.type === 'edit') {
+        const data = props.data
+        forms.img = data.img
+        forms.name = data.name
+        forms.code = data.code
+      }
+    })
+
+    return () => (
+        <div style="background: #fff; padding-top: 12px">
+          <NForm model={forms} ref={formsRef} label-placement="left" label-width="auto">
+            <NFormItem
+                label="分类名称"
+                path="name"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入分类名称'
+                  }
+                ]}
+            >
+              <NInput
+                  v-model:value={forms.name}
+                  placeholder="请输入声部名称"
+                  clearable
+                  maxlength={10}
+              ></NInput>
+            </NFormItem>
+          </NForm>
+
+          <NSpace justify="end">
+            <NButton type="default" onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton type="primary" onClick={() => onSubmit()} loading={btnLoading.value}>
+              保存
+            </NButton>
+          </NSpace>
+        </div>
+    )
+  }
+})

+ 192 - 0
src/views/system-manage/subject-manage/subject/modal/subject-save.tsx

@@ -0,0 +1,192 @@
+import {NButton, NForm, NFormItem, NInput, NSelect, NSpace, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
+import {subjectUpdate, subjectSave} from "@views/system-manage/subject-manage/api";
+import UploadFile from "@components/upload-file";
+
+export default defineComponent({
+  name: 'role-operation',
+  props: {
+    type: {
+      type: String,
+      default: 'add'
+    },
+    data: {
+      type: Object as PropType<any>,
+      default: () => {
+      }
+    },
+    categoryList: {
+      type: Object as PropType<any>,
+      default: () => []
+    },
+    instrumentList: {
+      type: Object as PropType<any>,
+      default: () => []
+    }
+  },
+  emits: ['close', 'getList'],
+  setup(props, {slots, attrs, emit}) {
+    const state = reactive({
+      forms: {
+        categoryId: null,
+        name: null,
+        img: null,
+        musicalInstrumentIds: [],
+      },
+      rowData: {},
+      categoryList: [],
+      instrumentList: [],
+    })
+    const btnLoading = ref(false)
+    const formsRef = ref()
+    const message = useMessage()
+
+    const onSubmit = async () => {
+      formsRef.value.validate(async (error: any) => {
+        if (error) return false
+        try {
+          btnLoading.value = true
+
+          if (props.type === 'add') {
+            const params: any = {
+              ...state.forms,
+              musicalInstrumentIds: state.forms.musicalInstrumentIds?.join(',') || ''
+            }
+            await subjectSave(params)
+            message.success('添加成功')
+          } else if (props.type === 'edit') {
+            const params: any = {
+              ...state.forms,
+              musicalInstrumentIds: state.forms.musicalInstrumentIds?.join(',') || '',
+              id: props.data.id
+            }
+            await subjectUpdate(params)
+            message.success('修改成功')
+          }
+
+          emit('close')
+          emit('getList')
+        } catch {
+        }
+        btnLoading.value = false
+      })
+    }
+
+    onMounted(async () => {
+      if (props.type === 'edit') {
+        const data = props.data
+        state.forms.categoryId = data.categoryId
+        state.forms.name = data.name
+        state.forms.img = data.img
+        state.forms.musicalInstrumentIds = data.musicalInstrumentIds?.split(',') || [];
+      }
+      console.log("pca", props.categoryList)
+      props.categoryList.forEach((next: any) => {
+        next.disabled = !next.enableFlag
+      })
+      props.instrumentList.forEach((next: any) => {
+        next.disabled = !next.enableFlag
+      })
+    })
+
+    return () => (
+        <div style="background: #fff; padding-top: 12px">
+          <NForm
+              model={state.forms}
+              ref={formsRef}
+              label-placement="left"
+              label-width="auto">
+            <NFormItem
+                label="声部分类"
+                path="categoryId"
+                rule={[
+                  {
+                    required: true,
+                    message: '请选择声部分类'
+                  }
+                ]}
+            >
+              <NSelect
+                  clearable
+                  v-model:value={state.forms.categoryId}
+                  placeholder="请选择声部分类"
+                  options={props.categoryList as any}
+                  filterable
+              />
+            </NFormItem>
+            <NFormItem
+                label="声部名称"
+                path="name"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入声部名称'
+                  }
+                ]}
+            >
+              <NInput
+                  v-model:value={state.forms.name}
+                  placeholder="请输入声部名称"
+                  clearable
+                  maxlength={10}
+              ></NInput>
+            </NFormItem>
+            <NFormItem
+                label="声部图片"
+                path="img"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入声部图片'
+                  }
+                ]}
+            >
+              <UploadFile
+                  accept=".jpg,.jpeg,.png"
+                  tips="请上传大小1M以内的JPG、PNG图片"
+                  v-model:fileList={state.forms.img}
+                  cropper
+                  bucketName="cbs"
+                  options={{
+                    autoCrop: true, //是否默认生成截图框
+                    enlarge: 2, //  图片放大倍数
+                    autoCropWidth: 200, //默框高度
+                    fixedBox: true, //是否固定截图框大认生成截图框宽度
+                    autoCropHeight: 200, //默认生成截图小 不允许改变
+                    previewsCircle: false, //预览图是否是原圆形
+                    title: '声部图片'
+                  }}
+              />
+            </NFormItem>
+            <NFormItem
+                label="关联乐器"
+                path="musicalInstrumentIds"
+                rule={[
+                  {
+                    required: true,
+                    message: '请选择关联乐器'
+                  }
+                ]}
+            >
+              <NSelect
+                  multiple
+                  clearable
+                  v-model:value={state.forms.musicalInstrumentIds}
+                  placeholder="请选择关联乐器"
+                  options={props.instrumentList as any}
+              />
+            </NFormItem>
+          </NForm>
+
+          <NSpace justify="end">
+            <NButton type="default" onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton type="primary" onClick={() => onSubmit()} loading={btnLoading.value}>
+              保存
+            </NButton>
+          </NSpace>
+        </div>
+    )
+  }
+})

+ 327 - 0
src/views/system-manage/subject-manage/subject/subject-list.tsx

@@ -0,0 +1,327 @@
+import SaveForm from '@/components/save-form'
+import {DataTableColumn, NButton, NDataTable, NDatePicker, NFormItem, NImage, NInput, NModal, NSelect, NSpace, useDialog, useMessage} from 'naive-ui'
+import {defineComponent, onMounted, reactive, ref} from 'vue'
+import styles from '../index.module.less'
+import {musicalInstrumentPage, subjectCategoryPage, subjectPage, subjectUpdateStatus} from "@views/system-manage/subject-manage/api";
+import {filterTimes} from "@/utils/dateUtil";
+import SubjectSave from "@views/system-manage/subject-manage/subject/modal/subject-save";
+import Pagination from "@components/pagination";
+import deepClone from "@/utils/deep.clone";
+
+export default defineComponent({
+  name: 'subject-list',
+  setup() {
+    const dialog = useDialog()
+    const message = useMessage()
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      dataList: [] as any,
+      saveMode: 'add',
+      showSaveDialog: false,
+      rowData: {},
+      categorizeList: [] as any,
+      instrumentList: [] as any,
+    })
+    const searchForm = reactive({
+      keyword: '',
+      times: null as any,
+      operatorKeyword: '', //创建人
+      categoryId: '',//声部分类
+      musicalInstrumentId: '',//关联乐器
+    })
+
+    const columns = (): DataTableColumn[] => {
+      return [
+        {
+          title: '编号',
+          key: 'id'
+        },
+        {
+          title: '声部名称',
+          key: 'name',
+        },
+        {
+          title: '声部分类',
+          key: 'categoryName'
+        },
+        {
+          title: '图片',
+          key: 'img',
+          render(row: any) {
+            return <NImage width={70} src={row.img}/>
+          }
+        },
+        {
+          title: '关联乐器',
+          key: 'musicalInstrumentName'
+        },
+        {
+          title: '操作人',
+          key: 'operator',
+          render(row: any) {
+            return (
+                <div>
+                  <div>{row.operatorName}</div>
+                  <div>{row.updateTime}</div>
+                </div>
+            )
+          }
+        },
+        {
+          title: '操作',
+          key: 'operation',
+          fixed: 'right',
+          width: 180,
+          render(row: any) {
+            return (
+                <NSpace>
+                  <NButton
+                      type="primary"
+                      text
+                      //v-auth="materialCategory/update1599962354053140482"
+                      onClick={() => {
+                        state.saveMode = 'edit'
+                        state.showSaveDialog = true
+                        state.rowData = row
+                      }}
+                  >
+                    修改
+                  </NButton>
+                  <NButton
+                      type="primary"
+                      size="small"
+                      text
+                      //v-auth="sysNotice/status1599959101026455553"
+                      onClick={() => onChangeStatus(row)}
+                  >
+                    {row.enableFlag ? '停用' : '启用'}
+                  </NButton>
+                </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    const saveForm = ref()
+    const onSubmit = () => {
+      state.pagination.page = 1
+      getList()
+    }
+
+    const onSearch = () => {
+      saveForm.value?.submit()
+    }
+    const onBtnReset = () => {
+      saveForm.value?.reset()
+    }
+
+    const initCategoryList = async () => {
+      try {
+        state.loading = true
+        const body = {
+          page: 1,
+          rows: 9999
+        }
+        const {data} = await subjectCategoryPage(body)
+        state.loading = false
+
+        data.rows.forEach((next: any) => {
+          state.categorizeList.push({
+            label: next.name,
+            value: next.id,
+            enableFlag: next.enableFlag,
+          })
+        })
+        console.log(state.categorizeList)
+      } catch (e) {
+        console.log("err", e);
+      }
+    }
+    const initInstrumentList = async () => {
+      try {
+        state.loading = true
+        const body = {
+          page: 1,
+          rows: 9999
+        }
+        const {data} = await musicalInstrumentPage(body)
+        state.loading = false
+
+        data.rows.forEach((next: any) => {
+          state.instrumentList.push({
+            label: next.name,
+            value: next.id + '',
+            enableFlag: next.enableFlag,
+          })
+        })
+      } catch (e) {
+
+      }
+    }
+
+    const getList = async () => {
+      try {
+        state.loading = true
+        const {times, ...reset} = searchForm
+        const body = {
+          ...reset,
+          ...filterTimes(times, ['startTime', 'endTime']),
+          page: state.pagination.page,
+          rows: state.pagination.rows,
+
+        }
+        const {data} = await subjectPage(body)
+        state.loading = false
+        state.pagination.pageTotal = Number(data.total)
+        state.dataList = data.rows || []
+      } catch {
+        state.loading = false
+      }
+    }
+
+    const onChangeStatus = (row: any) => {
+      const statusStr = row.enableFlag ? '停用' : '启用'
+      dialog.warning({
+        title: '警告',
+        content: `是否${statusStr}?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          try {
+            await subjectUpdateStatus({
+              id: row.id
+            })
+            getList()
+            message.success(`${statusStr}成功`)
+          } catch {
+          }
+        }
+      })
+    }
+
+    onMounted(() => {
+      getList()
+      initCategoryList()
+      initInstrumentList()
+    })
+
+    return () => (
+        <div class="system-menu-container">
+          <div class={['section-container']}>
+            <SaveForm
+                ref={saveForm}
+                model={searchForm}
+                onSubmit={onSubmit}
+                onSetModel={(val: any) => Object.assign(searchForm, val)}
+                saveKey="subject-list"
+            >
+              <NFormItem path="keyword" label="关键字">
+                <NInput
+                    placeholder="请输入编号/名称"
+                    v-model:value={searchForm.keyword}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="categorize" label="声部分类">
+                <NSelect
+                    v-model={[searchForm.categoryId, 'value']}
+                    placeholder="请选择声部分类"
+                    filterable
+                    options={state.categorizeList as any}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="categorize" label="关联乐器">
+                <NSelect
+                    v-model={[searchForm.musicalInstrumentId, 'value']}
+                    placeholder="请选择关联乐器"
+                    filterable
+                    options={state.instrumentList as any}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="operator" label="操作人">
+                <NInput
+                    placeholder="请输入操作人"
+                    v-model:value={searchForm.operatorKeyword}
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem path="times" label="操作时间">
+                <NDatePicker
+                    class={styles.datepicker}
+                    value-format="yyyy.MM.dd"
+                    v-model:value={searchForm.times}
+                    type="daterange"
+                    clearable
+                />
+              </NFormItem>
+              <NFormItem>
+                <NSpace>
+                  <NButton type="primary" onClick={onSearch}>
+                    查询
+                  </NButton>
+                  <NButton type="default" onClick={onBtnReset}>
+                    重置
+                  </NButton>
+                </NSpace>
+              </NFormItem>
+            </SaveForm>
+
+            <NSpace style={{paddingBottom: '12px'}}>
+              <NButton
+                  type="primary"
+                  //v-auth="materialCategory/save1599962104022290433"
+                  onClick={() => {
+                    state.saveMode = 'add'
+                    state.showSaveDialog = true
+                  }}
+                  disabled={state.loading}
+              >
+                添加
+              </NButton>
+            </NSpace>
+            <NDataTable
+                scroll-x="1300"
+                loading={state.loading}
+                columns={columns()}
+                data={state.dataList}
+                children-key="subMaterialCategoryList"
+                default-expand-all={false}
+                row-key={(row: any) => row.id}
+            ></NDataTable>
+            <Pagination
+                v-model:page={state.pagination.page}
+                v-model:pageSize={state.pagination.rows}
+                v-model:pageTotal={state.pagination.pageTotal}
+                onList={getList}
+                sync
+                saveKey="login-device"
+            ></Pagination>
+          </div>
+          <NModal
+              v-model:show={state.showSaveDialog}
+              preset="dialog"
+              showIcon={false}
+              title={state.saveMode === 'add' ? '添加声部' : '修改声部'}
+              style={{width: '400px'}}
+          >
+            <SubjectSave
+                type={state.saveMode}
+                data={state.rowData}
+                categoryList={deepClone(state.categorizeList)}
+                instrumentList={deepClone(state.instrumentList)}
+                onClose={() => (state.showSaveDialog = false)}
+                onGetList={getList}
+            />
+          </NModal>
+        </div>
+    )
+  }
+})