addMusic.tsx 21 KB

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