addMusic.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. import { defineComponent, h, onMounted, reactive, ref } from 'vue'
  2. import SaveForm from '@components/save-form'
  3. import {
  4. DataTableColumns,
  5. DataTableRowKey,
  6. NButton,
  7. NCascader,
  8. NDataTable,
  9. NFormItem,
  10. NIcon,
  11. NImage,
  12. NInput,
  13. NInputNumber,
  14. NSelect,
  15. NSpace,
  16. NStep,
  17. NSteps,
  18. useDialog,
  19. useMessage
  20. } from 'naive-ui'
  21. import Pagination from '@components/pagination'
  22. import { getMapValueByKey, getSelectDataFromObj } from '@/utils/objectUtil'
  23. import {appKey, musicSheetSourceType, musicSheetType} from '@/utils/constant'
  24. import {musicSheetApplicationExtendCategoryList, musicSheetApplicationExtendSaveBatch, musicSheetApplicationOwnerList, musicSheetPage} from '@views/music-library/api'
  25. import deepClone from '@/utils/deep.clone'
  26. import { getOwnerName } from '@views/music-library/musicUtil'
  27. import TheTooltip from '@/components/TheTooltip'
  28. import {sysApplicationPage} from "@views/menu-manage/api";
  29. export default defineComponent({
  30. name: 'kt-addMusic',
  31. props: {
  32. appId: {
  33. type: String,
  34. required: true
  35. },
  36. subjectList: {
  37. type: Array,
  38. default: () => []
  39. },
  40. musicSheetCategories: {
  41. type: Array,
  42. default: () => []
  43. }
  44. },
  45. emits: ['close', 'getList'],
  46. setup(props, { slots, attrs, emit }) {
  47. const dialogs = useDialog()
  48. const message = useMessage()
  49. const btnLoading = ref(false)
  50. const state = reactive({
  51. loading: false,
  52. pagination: {
  53. page: 1,
  54. rows: 5,
  55. pageTotal: 0
  56. },
  57. stepPagination: {
  58. page: 1,
  59. rows: 5,
  60. pageTotal: 0
  61. },
  62. searchForm: {
  63. keyword: null,
  64. musicSheetType: null,
  65. subjectId: null,
  66. sourceType: null,
  67. composer : null,
  68. userId : null,
  69. applicationId : null,
  70. },
  71. subjectList: [] as any,
  72. showAdd: false,
  73. currentStep: 1,
  74. dataList: [],
  75. selectRowData: [] as any, // 选择的数据列表
  76. musicSheetCategories: [] as any,
  77. startSortNum: null as any, // 排序起始值
  78. projectMusicCategoryId: null as any, // 曲目分类ID
  79. useProjectData: [] as any, // 适用项目行数据
  80. userIdDisable: true,
  81. userIdData: [] as any,
  82. })
  83. onMounted(async () => {
  84. state.searchForm.keyword = null
  85. state.searchForm.musicSheetType = null
  86. state.searchForm.subjectId = null
  87. state.searchForm.sourceType = null
  88. state.searchForm.composer = null
  89. state.searchForm.userId = null
  90. state.searchForm.applicationId = null
  91. state.loading = true
  92. state.subjectList = props.subjectList
  93. // state.musicSheetCategories = props.musicSheetCategories
  94. //加载曲目分类列表
  95. try {
  96. const {data} = await musicSheetApplicationExtendCategoryList({
  97. applicationIds: props.appId
  98. })
  99. if (data && data.length > 0) {
  100. state.musicSheetCategories = data[0].musicSheetCategories
  101. }
  102. } catch {
  103. }
  104. await initUseAppList()
  105. await getList()
  106. })
  107. const initUseAppList = async () => {
  108. try {
  109. const appKeys = Object.keys(appKey)
  110. const { data } = await sysApplicationPage({ page: 1, rows: 999 })
  111. const tempList = data.rows || []
  112. state.useProjectData = []
  113. const filter = tempList.filter((next: any) => {
  114. return appKeys.includes(next.appKey)
  115. })
  116. filter.forEach((item: any) => {
  117. state.useProjectData.push({
  118. ...item,
  119. label: item.appName,
  120. value: item.id
  121. })
  122. })
  123. } catch {}
  124. }
  125. const getList = async () => {
  126. try {
  127. state.loading = true
  128. const search = {
  129. ...state.searchForm,
  130. userId: (state.searchForm.sourceType && state.searchForm.sourceType == 'PERSON') ? state.searchForm.userId : null,
  131. organizationRoleId: (state.searchForm.sourceType && state.searchForm.sourceType == 'ORG') ? state.searchForm.userId : null,
  132. }
  133. const { data } = await musicSheetPage({
  134. ...state.pagination,
  135. ...search,
  136. addAppId: props.appId
  137. })
  138. state.pagination.pageTotal = Number(data.total)
  139. state.dataList = data.rows || []
  140. } catch {}
  141. state.loading = false
  142. }
  143. const saveForm = ref()
  144. const onSearch = () => {
  145. saveForm.value?.submit()
  146. }
  147. const onBtnReset = () => {
  148. saveForm.value?.reset()
  149. }
  150. const onSubmit = () => {
  151. state.pagination.page = 1
  152. getList()
  153. }
  154. const onSave = async () => {
  155. if (state.selectRowData.length == 0) {
  156. message.error('未选择曲目')
  157. return
  158. }
  159. const params = [] as any[]
  160. for (let i = 0; i < state.selectRowData.length; i++) {
  161. const item = state.selectRowData[i]
  162. if (!item.projectMusicCategoryId) {
  163. message.error('乐谱教材不能为空')
  164. return
  165. }
  166. if (!item.sortNo) {
  167. item.sortNo = 0
  168. }
  169. params.push({
  170. ...item,
  171. musicSheetId: item.id,
  172. musicSheetCategoryId: item.projectMusicCategoryId,
  173. applicationId: props.appId,
  174. id: null
  175. })
  176. }
  177. btnLoading.value = true
  178. try {
  179. const res = (await musicSheetApplicationExtendSaveBatch(params)) as any
  180. if (res && res.code == '200') {
  181. message.success(`添加成功`)
  182. emit('getList')
  183. emit('close')
  184. }
  185. } catch (err) {}
  186. btnLoading.value = false
  187. }
  188. const columnsField = [
  189. {
  190. type: 'selection'
  191. },
  192. {
  193. title: '曲目编号',
  194. key: 'id'
  195. },
  196. {
  197. title: '封面图',
  198. key: 'titleImg',
  199. render(row: any) {
  200. return <NImage width={40} height={40} src={row.musicCover} />
  201. }
  202. },
  203. {
  204. title: '可用声部',
  205. key: 'subjectNames',
  206. render: (row: any) => {
  207. return <TheTooltip content={row.subjectNames}/>
  208. }
  209. },
  210. {
  211. title: '曲目名称',
  212. key: 'name'
  213. },
  214. {
  215. title: '音乐人',
  216. key: 'composer'
  217. },
  218. {
  219. title: '多声轨渲染',
  220. key: 'musicSheetType',
  221. render: (row: any) => {
  222. return (
  223. <div>
  224. {getMapValueByKey(row.musicSheetType, new Map(Object.entries(musicSheetType)))}
  225. </div>
  226. )
  227. }
  228. },
  229. {
  230. title: '曲目来源',
  231. key: 'sourceType',
  232. render(row: any) {
  233. return getMapValueByKey(row.sourceType, new Map(Object.entries(musicSheetSourceType)))
  234. }
  235. },
  236. {
  237. title: '所属人',
  238. key: 'userName',
  239. render: (row: any) => {
  240. return <TheTooltip content={getOwnerName(row.musicSheetExtend, row.sourceType)} />
  241. }
  242. }
  243. ]
  244. const columns = (): any => {
  245. return columnsField
  246. }
  247. const stepColumns = (): DataTableColumns => {
  248. const field = deepClone(columnsField)
  249. field.splice(0, 1)
  250. field.push({
  251. title(column: any) {
  252. return (
  253. <NSpace>
  254. 乐谱教材
  255. <NButton
  256. type="primary"
  257. size="small"
  258. text
  259. onClick={() => {
  260. dialogs.create({
  261. title: '请选择乐谱教材',
  262. showIcon: false,
  263. content: () => {
  264. return h(
  265. 'div',
  266. {
  267. class: 'flex flex-col justify-center items-center text-14px'
  268. },
  269. [
  270. // icon
  271. h(NCascader, {
  272. onUpdateValue(v) {
  273. state.projectMusicCategoryId = v
  274. },
  275. valueField: 'id',
  276. labelField: 'name',
  277. childrenField: 'children',
  278. placeholderField: '请选择乐谱教材',
  279. options: state.musicSheetCategories
  280. })
  281. ]
  282. )
  283. },
  284. positiveText: '确定',
  285. negativeText: '取消',
  286. onPositiveClick: () => {
  287. for (let i = 0; i < state.selectRowData.length; i++) {
  288. const item = state.selectRowData[i]
  289. item.projectMusicCategoryId = state.projectMusicCategoryId
  290. }
  291. }
  292. })
  293. }}
  294. >
  295. <NIcon size={15} style="padding-left: 5px;margin-top:4px">
  296. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  297. <path d="M2 26h28v2H2z" fill="currentColor"></path>
  298. <path
  299. d="M25.4 9c.8-.8.8-2 0-2.8l-3.6-3.6c-.8-.8-2-.8-2.8 0l-15 15V24h6.4l15-15zm-5-5L24 7.6l-3 3L17.4 7l3-3zM6 22v-3.6l10-10l3.6 3.6l-10 10H6z"
  300. fill="currentColor"
  301. ></path>
  302. </svg>
  303. </NIcon>
  304. </NButton>
  305. </NSpace>
  306. )
  307. },
  308. key: 'projectMusicCategoryId',
  309. fixed: 'right',
  310. width: 200,
  311. render: (row: any) => {
  312. // })
  313. return (
  314. <NCascader
  315. valueField="id"
  316. labelField="name"
  317. children-field="children"
  318. placeholder="请选择曲目分类"
  319. value={row.projectMusicCategoryId}
  320. options={state.musicSheetCategories}
  321. onUpdateValue={(value: any) => {
  322. row.projectMusicCategoryId = value
  323. }}
  324. clearable
  325. />
  326. )
  327. }
  328. })
  329. field.push({
  330. title(column: any) {
  331. return (
  332. <NSpace>
  333. 排序
  334. <NButton
  335. type="primary"
  336. size="small"
  337. text
  338. onClick={() => {
  339. dialogs.create({
  340. title: '请输入排序起始值',
  341. showIcon: false,
  342. content: () => {
  343. return h(
  344. 'div',
  345. {
  346. class: 'flex flex-col justify-center items-center text-14px'
  347. },
  348. [
  349. // icon
  350. h(NInputNumber, {
  351. onUpdateValue(v) {
  352. state.startSortNum = v
  353. },
  354. min: 0,
  355. max: 9999
  356. })
  357. ]
  358. )
  359. },
  360. positiveText: '确定',
  361. negativeText: '取消',
  362. onPositiveClick: () => {
  363. if (state.startSortNum) {
  364. for (let i = 0; i < state.selectRowData.length; i++) {
  365. const item = state.selectRowData[i]
  366. item.sortNo = state.startSortNum + i
  367. }
  368. }
  369. }
  370. })
  371. }}
  372. >
  373. <NIcon size={15} style="padding-left: 5px;margin-top:4px">
  374. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  375. <path d="M2 26h28v2H2z" fill="currentColor"></path>
  376. <path
  377. d="M25.4 9c.8-.8.8-2 0-2.8l-3.6-3.6c-.8-.8-2-.8-2.8 0l-15 15V24h6.4l15-15zm-5-5L24 7.6l-3 3L17.4 7l3-3zM6 22v-3.6l10-10l3.6 3.6l-10 10H6z"
  378. fill="currentColor"
  379. ></path>
  380. </svg>
  381. </NIcon>
  382. </NButton>
  383. </NSpace>
  384. )
  385. },
  386. key: 'sortNo',
  387. fixed: 'right',
  388. width: 150,
  389. render: (row: any) => {
  390. return h(NInputNumber, {
  391. value: row.sortNo,
  392. min: 0,
  393. max: 9999,
  394. defaultValue: 0,
  395. onUpdateValue(value: any) {
  396. row.sortNo = value
  397. }
  398. })
  399. }
  400. })
  401. field.push({
  402. title: '操作',
  403. key: 'operation',
  404. fixed: 'right',
  405. render(row: any) {
  406. return (
  407. <NSpace>
  408. <NButton
  409. type="primary"
  410. size="small"
  411. text
  412. //v-auth="musicSheet/update1602302618558099458"
  413. onClick={() => {
  414. dialogs.warning({
  415. title: '提示',
  416. content: `是否删除该数据?`,
  417. positiveText: '确定',
  418. negativeText: '取消',
  419. onPositiveClick: async () => {
  420. try {
  421. const index = state.selectRowData.findIndex((item: any) => {
  422. if (item.id == row.id) {
  423. return true
  424. }
  425. })
  426. if (index > -1) {
  427. state.selectRowData.splice(index, 1)
  428. }
  429. const index1 = checkedRowKeysRef.value.findIndex((item: any) => {
  430. if (item == row.id) {
  431. return true
  432. }
  433. })
  434. if (index1 > -1) {
  435. checkedRowKeysRef.value.splice(index, 1)
  436. }
  437. } catch {}
  438. }
  439. })
  440. }}
  441. >
  442. 移除
  443. </NButton>
  444. </NSpace>
  445. )
  446. }
  447. })
  448. return field
  449. }
  450. const checkedRowKeysRef = ref<DataTableRowKey[]>([])
  451. const handleCheck = (rowKeys: DataTableRowKey[]) => {
  452. checkedRowKeysRef.value = rowKeys
  453. // 添加行更新值
  454. state.dataList.forEach((next: any) => {
  455. if (checkedRowKeysRef.value.includes(next.id)) {
  456. const find = state.selectRowData.find((row: any) => {
  457. return row.id === next.id
  458. })
  459. if (!find) {
  460. state.selectRowData.push(next)
  461. }
  462. }
  463. })
  464. // 去掉行更新值
  465. state.selectRowData = state.selectRowData.filter((next: any) => {
  466. return checkedRowKeysRef.value.includes(next.id)
  467. })
  468. }
  469. const updateUserIdData = async (sourceType: any) => {
  470. if (!state.searchForm.applicationId) {
  471. return
  472. }
  473. state.userIdData = []
  474. state.searchForm.userId = null
  475. if (sourceType && sourceType !== 'PLATFORM') {
  476. const { data } = await musicSheetApplicationOwnerList({
  477. page: 1,
  478. rows: 9999,
  479. sourceType: sourceType,
  480. applicationId: state.searchForm.applicationId
  481. })
  482. const temp = data.rows || []
  483. temp.forEach((next: any) => {
  484. state.userIdData.push({
  485. ...next,
  486. label: sourceType === 'PERSON' ? next.userName : next.organizationRole,
  487. value: sourceType === 'PERSON' ? next.userId : next.organizationRoleId
  488. })
  489. })
  490. }
  491. }
  492. return () => {
  493. return (
  494. <div class="system-menu-container">
  495. <NSpace vertical size="medium">
  496. <NSteps
  497. current={state.currentStep}
  498. // onUpdateCurrent={()=>{
  499. // state.currentStep = val
  500. // }}
  501. style={'margin-bottom: 10px;margin-top: 10px'}
  502. >
  503. <NStep title="选择曲目" description=""></NStep>
  504. <NStep title="设置曲目信息" description=""></NStep>
  505. </NSteps>
  506. </NSpace>
  507. {state.currentStep === 1 && (
  508. <div class="system-menu-container">
  509. <SaveForm
  510. ref={saveForm}
  511. model={state.searchForm}
  512. onSubmit={onSubmit}
  513. // saveKey="cooleshow-edu-addMusic"
  514. onSetModel={(val: any) => (state.searchForm = val)}
  515. >
  516. <NFormItem label="关键词" path="keyword">
  517. <NInput
  518. v-model:value={state.searchForm.keyword}
  519. placeholder="请输入曲目名称/编号"
  520. clearable
  521. />
  522. </NFormItem>
  523. <NFormItem label="多声轨渲染" path="musicSheetType">
  524. <NSelect
  525. placeholder="请选择多声轨渲染"
  526. v-model:value={state.searchForm.musicSheetType}
  527. options={getSelectDataFromObj(musicSheetType)}
  528. clearable
  529. />
  530. </NFormItem>
  531. <NFormItem label="可用声部" path="musicSubject">
  532. <NSelect
  533. placeholder="请选择可用声部"
  534. v-model:value={state.searchForm.subjectId}
  535. options={state.subjectList}
  536. clearable
  537. />
  538. </NFormItem>
  539. <NFormItem label="音乐人" path="composer">
  540. <NInput
  541. placeholder="请选择音乐人"
  542. v-model:value={state.searchForm.composer}
  543. clearable
  544. />
  545. </NFormItem>
  546. <NFormItem label="曲目来源" path="sourceType">
  547. <NSelect
  548. placeholder="请选择曲目来源"
  549. v-model:value={state.searchForm.sourceType}
  550. options={getSelectDataFromObj(musicSheetSourceType)}
  551. onUpdateValue={async (value: any) => {
  552. state.userIdData = []
  553. state.searchForm.userId = null
  554. if (value && value !== 'PLATFORM') {
  555. await updateUserIdData(value)
  556. state.userIdDisable = false
  557. } else {
  558. state.userIdDisable = true
  559. }
  560. }}
  561. clearable
  562. />
  563. </NFormItem>
  564. <NFormItem label="项目" path="applicationId">
  565. <NSelect
  566. placeholder="请选择项目"
  567. v-model:value={state.searchForm.applicationId}
  568. options={state.useProjectData}
  569. clearable
  570. onUpdateValue={async (value: any) => {
  571. state.searchForm.applicationId = value
  572. if (value) {
  573. await updateUserIdData(state.searchForm.sourceType)
  574. state.userIdDisable = !(
  575. state.searchForm.sourceType && state.searchForm.sourceType !== 'PLATFORM'
  576. )
  577. } else {
  578. state.searchForm.userId = null
  579. state.userIdDisable = true
  580. state.userIdData = []
  581. }
  582. }}
  583. />
  584. </NFormItem>
  585. <NFormItem label="所属人" path="author">
  586. <NSelect
  587. filterable
  588. placeholder="请选择所属人"
  589. disabled={state.userIdDisable || (!state.searchForm.applicationId && !state.searchForm.sourceType)}
  590. v-model:value={state.searchForm.userId}
  591. options={state.userIdData}
  592. clearable
  593. ></NSelect>
  594. </NFormItem>
  595. <NFormItem>
  596. <NSpace>
  597. <NButton type="primary" onClick={onSearch}>
  598. 搜索
  599. </NButton>
  600. <NButton type="default" onClick={onBtnReset}>
  601. 重置
  602. </NButton>
  603. </NSpace>
  604. </NFormItem>
  605. </SaveForm>
  606. <p style={{ paddingBottom: '12px' }}>
  607. 你选择了<span style={'color:red;padding:0 8px'}>{state.selectRowData.length}</span>
  608. 条曲目
  609. </p>
  610. <NDataTable
  611. loading={state.loading}
  612. columns={columns()}
  613. data={state.dataList}
  614. rowKey={(row: any) => row.id}
  615. onUpdateCheckedRowKeys={handleCheck}
  616. ></NDataTable>
  617. <Pagination
  618. v-model:page={state.pagination.page}
  619. v-model:pageSize={state.pagination.rows}
  620. v-model:pageTotal={state.pagination.pageTotal}
  621. onList={getList}
  622. sync
  623. // saveKey="cooleshow-edu-addMusic"
  624. ></Pagination>
  625. </div>
  626. )}
  627. {state.currentStep === 2 && (
  628. <div class="system-menu-container" style={'margin-top: 15px;'}>
  629. <NDataTable
  630. loading={state.loading}
  631. columns={stepColumns()}
  632. data={state.selectRowData}
  633. rowKey={(row: any) => row.id}
  634. maxHeight={500}
  635. ></NDataTable>
  636. </div>
  637. )}
  638. <NSpace justify="end" style={'margin-top:10px'}>
  639. <NButton
  640. type="default"
  641. onClick={() => {
  642. if (state.currentStep > 1) {
  643. state.currentStep = state.currentStep - 1
  644. } else {
  645. emit('close')
  646. }
  647. }}
  648. >
  649. {state.currentStep === 1 ? '取消' : '上一步'}
  650. </NButton>
  651. <NButton
  652. type="primary"
  653. onClick={() => {
  654. if (state.currentStep < 2) {
  655. if (state.selectRowData.length == 0) {
  656. message.warning('请选择曲目')
  657. return
  658. }
  659. state.currentStep = state.currentStep + 1
  660. } else {
  661. onSave()
  662. }
  663. }}
  664. loading={btnLoading.value}
  665. disabled={btnLoading.value}
  666. >
  667. {state.currentStep === 2 ? '确定' : '下一步'}
  668. </NButton>
  669. </NSpace>
  670. </div>
  671. )
  672. }
  673. }
  674. })