addMusic.tsx 18 KB

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