Browse Source

需求开发

yuanliang 1 year ago
parent
commit
27c5f5bea4

+ 1 - 1
src/router/constant.ts

@@ -65,6 +65,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'), // 曲谱管理
-    appClient: () => import('@/views/app-manage/index'), // 应用管理
+    instrumentManger: () => import('@/views/system-manage/instrument-manage/index'), // 应用管理
 
 }

+ 19 - 0
src/utils/constant.ts

@@ -230,3 +230,22 @@ export const heardLevelType = {
   ADVANCED: '进阶级',
   PERFORMER: '大师级'
 } as any
+
+// 曲目类型
+export const musicSheetType = {
+  SINGLE: '单曲',
+  CONCERT: '合奏'
+} as any
+
+// 作者属性
+export const musicSheetSourceType = {
+  PLATFORM: '平台',
+  ORG: '机构',
+  PERSON: '个人'
+} as any
+
+// 审核版本
+export const releaseCandidate = {
+  YES: '是',
+  NO: '否'
+} as any

+ 43 - 31
src/utils/filters.ts

@@ -1,5 +1,17 @@
 import * as constant from './constant'
 
+
+// 公共处理
+export const getMapValueByKey = (key: string, map: Map<string, string>) => {
+  if (key && map) {
+    let value = map.get(key);
+    if (value) {
+      return value;
+    }
+  }
+  return key
+}
+
 // 岗位管理
 export const filterPosition = (key: 'IM_SERVICE' | 'REPAIR' | 'STAFF' | '') => {
   if (key && constant.position && constant.position[key]) {
@@ -47,7 +59,7 @@ export const filterOpenType = (key: 'ORIGINAL' | 'ADAPAY' | 'OTHER' | '') => {
 
 // 客户端类型
 export const filterClientType = (
-  key: 'BACKEND' | 'SCHOOL' | 'TEACHER' | 'STUDENT' | 'REPAIR' | ''
+    key: 'BACKEND' | 'SCHOOL' | 'TEACHER' | 'STUDENT' | 'REPAIR' | ''
 ) => {
   if (key && constant.clientType && constant.clientType[key]) {
     return constant.clientType[key]
@@ -85,7 +97,7 @@ export const filterSuggestionType = (key: 'APP' | 'SMART_PRACTICE' | '') => {
 
 // 内容分类
 export const filterContentCategory = (
-  key: 'HOT_CONSULTATION' | 'OPEN_SCREEN_AD' | 'FLASH_PAGE' | 'ROTATION_CHART' | 'MUSIC' | ''
+    key: 'HOT_CONSULTATION' | 'OPEN_SCREEN_AD' | 'FLASH_PAGE' | 'ROTATION_CHART' | 'MUSIC' | ''
 ) => {
   if (key && constant.contentCategory && constant.contentCategory[key]) {
     return constant.contentCategory[key]
@@ -96,7 +108,7 @@ export const filterContentCategory = (
 
 // 交付团类型  filterGroupType
 export const filterGroupType = (
-  key: 'SINGLE_DELIVERY' | 'MULTIPLE_DELIVERY' | 'MULTIPLE_DELIVERY_SCHOOL'
+    key: 'SINGLE_DELIVERY' | 'MULTIPLE_DELIVERY' | 'MULTIPLE_DELIVERY_SCHOOL'
 ) => {
   if (key && constant.orchestraTypes && constant.orchestraTypes[key]) {
     return constant.orchestraTypes[key]
@@ -112,7 +124,7 @@ export const filterOrchestraType = (key: 'DELIVERY' | 'PROMOTION') => {
 
 // 乐团状态  SUBJECT_CONFIG
 export const filterGroupStatus = (
-  key: 'INITIATION_SURVEY' | 'PRE_REGISTER' | 'REGISTER' | 'DOING' | 'DONE' | 'CLOSE'
+    key: 'INITIATION_SURVEY' | 'PRE_REGISTER' | 'REGISTER' | 'DOING' | 'DONE' | 'CLOSE'
 ) => {
   if (key && constant.musicStatus && constant.musicStatus[key]) {
     return constant.musicStatus[key]
@@ -139,20 +151,20 @@ export const filterAccompanimentType = (key: 'HOMEMODE' | 'COMMON' | '') => {
 
 // 课程类型
 export const filterCourseEmnu = (
-  key:
-    | 'PERCUSSION_SINGLE'
-    | 'FLUTE_SINGLE'
-    | 'SAX_SINGLE'
-    | 'CLARINET_SINGLE'
-    | 'TRUMPET_SINGLE'
-    | 'TROMBONE_SINGLE'
-    | 'HORN_SINGLE'
-    | 'BARITONE_TUBA_SINGLE'
-    | 'EUPHONIUM_SINGLE'
-    | 'TUBA_SINGLE'
-    | 'MUSIC_THEORY'
-    | 'INSTRUMENTAL_ENSEMBLE'
-    | ''
+    key:
+        | 'PERCUSSION_SINGLE'
+        | 'FLUTE_SINGLE'
+        | 'SAX_SINGLE'
+        | 'CLARINET_SINGLE'
+        | 'TRUMPET_SINGLE'
+        | 'TROMBONE_SINGLE'
+        | 'HORN_SINGLE'
+        | 'BARITONE_TUBA_SINGLE'
+        | 'EUPHONIUM_SINGLE'
+        | 'TUBA_SINGLE'
+        | 'MUSIC_THEORY'
+        | 'INSTRUMENTAL_ENSEMBLE'
+        | ''
 ) => {
   if (key && constant.courseEmnu && constant.courseEmnu[key]) {
     return constant.courseEmnu[key]
@@ -181,16 +193,16 @@ export const filterStudentStatusEmnu = (key: 'REGISTER' | 'LEARNING' | 'OUTOF_OR
 
 // 订单状态
 export const filterOrderStatus = (
-  key:
-    | 'WAIT_PAY'
-    | 'PAYING'
-    | 'PAID'
-    | 'TIMEOUT'
-    | 'FAIL'
-    | 'CLOSED'
-    | 'REFUNDING'
-    | 'REFUNDED'
-    | ''
+    key:
+        | 'WAIT_PAY'
+        | 'PAYING'
+        | 'PAID'
+        | 'TIMEOUT'
+        | 'FAIL'
+        | 'CLOSED'
+        | 'REFUNDING'
+        | 'REFUNDED'
+        | ''
 ) => {
   if (key && constant.orderStatus && constant.orderStatus[key]) {
     return constant.orderStatus[key]
@@ -237,7 +249,7 @@ export const filterRefundStatus = (key: 'PASS' | 'REJECT' | 'ING' | 'CLOSED' | '
 
 // 学生考勤类型  attendanceStatus
 export const filterAttendanceStatus = (
-  key: 'LATE' | 'NORMAL' | 'LEAVE' | 'TRUANCY' | 'UNCALLED' | ''
+    key: 'LATE' | 'NORMAL' | 'LEAVE' | 'TRUANCY' | 'UNCALLED' | ''
 ) => {
   if (key && constant.attendanceStatus && constant.attendanceStatus[key]) {
     return constant.attendanceStatus[key]
@@ -248,7 +260,7 @@ export const filterAttendanceStatus = (
 
 // 老师考勤类型
 export const filterTeacherAttendanceStatus = (
-  key: 'NORMAL' | 'EXCEPTION' | 'LATE' | 'EARLY' | 'TRUANCY' | 'NO_SIGN' | 'LOCATION_EXCEPTION' | ''
+    key: 'NORMAL' | 'EXCEPTION' | 'LATE' | 'EARLY' | 'TRUANCY' | 'NO_SIGN' | 'LOCATION_EXCEPTION' | ''
 ) => {
   if (key && constant.teacherAttendanceStatus && constant.teacherAttendanceStatus[key]) {
     return constant.teacherAttendanceStatus[key]
@@ -268,7 +280,7 @@ export const filterAuthStatus = (key: 'DOING' | 'PASS' | 'UNPASS') => {
 
 // 结算状态
 export const filterWithdrawalStatus = (
-  key: 'WAIT' | 'SETTLED' | 'PART_SETTLED' | 'SETTLE_FAIL'
+    key: 'WAIT' | 'SETTLED' | 'PART_SETTLED' | 'SETTLE_FAIL'
 ) => {
   if (key && constant.withdrawalStatus && constant.withdrawalStatus[key]) {
     return constant.withdrawalStatus[key]

+ 27 - 0
src/utils/objectUtil.ts

@@ -0,0 +1,27 @@
+export const getSelectDataFromObj = (obj: Object) => {
+  const result = [];
+  if (obj) {
+    const map = new Map(Object.entries(obj));
+    for (const key of map.keys()) {
+      let oneSelect = Object.fromEntries([
+        ["label", map.get(key)],
+        ["value", key],
+      ]);
+      result.push(oneSelect);
+    }
+  }
+  console.log(result)
+  return result;
+}
+
+
+// 公共处理
+export const getMapValueByKey = (key: string, map: Map<string, string>) => {
+  if (key && map) {
+    let value = map.get(key);
+    if (value) {
+      return value;
+    }
+  }
+  return key
+}

+ 0 - 9
src/views/app-manage/api.ts

@@ -1,9 +0,0 @@
-import request from '@/utils/request/index'
-// 查询列表
-export const sysMessagePage = (params: object) => {
-  return request({
-    url: '/cbs-app/sysMessage/page',
-    method: 'post',
-    data: params
-  } as any)
-}

+ 0 - 59
src/views/app-manage/index.tsx

@@ -1,59 +0,0 @@
-import { NTabPane, NTabs } from 'naive-ui'
-import { defineComponent, h, reactive, resolveDynamicComponent } from 'vue'
-import CategroryList from '@/views/music-categrory/index'
-import { useRoute } from 'vue-router'
-import { getTabsCache, setTabsCaches } from '@/hooks/use-async'
-export default defineComponent({
-  name: 'app-manage',
-  setup() {
-    const state = reactive({
-      tabName: 'MusicList' as 'MusicList' | 'TagList' | 'CategroryList',
-      searchId: null
-    })
-    const route = useRoute()
-    getTabsCache((val: any) => {
-      if (val.form.tabName) {
-        state.tabName = val.form.tabName
-      }
-    })
-    const setTabName = (val: any) => {
-      console.log('setTabName', val)
-      state.tabName = val.tabName
-      state.searchId = val.id
-    }
-    const setTabs = (val: any) => {
-      setTabsCaches(val, 'tabName', route)
-    }
-    return () => {
-      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="MusicList" tab="曲目列表"
-                    //v-auth="musicSheet/page1602301588206350338"
-                >
-                </NTabPane>
-                {/* <NTabPane name="TagList" tab="曲目标签管理" v-auth="musicTag/page1602301689389740033">
-                <TagList />
-              </NTabPane> */}
-                <NTabPane
-                    name="CategroryList"
-                    tab="曲目分类管理"
-                    //v-auth="/musicCategrory1607664813521346561"
-                >
-                  <CategroryList onSetTabName={setTabName} />
-                </NTabPane>
-              </NTabs>
-            </div>
-          </div>
-      )
-    }
-  }
-})

+ 53 - 28
src/views/music-library/music-sheet/component/music-list.tsx

@@ -1,33 +1,17 @@
 import SaveForm from '@/components/save-form'
 import Pagination from '@/components/pagination'
-import {
-  NButton,
-  NDataTable,
-  NFormItem,
-  NImage,
-  NInput,
-  NModal,
-  NSelect,
-  NSpace,
-  NTag,
-  useDialog,
-  useMessage,
-  NCascader,
-  NDescriptions,
-  NDescriptionsItem, NIcon
-} from 'naive-ui'
+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 MusicOperation from '../modal/music-operation'
-import {subjectBasicConfigPage, subjectPage} from '@/views/system-manage/api'
-import {accompanimentTypeArray, audioTypeArray} from '@/utils/searchArray'
+import {subjectPage} from '@/views/system-manage/api'
 import MusicPreView from '../modal/musicPreView'
-import TheTooltip from '@/components/TheTooltip'
 import {filterPointCategory} from '@/views/teaching-manage/unit-test'
 import UseProject from "@views/music-library/music-sheet/modal/use-project";
-import {DeleteFilled} from "@vicons/antd";
-import {IMAGE_SVG} from "@wangeditor/editor/dist/editor/src/constants/svg";
+import {filterClientType, getMapValueByKey} from "@/utils/filters";
+import {getValueForKey} from "@/utils/searchArray";
+import {musicSheetSourceType, musicSheetType} from "@/utils/constant";
 
 export default defineComponent({
   name: 'content-flash',
@@ -66,7 +50,8 @@ export default defineComponent({
       musicPreview: false,
       musicScore: null as any,
       showUseProject: false, // 适用项目
-      showUseProjectData: undefined // 适用项目行数据
+      showUseProjectData: undefined, // 适用项目行数据
+      detailReadonly: false // 新增、修改、详情是否可编辑
     })
 
     const columns = (): any => {
@@ -92,23 +77,41 @@ export default defineComponent({
           title: '封面图',
           key: 'titleImg',
           render(row: any): JSX.Element {
-            return <NImage width={60} height={60} src={row.titleImg}/>
+            return <NImage width={60} height={60} src={row.musicCover}/>
           }
         },
         {
           title: '音乐人',
-          key: 'author'
+          key: 'composer'
         },
         {
           title: '曲目类型',
-          key: 'musicSheetCategoriesName'
+          key: 'musicSheetType',
+          render(row: any) {
+            return getMapValueByKey(row.musicSheetType, new Map(Object.entries(musicSheetType)));
+          }
         },
         {
           title: '作者属性',
-          key: 'authorFrom'
+          key: 'sourceType',
+          render(row: any) {
+            return getMapValueByKey(row.sourceType, new Map(Object.entries(musicSheetSourceType)));
+          }
         },
         {
           title: '所属人',
+          key: 'composer'
+        },
+        {
+          title: '上传人',
+          key: 'createBy'
+        },
+        {
+          title: '上传时间',
+          key: 'createTime'
+        },
+        {
+          title: '审核版本',
           key: 'userName'
         },
         {
@@ -173,6 +176,7 @@ export default defineComponent({
                         state.visiableMusic = true
                         state.musicOperation = 'preview'
                         state.musicData = row
+                        state.detailReadonly = true
                       }}
                   >
                     查看
@@ -186,6 +190,7 @@ export default defineComponent({
                         state.visiableMusic = true
                         state.musicOperation = 'edit'
                         state.musicData = row
+                        state.detailReadonly = true
                       }}
                   >
                     修改
@@ -256,7 +261,7 @@ export default defineComponent({
     const onRmove = (row: any): void => {
       dialog.warning({
         title: '警告',
-        content: `删除"${row.musicSheetName}",是否继续?`,
+        content: `删除"${row.name}",是否继续?`,
         positiveText: '确定',
         negativeText: '取消',
         onPositiveClick: async () => {
@@ -467,6 +472,25 @@ export default defineComponent({
                   }
               />
             </NFormItem>
+            <NFormItem label="审核版本" path="status">
+              <NSelect
+                  v-model={[state.searchForm.status, 'value']}
+                  placeholder="请选择审核版本"
+                  clearable
+                  options={
+                    [
+                      {
+                        label: '是',
+                        value: 1
+                      },
+                      {
+                        label: '否',
+                        value: 0
+                      }
+                    ] as any
+                  }
+              />
+            </NFormItem>
             <NFormItem>
               <NSpace>
                 <NButton type="primary" onClick={onSearch}>
@@ -487,6 +511,7 @@ export default defineComponent({
                     state.visiableMusic = true
                     state.musicOperation = 'add'
                     state.musicData = {}
+                    state.detailReadonly = true
                   }}
               >
                 新增曲目
@@ -566,7 +591,7 @@ export default defineComponent({
               preset="dialog"
               showIcon={false}
               title={'适用项目'}
-              style={{width: '500px',height: '650px'}}
+              style={{width: '500px', height: '650px'}}
           >
             <UseProject item={state.musicScore} rowData={state.showUseProjectData}/>
           </NModal>

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

@@ -43,9 +43,6 @@ export default defineComponent({
               >
                 <MusicList searchId={state.searchId} />
               </NTabPane>
-              {/* <NTabPane name="TagList" tab="曲目标签管理" v-auth="musicTag/page1602301689389740033">
-                <TagList />
-              </NTabPane> */}
               <NTabPane
                 name="CategroryList"
                 tab="曲目分类管理"

+ 13 - 1
src/views/music-library/music-sheet/modal/music-operation.tsx

@@ -26,6 +26,8 @@ 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 {getSelectDataFromObj} from "@/utils/objectUtil";
 
 /**
  * 获取指定元素下一个Note元素
@@ -295,7 +297,8 @@ export default defineComponent({
       belongTo: undefined,
       speed: undefined,
       playMetronome: 1,
-      playStyle: undefined // 播放方式
+      playStyle: undefined, // 播放方式
+      releaseCandidate: 'NO' // 是否审核版本
     })
     const state = reactive({
       tagList: [...props.tagList] as any, // 标签列表
@@ -657,6 +660,15 @@ export default defineComponent({
                 />
               </NFormItemGi>
             </NGrid>
+            <NGrid cols={2}>
+              <NFormItemGi label="审核版本" path="speed">
+                <NSelect
+                    options={getSelectDataFromObj(releaseCandidate)}
+                    v-model:value={forms.releaseCandidate}
+                    defaultValue={'NO'}
+                />
+              </NFormItemGi>
+            </NGrid>
             <NAlert showIcon={false} style={{marginBottom:"12px" }}>曲目上传</NAlert>
             <NGrid cols={2}>
               <NFormItemGi label="播放模式" path="audioType"

+ 38 - 10
src/views/music-library/music-sheet/modal/use-project.tsx

@@ -2,8 +2,7 @@ import {defineComponent, reactive, onMounted} from 'vue'
 import {useUserStore} from '@/store/modules/user'
 import styles from "@views/music-library/music-sheet/modal/index.module.less";
 import {ref} from 'vue';
-import {NButton, NCheckbox, NForm, NFormItem, NGrid, NInput, NInputNumber, NSelect, NSpace, NTab, NTabPane, NTabs} from "naive-ui";
-import {CheckboxGroup} from "vant";
+import {NButton, NCheckbox, NCheckboxGroup, NForm, NFormItem, NGrid, NInput, NInputNumber, NSelect, NSpace, NTab, NTabPane, NTabs} from "naive-ui";
 import {setTabsCaches} from "@/hooks/use-async";
 import {useRoute} from "vue-router";
 
@@ -23,7 +22,12 @@ export default defineComponent({
   },
   setup(props, {emit}) {
     const state = reactive({
-      tabName: '管乐迷'
+      tabName: '管乐迷',
+      selectApp: '', //选择的APP
+      showTabMec: true,
+      showTabJmedu: true,
+
+
     })
 
     onMounted(() => {
@@ -44,6 +48,12 @@ export default defineComponent({
 
     }
 
+    function changeSelectapp(apps: string) {
+      apps.split(',').forEach(app => {
+
+      })
+    }
+
 
     return () => (
         <div>
@@ -64,12 +74,16 @@ export default defineComponent({
                   }
                 ]}
             >
-              <CheckboxGroup>
-                <NCheckbox value="长笛">长笛</NCheckbox>
-                <NCheckbox value="竖笛">竖笛</NCheckbox>
-                <NCheckbox value="葫芦丝">葫芦丝</NCheckbox>
-                <NCheckbox value="萨克斯">萨克斯</NCheckbox>
-              </CheckboxGroup>
+              <NCheckboxGroup
+                  onUpdateValue={(value) => {
+                    state.selectApp = value.toString();
+                  }}
+              >
+                <NCheckbox value="MEC">管乐迷</NCheckbox>
+                <NCheckbox value="JMEDU">管乐团</NCheckbox>
+                <NCheckbox value="COOLESHOW">酷乐秀</NCheckbox>
+                <NCheckbox value="COOLESHOW_EDU">音乐数字课堂</NCheckbox>
+              </NCheckboxGroup>
             </NFormItem>
             <NTabs
                 type="line"
@@ -91,7 +105,21 @@ export default defineComponent({
                     label="是否收费"
                     path="musicSheetType"
                 >
-                  <NSelect>
+                  <NSelect
+                      clearable
+                      options={
+                        [
+                          {
+                            label: '是',
+                            value: 1
+                          },
+                          {
+                            label: '否',
+                            value: 0
+                          }
+                        ] as any
+                      }
+                  >
                   </NSelect>
                 </NFormItem>
                 <NFormItem

+ 178 - 0
src/views/system-manage/instrument-manage/index.tsx

@@ -0,0 +1,178 @@
+import Pagination from '@/components/pagination'
+import { NButton, NDataTable, NImage, NModal, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { subjectPage, subjectUpdate } from '../api'
+import SongOperation from './instrument-operation'
+
+export default defineComponent({
+  name: 'instrument-manage',
+  setup() {
+    const state = reactive({
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      searchForm: {
+        keyword: null,
+        status: null
+      },
+      dataList: [] as any,
+      visiableSong: false,
+      songOperation: 'add',
+      songData: {} as any
+    })
+    const dialog = useDialog()
+    const message = useMessage()
+
+    const columns = () => {
+      return [
+        {
+          title: '编号',
+          key: 'id'
+        },
+        {
+          title: '图片',
+          key: 'img',
+          render(row: any) {
+            return <NImage width={70} src={row.img} />
+          }
+        },
+        {
+          title: '所属声部',
+          key: 'name'
+        },
+        {
+          title: '乐器名称',
+          key: 'name'
+        },
+        {
+          title: '乐器编码',
+          key: 'code'
+        },
+        // {
+        //   title: '状态',
+        //   key: 'delFlag',
+        //   render(row: any) {
+        //     return !row.delFlag ? <NTag type="primary">启用</NTag> : <NTag type="error">停用</NTag>
+        //   }
+        // },
+        {
+          title: '操作',
+          key: 'operation',
+          render(row: any) {
+            return (
+              <NSpace>
+                <NButton
+                  type="primary"
+                  size="small"
+                  text
+                  //v-auth="subject/update1598205405598932994"
+                  onClick={() => {
+                    state.visiableSong = true
+                    state.songOperation = 'edit'
+                    state.songData = row
+                  }}
+                >
+                  修改
+                </NButton>
+
+                {/* <NButton
+                  type="primary"
+                  size="small"
+                  text
+                  v-auth="subject/update1668958388100288514"
+                  onClick={() => onOperation(row)}
+                >
+                  {row.delFlag ? '启用' : '停用'}
+                </NButton> */}
+              </NSpace>
+            )
+          }
+        }
+      ]
+    }
+
+    // const onOperation = async (row: any) => {
+    //   const content = row.delFlag ? `您是否启用“${row.name}”?` : `您是否停用“${row.name}”?`
+    //   dialog.warning({
+    //     title: '提示',
+    //     content,
+    //     positiveText: '确定',
+    //     negativeText: '取消',
+    //     onPositiveClick: async () => {
+    //       try {
+    //         await subjectUpdate({ id: row.id, delFlag: !row.delFlag })
+    //         message.success(`${row.delFlag ? '启用' : '停用'}成功`)
+    //         getList()
+    //       } catch {}
+    //     }
+    //   })
+    // }
+
+    const getList = async () => {
+      try {
+        state.loading = true
+        const { data } = await subjectPage({ ...state.pagination, ...state.searchForm })
+        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']}>
+          <NSpace style={{ paddingBottom: '12px' }}>
+            <NButton
+              type="primary"
+              //v-auth="subject/save1598205342327857154"
+              onClick={() => {
+                state.visiableSong = true
+                state.songOperation = 'add'
+                state.songData = {}
+              }}
+            >
+              添加乐器
+            </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
+          ></Pagination>
+        </div>
+        <NModal
+          v-model:show={state.visiableSong}
+          preset="dialog"
+          showIcon={false}
+          title={state.songOperation === 'add' ? '新增声部' : '修改声部'}
+          style={{ width: '500px' }}
+        >
+          <SongOperation
+            type={state.songOperation}
+            data={state.songData}
+            onClose={() => (state.visiableSong = false)}
+            onGetList={getList}
+          />
+        </NModal>
+      </div>
+    )
+  }
+})

+ 142 - 0
src/views/system-manage/instrument-manage/instrument-operation.tsx

@@ -0,0 +1,142 @@
+import UploadFile from '@/components/upload-file'
+import deepClone from '@/utils/deep.clone'
+import {NForm, NInput, NSpace, NButton, useMessage, NFormItem, NSelect} from 'naive-ui'
+import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
+import {subjectSave, subjectUpdate} from '../api'
+import {platformArr} from "@views/system-manage/app-version/operation";
+
+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,
+      code: null,
+      img: 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 subjectSave({...forms})
+            message.success('添加成功')
+          } else if (props.type === 'edit') {
+            await subjectUpdate({
+              ...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="img"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入声部图片'
+                  }
+                ]}
+            >
+              <UploadFile
+                  size={2}
+                  cropper
+                  v-model:fileList={forms.img}
+                  bucketName="gyt"
+                  path="basic/"
+                  tips="图片大小2M以内"
+              />
+            </NFormItem>
+            <NFormItem
+                label="所属声部"
+                path="name"
+                rule={[
+                  {
+                    required: true
+                  }
+                ]}
+            >
+              <NSelect
+                  // v-model:value={state.searchForm.platform}
+                  placeholder="请选择声部"
+                  // options={platformArr}
+                  clearable
+              />
+            </NFormItem>
+            <NFormItem
+                label="乐器名称"
+                path="name"
+                rule={[
+                  {
+                    required: true,
+                    message: '请输入声部名称'
+                  }
+                ]}
+            >
+              <NInput
+                  v-model:value={forms.name}
+                  placeholder="请输入声部名称"
+                  clearable
+                  maxlength={100}
+              ></NInput>
+            </NFormItem>
+            <NFormItem label="乐器编码" path="code">
+              <NInput v-model:value={forms.code} 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>
+    )
+  }
+})