index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import { defineComponent, onMounted, reactive, ref } from 'vue'
  2. import { Image, Tabs, Tab, List, Button, Popup, Dialog } from 'vant'
  3. import styles from './index.module.less'
  4. import TheSticky from '@/components/the-sticky'
  5. import ColHeader from '@/components/col-header'
  6. import { useWindowScroll, useEventListener } from '@vueuse/core'
  7. import request from '@/helpers/request'
  8. import iconMenu from './images/icon-menu.png'
  9. import iconRightTop from './images/icon-right-top.png'
  10. import iconAlbumCover from '../../images/icon-album-cover.png'
  11. import { state as baseState } from '@/state'
  12. import Song from '../component/song'
  13. import { useRoute, useRouter } from 'vue-router'
  14. import ColResult from '@/components/col-result'
  15. import { moneyFormat } from '@/helpers/utils'
  16. import { orderStatus } from '@/views/order-detail/orderStatus'
  17. import { postMessage } from '@/helpers/native-message'
  18. export default defineComponent({
  19. name: 'train-tool',
  20. setup() {
  21. const route = useRoute()
  22. const router = useRouter()
  23. const background = ref<string>('rgba(55, 205, 177, 0)')
  24. const color = ref<string>('#fff')
  25. const state = reactive({
  26. details: {} as any,
  27. albumId: route.query.albumId || null,
  28. activeTab: 'SUBJECT',
  29. loading: false,
  30. finished: false,
  31. isError: false,
  32. list: [] as any,
  33. popupStatus: false,
  34. selectMember: {} as any, // 购买的月份
  35. buyList: [
  36. {
  37. purchaseCycle: 6,
  38. salePrice: 0,
  39. costPrice: 0,
  40. status: true
  41. },
  42. {
  43. purchaseCycle: 12,
  44. salePrice: 0,
  45. costPrice: 0,
  46. status: false
  47. },
  48. {
  49. purchaseCycle: 18,
  50. salePrice: 0,
  51. costPrice: 0,
  52. status: false
  53. },
  54. {
  55. purchaseCycle: 24,
  56. salePrice: 0,
  57. costPrice: 0,
  58. status: false
  59. },
  60. {
  61. purchaseCycle: 30,
  62. salePrice: 0,
  63. costPrice: 0,
  64. status: false
  65. },
  66. {
  67. purchaseCycle: 36,
  68. salePrice: 0,
  69. costPrice: 0,
  70. status: false
  71. }
  72. ],
  73. ensembleCounts: false,
  74. musicCounts: false,
  75. subjectCounts: false
  76. })
  77. const params = reactive({
  78. page: 1,
  79. rows: 20
  80. })
  81. const apiSuffix = ref(
  82. baseState.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
  83. )
  84. const getDetails = async () => {
  85. try {
  86. let url = apiSuffix.value + '/userTenantAlbumRecord/detail'
  87. if (state.albumId) {
  88. url = url + '?albumId=' + state.albumId
  89. }
  90. const { data } = await request.post(url)
  91. state.details = data || {}
  92. state.buyList.forEach((item: any, index: number) => {
  93. item.salePrice = (index + 1) * data.salePrice
  94. item.costPrice = (index + 1) * data.costPrice
  95. })
  96. state.selectMember = {
  97. ...state.buyList[0]
  98. }
  99. state.ensembleCounts = data.ensembleCounts <= 0 ? false : true
  100. state.subjectCounts = data.subjectCounts <= 0 ? false : true
  101. state.musicCounts = data.musicCounts <= 0 ? false : true
  102. if (state.subjectCounts) {
  103. state.activeTab = 'SUBJECT'
  104. } else if (state.ensembleCounts) {
  105. state.activeTab = 'ENSEMBLE'
  106. } else if (state.musicCounts) {
  107. state.activeTab = 'MUSIC'
  108. }
  109. } catch {
  110. //
  111. }
  112. }
  113. const FetchList = async () => {
  114. if (state.loading) {
  115. return
  116. }
  117. console.log(state.details, 'state.details')
  118. state.loading = true
  119. state.isError = false
  120. const tempParams = {
  121. albumId: state.details.id || null,
  122. subjectType: state.activeTab,
  123. ...params
  124. }
  125. try {
  126. const { data } = await request.post(
  127. `${apiSuffix.value}/tenantAlbumMusic/page`,
  128. {
  129. data: tempParams
  130. }
  131. )
  132. if (state.list.length > 0 && data.pageNo === 1) {
  133. return
  134. }
  135. state.list = state.list.concat(data.rows || [])
  136. params.page = data.pageNo + 1
  137. // showContact.value = state.list.length > 0
  138. state.loading = false
  139. state.finished = data.pageNo >= data.totalPage
  140. params.page = data.pageNo + 1
  141. } catch (error) {
  142. state.isError = true
  143. }
  144. state.loading = false
  145. }
  146. onMounted(async () => {
  147. useEventListener(document, 'scroll', evt => {
  148. const { y } = useWindowScroll()
  149. if (y.value > 20) {
  150. background.value = `rgba(255, 255, 255)`
  151. color.value = 'black'
  152. postMessage({
  153. api: 'backIconChange',
  154. content: { iconStyle: 'black' }
  155. })
  156. } else {
  157. background.value = 'transparent'
  158. color.value = '#fff'
  159. postMessage({
  160. api: 'backIconChange',
  161. content: { iconStyle: 'white' }
  162. })
  163. }
  164. })
  165. state.loading = true
  166. await getDetails()
  167. await FetchList()
  168. state.loading = false
  169. })
  170. const onSubmit = async () => {
  171. const album = state.selectMember
  172. const details = state.details
  173. orderStatus.orderObject.orderType = 'TENANT_ALBUM'
  174. orderStatus.orderObject.orderName = details.name
  175. orderStatus.orderObject.orderDesc = details.name
  176. orderStatus.orderObject.actualPrice = album.salePrice
  177. // orderStatus.orderObject.recomUserId = route.query.recomUserId || 0
  178. // orderStatus.orderObject.activityId = route.query.activityId || 0
  179. orderStatus.orderObject.orderNo = ''
  180. orderStatus.orderObject.orderList = [
  181. {
  182. orderType: 'TENANT_ALBUM',
  183. goodsName: details.name,
  184. actualPrice: album.salePrice,
  185. // recomUserId: route.query.recomUserId || 0,
  186. price: album.salePrice,
  187. ...details,
  188. ...album
  189. }
  190. ]
  191. const res = await request.post('/api-student/userOrder/getPendingOrder', {
  192. data: {
  193. goodType: 'TENANT_ALBUM',
  194. bizId: details.id
  195. }
  196. })
  197. const result = res.data
  198. if (result) {
  199. state.popupStatus = false
  200. Dialog.confirm({
  201. title: '提示',
  202. message: '您有一个未支付的订单,是否继续支付?',
  203. confirmButtonColor: '#269a93',
  204. cancelButtonText: '取消订单',
  205. confirmButtonText: '继续支付'
  206. })
  207. .then(async () => {
  208. orderStatus.orderObject.orderNo = result.orderNo
  209. orderStatus.orderObject.actualPrice = result.actualPrice
  210. orderStatus.orderObject.discountPrice = result.discountPrice
  211. orderStatus.orderObject.paymentConfig = result.paymentConfig
  212. routerTo()
  213. })
  214. .catch(() => {
  215. Dialog.close()
  216. // 只用取消订单,不用做其它处理
  217. cancelPayment(result.orderNo)
  218. })
  219. } else {
  220. routerTo()
  221. }
  222. }
  223. const routerTo = () => {
  224. const album = state.details
  225. router.push({
  226. path: '/orderDetail',
  227. query: {
  228. orderType: 'ALBUM',
  229. album: album.id
  230. }
  231. })
  232. }
  233. const cancelPayment = async (orderNo: string) => {
  234. try {
  235. await request.post('/api-student/userOrder/orderCancel/v2', {
  236. data: {
  237. orderNo
  238. }
  239. })
  240. } catch {
  241. //
  242. }
  243. }
  244. return () => (
  245. <div class={styles.trainTool}>
  246. <TheSticky position="top">
  247. <ColHeader
  248. background={background.value}
  249. border={false}
  250. isFixed={false}
  251. color={color.value}
  252. backIconColor="white"
  253. />
  254. </TheSticky>
  255. <img class={styles.bgImg} src={state.details?.coverImg} />
  256. <div class={styles.musicContent}></div>
  257. <div class={styles.bg}>
  258. <div class={styles.alumWrap}>
  259. <div class={styles.img}>
  260. <Image
  261. class={styles.image}
  262. width="100%"
  263. height="100%"
  264. fit="cover"
  265. src={state.details?.coverImg || iconAlbumCover}
  266. errorIcon={iconAlbumCover}
  267. />
  268. <span class={styles.numContent}>
  269. <img src={iconMenu} class={styles.iconMenu} />共
  270. {state.details?.musicNum}首
  271. </span>
  272. <div class={styles.iconPian}>
  273. <Image
  274. class={styles.image}
  275. width="100%"
  276. height="100%"
  277. fit="cover"
  278. src={state.details?.coverImg}
  279. />
  280. </div>
  281. </div>
  282. <div class={styles.alumDes}>
  283. <div class={[styles.alumTitle, 'van-ellipsis']}>
  284. {state.details?.name}
  285. </div>
  286. <div
  287. class={[styles.des, 'van-multi-ellipsis--l2']}
  288. style={{
  289. height: '32px',
  290. lineHeight: '16px'
  291. }}
  292. >
  293. {state.details?.describe}
  294. </div>
  295. </div>
  296. </div>
  297. </div>
  298. <div class={styles.musicList}>
  299. <Tabs
  300. color="var(--van-primary)"
  301. background="transparent"
  302. lineWidth={20}
  303. shrink
  304. v-model:active={state.activeTab}
  305. onChange={val => {
  306. state.activeTab = val
  307. params.page = 1
  308. state.list = []
  309. FetchList()
  310. }}
  311. >
  312. {state.subjectCounts && <Tab title="声部练习" name="SUBJECT"></Tab>}
  313. {state.ensembleCounts && (
  314. <Tab title="合奏练习" name="ENSEMBLE"></Tab>
  315. )}
  316. {state.musicCounts && <Tab title="独奏曲目" name="MUSIC"></Tab>}
  317. </Tabs>
  318. <div class={styles.alumnList}>
  319. <List
  320. loading={state.loading}
  321. finished={state.finished}
  322. finished-text={' '}
  323. onLoad={FetchList}
  324. immediateCheck={false}
  325. error={state.isError}
  326. >
  327. {state.list && state.list.length ? (
  328. <Song
  329. showNumber
  330. list={state.list}
  331. onDetail={(item: any) => {
  332. router.push({
  333. path: '/music-detail',
  334. query: {
  335. id: item.id
  336. // albumId: route.params.id
  337. }
  338. })
  339. }}
  340. />
  341. ) : (
  342. !state.loading && (
  343. <ColResult
  344. tips="暂无曲目"
  345. classImgSize="SMALL"
  346. btnStatus={false}
  347. />
  348. )
  349. )}
  350. </List>
  351. </div>
  352. </div>
  353. {baseState.platformType === 'STUDENT' && (
  354. <TheSticky position="bottom">
  355. <div class={styles.btnGroup}>
  356. <Button
  357. round
  358. block
  359. disabled={state.details?.musicNum <= 0}
  360. color="#FE2451"
  361. onClick={() => (state.popupStatus = true)}
  362. >
  363. 购买教程
  364. </Button>
  365. </div>
  366. </TheSticky>
  367. )}
  368. <Popup
  369. v-model:show={state.popupStatus}
  370. position="bottom"
  371. round
  372. zIndex={9999}
  373. safe-area-inset-bottom
  374. closeable={false}
  375. class={styles.popupStatus}
  376. onClose={() => (state.popupStatus = false)}
  377. // onClosed={() => (this.openStatus = false)}
  378. >
  379. <div class={styles.bottom_function}>
  380. <i
  381. class={styles.iconClose}
  382. onClick={() => (state.popupStatus = false)}
  383. ></i>
  384. <img src={iconRightTop} class={styles.iconRightTop} />
  385. <div class={styles.memberMeal}>
  386. <div class={styles.titleMeal}>
  387. <span>请选择教程购买周期</span>
  388. </div>
  389. <div class={styles['system-list']}>
  390. {state.buyList.map((item: any) => (
  391. <div
  392. class={[
  393. styles['system-item'],
  394. item.status && styles.active
  395. ]}
  396. onClick={() => {
  397. state.buyList.forEach((item: any) => {
  398. item.status = false
  399. })
  400. item.status = true
  401. state.selectMember = item
  402. }}
  403. >
  404. <p class={styles.title}>{item.purchaseCycle}个月</p>
  405. <p class={styles.price}>
  406. <span>¥</span>
  407. {moneyFormat(item.salePrice, '0,0[.]00')}
  408. </p>
  409. {item.salePrice < item.costPrice && (
  410. <del class={styles.originalPrice}>
  411. ¥{moneyFormat(item.costPrice, '0,0[.]00')}
  412. </del>
  413. )}
  414. </div>
  415. ))}
  416. </div>
  417. </div>
  418. <div class={styles.btnGroup}>
  419. <Button round block class={styles.btn} onClick={onSubmit}>
  420. 点击购买
  421. </Button>
  422. </div>
  423. </div>
  424. </Popup>
  425. </div>
  426. )
  427. }
  428. })