index.tsx 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. import {
  2. computed,
  3. defineComponent,
  4. nextTick,
  5. onMounted,
  6. reactive,
  7. shallowRef
  8. } from 'vue'
  9. import styles from './index.module.less'
  10. import ColHeader from '@/components/col-header'
  11. import { Button, Image, NoticeBar, Popup, Toast } from 'vant'
  12. import { state as baseState, setLogin } from '@/state'
  13. import iconShare from './new-images/icon-share.png'
  14. import { useEventListener } from '@vant/use'
  15. import ColShare from '@/components/col-share'
  16. import iconMemberLogo from './images/member_logo.png'
  17. import iconStudent from '@common/images/icon_student.png'
  18. import iconTeacher from '@common/images/icon_teacher.png'
  19. import iconGift from './new-images/icon-discount-gift.png'
  20. import request from '@/helpers/request'
  21. import MemberInteres from './components/member-interes'
  22. import ColSticky from '@/components/col-sticky'
  23. import { moneyFormat } from '@/helpers/utils'
  24. import { useRoute, useRouter } from 'vue-router'
  25. import deepClone from '@/helpers/deep-clone'
  26. import { memberSimpleType, memberType, studyCardType } from '@/constant'
  27. import dayjs from 'dayjs'
  28. import { orderStatus } from '../order-detail/orderStatus'
  29. import TheNoticeBar from '@/components/the-noticeBar'
  30. import { tradeOrder } from '@/student/trade/tradeOrder'
  31. export default defineComponent({
  32. name: 'member-center',
  33. setup() {
  34. const route = useRoute()
  35. const router = useRouter()
  36. const vipList = shallowRef([] as any)
  37. const svipList = shallowRef([] as any)
  38. const state = reactive({
  39. activityId: route.query.activityId,
  40. recomUserId: route.query.recomUserId,
  41. titleOpacity: 0,
  42. shareStatus: false, // 分享
  43. dialogVisiable: false,
  44. orderVisiable: false,
  45. orderDetail: {} as any,
  46. shareUrl: '',
  47. shareDiscount: 0,
  48. discountTeacher: {
  49. avatar: '',
  50. discount: 0,
  51. username: ''
  52. }, // 优惠折扣老师
  53. apiSuffix:
  54. baseState.platformType === 'STUDENT' ? '/api-student' : '/api-teacher',
  55. tabActive: 'SVIP' as 'SVIP' | 'VIP', // 当前选中
  56. selectMember: {} as any, // 选中的数据
  57. memberShowList: [] // 购买商品信息
  58. })
  59. // 活动数据
  60. const activitData = reactive({
  61. activityId: undefined,
  62. activityStart: '',
  63. activityEnd: '',
  64. registrationPrice: 0,
  65. buyCount: 0, //buyCount 小于1的时候 代表能无限购买
  66. buyNum: 0,
  67. vipCardId: 0,
  68. activityList: [],
  69. extConfig: {} as Record<string, any>,
  70. vipType: 'VIP'
  71. })
  72. const userInfo = computed(() => {
  73. const users = baseState.user.data
  74. return {
  75. username: users?.username,
  76. phone: users?.phone,
  77. avatar: users?.heardUrl,
  78. id: users?.userId,
  79. userVip: users?.userVip,
  80. discountCardFlag: users?.discountCardFlag,
  81. discountEndTime: users?.discountEndTime,
  82. discountStartTime: users?.discountStartTime
  83. }
  84. })
  85. // 是否为永久会员
  86. const isPermanent = computed(() => {
  87. return userInfo.value.userVip?.vipType === 'PERMANENT_SVIP' ? true : false
  88. })
  89. // 购买按钮文案
  90. const btnSubmitText = computed(() => {
  91. if (isPermanent.value) {
  92. return '您已是永久SVIP会员'
  93. } else if (userMemberStatus.value === 'EXPIREVIP') {
  94. return '立即续费'
  95. } else if (userMemberStatus.value === 'NOT_VIP') {
  96. if (state.selectMember?.id) {
  97. // ¥988/永久SVIP 立即开通
  98. return `¥${calcSalePrice(state.selectMember)}/${
  99. memberSimpleType[state.selectMember?.period]
  100. }${state.tabActive} 立即开通`
  101. } else {
  102. return '立即开通'
  103. }
  104. }
  105. return '立即续费'
  106. })
  107. /** 当前用户会员状态 动态判断vip svip */
  108. const userMemberStatus = computed(() => {
  109. // vip类型 VIP:会员 SVIP:SVIP,PERMANENT_SVIP:永久SVIP,NOT_VIP:不是vip
  110. // vip过期类型 VIP:会员 SVIP:SVIP,ALL_VIP:全vip
  111. if (state.tabActive === 'SVIP') {
  112. if (userInfo.value.userVip?.vipType === 'PERMANENT_SVIP') {
  113. return 'PERMANENT'
  114. } else if (
  115. userInfo.value.userVip?.vipType === 'SVIP' ||
  116. userInfo.value.userVip?.svipEndDays > 0
  117. ) {
  118. return 'VIP'
  119. } else if (
  120. ['SVIP', 'ALL_VIP'].includes(userInfo.value.userVip?.expireVipType)
  121. ) {
  122. return 'EXPIREVIP'
  123. } else if (userInfo.value.userVip?.vipType === 'NOT_VIP') {
  124. return 'NOT_VIP'
  125. }
  126. } else if (state.tabActive === 'VIP') {
  127. if (userInfo.value.userVip?.vipType === 'PERMANENT_SVIP') {
  128. return 'PERMANENT'
  129. } else if (
  130. userInfo.value.userVip?.vipType === 'VIP' ||
  131. userInfo.value.userVip?.vipEndDays > 0
  132. ) {
  133. return 'VIP'
  134. } else if (
  135. ['VIP', 'ALL_VIP'].includes(userInfo.value.userVip?.expireVipType)
  136. ) {
  137. return 'EXPIREVIP'
  138. } else if (userInfo.value.userVip?.vipType === 'NOT_VIP') {
  139. return 'NOT_VIP'
  140. }
  141. }
  142. return 'NOT_VIP'
  143. })
  144. /** 会员信息 */
  145. const memberInfos = computed(() => {
  146. return {
  147. memberLength: state.memberShowList.length,
  148. vipLength: vipList.value.legnth,
  149. svipLength: svipList.value.legnth,
  150. onlyVip:
  151. vipList.value.length > 0 && svipList.value.length <= 0 ? true : false,
  152. onlySVip:
  153. svipList.value.length > 0 && vipList.value.length <= 0 ? true : false,
  154. hasAll:
  155. vipList.value.length > 0 && svipList.value.length > 0 ? true : false
  156. }
  157. })
  158. /** 分享 */
  159. const onShare = async () => {
  160. try {
  161. const res = await request.post('/api-teacher/open/vipProfit', {
  162. data: {
  163. userId: userInfo.value.id
  164. }
  165. })
  166. state.shareUrl = `${location.origin}/teacher#/shareVip?recomUserId=${userInfo.value.id}&userType=${baseState.platformType}`
  167. // 判断是否有我分享的编号
  168. if (res.data && res.data.activityId) {
  169. state.shareUrl = state.shareUrl + '&activityId=' + res.data.activityId
  170. }
  171. state.shareStatus = true
  172. state.shareDiscount = res.data.discount || 0
  173. return
  174. } catch {
  175. //
  176. }
  177. }
  178. useEventListener('scroll', () => {
  179. const height =
  180. window.scrollY ||
  181. window.pageYOffset ||
  182. document.documentElement.scrollTop
  183. state.titleOpacity = height > 30 ? 1 : 0
  184. })
  185. /** 切换购买类型 */
  186. const onChangeTab = (type: 'SVIP' | 'VIP') => {
  187. if (type === 'SVIP') {
  188. state.memberShowList = deepClone(svipList.value)
  189. state.selectMember = state.memberShowList[0]
  190. } else if (type === 'VIP') {
  191. state.memberShowList = deepClone(vipList.value)
  192. state.selectMember = state.memberShowList[0]
  193. }
  194. state.tabActive = type
  195. nextTick(() => {
  196. document.querySelector('.system-list')?.scroll(0, 0)
  197. })
  198. }
  199. const calcSalePrice = (item: any) => {
  200. // 有活动 以活动价格为准
  201. if (item.id === activitData.vipCardId) {
  202. return activitData.registrationPrice
  203. }
  204. // discount
  205. if (item.discount === 1) {
  206. const tempPrice = Number(
  207. (item.salePrice - item.discountPrice).toFixed(2)
  208. )
  209. return tempPrice >= 0 ? tempPrice : 0
  210. }
  211. return item.salePrice
  212. }
  213. const onSubmit = async () => {
  214. try {
  215. // 永久会员
  216. if (isPermanent.value) return
  217. const { data } = await request.post(
  218. `${state.apiSuffix}/memberPriceSettings/list`,
  219. {
  220. data: {
  221. status: 1
  222. }
  223. }
  224. )
  225. const result = data.list || []
  226. const selectItem = result.find(
  227. (item: any) => item.id === state.selectMember?.id
  228. )
  229. // 状态、售价变更时
  230. if (
  231. !selectItem ||
  232. (selectItem && selectItem.salePrice !== state.selectMember.salePrice)
  233. ) {
  234. state.dialogVisiable = true
  235. return
  236. }
  237. // 判断是否有待支付订单
  238. const resPadding = await request.post(
  239. `${state.apiSuffix}/userOrder/getPendingOrder`,
  240. {
  241. data: { goodType: state.tabActive }
  242. }
  243. )
  244. console.log(resPadding, 'resPadding')
  245. if (resPadding?.data?.id) {
  246. state.orderVisiable = true
  247. state.orderDetail = resPadding.data || {}
  248. return
  249. }
  250. const member: any = state.selectMember
  251. // 判断是否有会员
  252. let startTime = new Date()
  253. // vip类型 VIP:会员 SVIP:SVIP,PERMANENT_SVIP:永久SVIP,NOT_VIP:不是vip
  254. // vip过期类型 VIP:会员 SVIP:SVIP,ALL_VIP:全vip
  255. if (state.tabActive === 'SVIP') {
  256. startTime = dayjs(
  257. userInfo.value.userVip.svipEndDate || new Date()
  258. ).toDate()
  259. } else if (state.tabActive === 'VIP') {
  260. // 购买Vip时,先有vip的有效时间,如果没有则取SVIP有效时间,都没有默认当前
  261. startTime = dayjs(
  262. userInfo.value.userVip.vipEndDate ||
  263. userInfo.value.userVip.svipEndDate ||
  264. new Date()
  265. ).toDate()
  266. } else if (isPermanent.value) {
  267. Toast('您已是永久SVIP会员')
  268. return
  269. }
  270. let endTime = new Date()
  271. if (member.period === 'MONTH') {
  272. endTime = dayjs(startTime).add(1, 'month').toDate()
  273. } else if (member.period === 'QUARTERLY') {
  274. endTime = dayjs(startTime).add(3, 'month').toDate()
  275. } else if (member.period === 'YEAR_HALF') {
  276. endTime = dayjs(startTime).add(6, 'month').toDate()
  277. } else if (member.period === 'YEAR') {
  278. endTime = dayjs(startTime).add(1, 'year').toDate()
  279. }
  280. orderStatus.orderObject.orderType = state.tabActive
  281. orderStatus.orderObject.orderName = `小酷Ai ${state.tabActive} ${member.title}`
  282. orderStatus.orderObject.orderDesc = `小酷Ai ${state.tabActive} ${member.title}`
  283. orderStatus.orderObject.actualPrice = calcSalePrice(member)
  284. orderStatus.orderObject.recomUserId = state.recomUserId
  285. orderStatus.orderObject.activityId = state.activityId
  286. orderStatus.orderObject.orderNo = ''
  287. const orderData = {
  288. orderType: state.tabActive,
  289. goodsName: `小酷Ai ${state.tabActive} ${member.title}`,
  290. id: member.id,
  291. title: member.title,
  292. num: 1, // 购买个数
  293. salePrice: member.salePrice,
  294. period: member.period,
  295. vipEndDays: userInfo.value.userVip?.vipEndDays || 0, // 会员剩余天数
  296. svipEndDays: userInfo.value.userVip?.svipEndDays || 0,
  297. discount: member.discount, // 是否有折扣
  298. discountPrice: member.discountPrice, // 折扣金额
  299. price: calcSalePrice(member),
  300. startTime: dayjs(startTime).format('YYYY-MM-DD'),
  301. endTime: dayjs(endTime).format('YYYY-MM-DD'),
  302. recomUserId: state.recomUserId,
  303. activityBuyCount: 0, // 活动购买限制次数
  304. activityList: [], // 活动赠送的东西
  305. discountCardFlag: userInfo.value.discountCardFlag, // 畅学卡结束时间
  306. discountEndTime: userInfo.value.discountEndTime, // 畅学卡结束时间
  307. discountStartTime: userInfo.value.discountStartTime // 畅学卡开始时间
  308. }
  309. // 当能购买并且是当前活动商品时候加上 活动信息
  310. const canBuyNum = activitData.buyCount - activitData.buyNum
  311. if (member.id === activitData.vipCardId && canBuyNum > 0) {
  312. orderData.activityBuyCount = canBuyNum
  313. orderData.activityList = activitData.activityList
  314. orderStatus.orderObject.activityId = activitData.activityId
  315. }
  316. orderStatus.orderObject.orderList = [orderData]
  317. router.push({
  318. path: '/orderDetail',
  319. query: {
  320. orderType: state.tabActive
  321. }
  322. })
  323. } catch {
  324. //
  325. }
  326. }
  327. // 取消支付
  328. const onCancelOrder = async () => {
  329. try {
  330. await request.post(`${state.apiSuffix}/userOrder/orderCancel`, {
  331. data: { orderNo: state.orderDetail.orderNo }
  332. })
  333. } catch {
  334. //
  335. }
  336. state.orderVisiable = false
  337. }
  338. // 继续支付
  339. const onContinueOrder = async () => {
  340. const orderDetail = state.orderDetail || {}
  341. tradeOrder(orderDetail, () => {
  342. router.push({
  343. path: '/orderDetail',
  344. query: {
  345. orderType: orderDetail.orderType
  346. }
  347. })
  348. })
  349. }
  350. /** 格式化分类信息 */
  351. const formatMemberList = () => {
  352. // console.log(vipList.value, svipList.value, 'vipList.value')
  353. const onlyVip =
  354. vipList.value.length > 0 && svipList.value.length <= 0 ? true : false
  355. const onlySVip =
  356. svipList.value.length > 0 && vipList.value.length <= 0 ? true : false
  357. const hasAll =
  358. vipList.value.length > 0 && svipList.value.length > 0 ? true : false
  359. if (hasAll) {
  360. state.tabActive = 'SVIP'
  361. state.memberShowList = deepClone(svipList.value)
  362. } else if (onlySVip) {
  363. state.tabActive = 'SVIP'
  364. state.memberShowList = deepClone(svipList.value)
  365. } else if (onlyVip) {
  366. state.tabActive = 'VIP'
  367. state.memberShowList = deepClone(vipList.value)
  368. }
  369. if (state.memberShowList.length > 0) {
  370. // 判断是否有数据更新了,需要重新为选择的赋值
  371. const item = state.memberShowList.find(
  372. (item: any) => item.id === state.selectMember?.id
  373. )
  374. if (item) {
  375. state.selectMember = item
  376. } else {
  377. state.selectMember = state.memberShowList[0]
  378. }
  379. }
  380. }
  381. const _init = async () => {
  382. try {
  383. const { data } = await request.post(
  384. `${state.apiSuffix}/memberPriceSettings/list`,
  385. {
  386. data: {
  387. activityId: Number(state.activityId),
  388. userId: state.recomUserId,
  389. status: 1
  390. }
  391. }
  392. )
  393. const { list, ...more } = data
  394. // 学生端 活动 有分享活动的时候 就不支持折扣活动
  395. if (baseState.platformType === 'STUDENT' && !state.activityId) {
  396. const activitRes = await request.post(
  397. `/api-student/memberPriceSettings/getMemberBuyGift`
  398. )
  399. if (activitRes.code === 200 && activitRes.data) {
  400. const {
  401. activityStart,
  402. activityEnd,
  403. registrationPrice,
  404. buyCount,
  405. buyNum,
  406. vipCardId,
  407. activityRewardList,
  408. extConfig,
  409. id
  410. } = activitRes.data || {}
  411. // 匹配当前的 会员
  412. const vipData = list.find(item => {
  413. return item.id === vipCardId
  414. })
  415. // 当匹配上会员 并且没有达到购买限制的时候才有活动
  416. if (vipData && (buyCount < 1 || buyCount > buyNum)) {
  417. activitData.activityId = id
  418. activitData.vipType = vipData.vipType
  419. activitData.activityStart = activityStart
  420. activitData.activityEnd = activityEnd
  421. activitData.registrationPrice = registrationPrice
  422. activitData.buyCount = buyCount < 1 ? 99999 : buyCount //buyCount 小于1的时候 代表能无限购买
  423. activitData.buyNum = buyNum
  424. activitData.vipCardId = vipCardId
  425. extConfig && (activitData.extConfig = JSON.parse(extConfig))
  426. activitData.activityList = (activityRewardList || []).map(
  427. item => {
  428. const { rewardType, vipCardId, unit } = item.activityReward
  429. if (rewardType === 'DISCOUNT') {
  430. return {
  431. goodType: rewardType,
  432. goodName: `畅学卡 ${studyCardType[unit]}`,
  433. goodNum: 1,
  434. bizContent: vipCardId,
  435. giftFlag: true,
  436. vipEndDays: null,
  437. goodsNum: 1,
  438. unit
  439. }
  440. }
  441. return {
  442. goodType: rewardType,
  443. goodName: `小酷Ai ${rewardType} ${memberType[unit]}`,
  444. goodNum: 1,
  445. bizContent: vipCardId,
  446. giftFlag: true,
  447. vipEndDays: null,
  448. goodsNum: 1,
  449. unit
  450. }
  451. }
  452. )
  453. }
  454. }
  455. }
  456. state.discountTeacher = {
  457. ...more
  458. }
  459. const result = list || []
  460. const vipTemp = [] as any
  461. const svipTemp = [] as any
  462. result.forEach((item: any) => {
  463. item.title = memberType[item.period]
  464. if (item.vipType === 'VIP' && item.period !== 'DAY') {
  465. vipTemp.push(item)
  466. } else if (item.vipType === 'SVIP' && item.period !== 'DAY') {
  467. svipTemp.push(item)
  468. }
  469. })
  470. vipList.value = vipTemp ? vipTemp.reverse() : []
  471. svipList.value = svipTemp ? svipTemp.reverse() : []
  472. formatMemberList()
  473. } catch {
  474. //
  475. }
  476. }
  477. onMounted(async () => {
  478. try {
  479. const userInfo = await request.get(
  480. baseState.platformType === 'TEACHER'
  481. ? '/api-teacher/teacher/queryUserInfo'
  482. : '/api-student/student/queryUserInfo'
  483. )
  484. setLogin(userInfo.data)
  485. _init()
  486. } catch {
  487. //
  488. }
  489. })
  490. return () => (
  491. <div class={styles.memberCenter}>
  492. <ColHeader
  493. background={`rgba(255,255,255, ${state.titleOpacity})`}
  494. color={`rgba(0,0,0, ${state.titleOpacity})`}
  495. backIconColor="black"
  496. hideHeader={false}
  497. border={false}
  498. v-slots={{
  499. right: () => (
  500. <Image
  501. src={iconShare}
  502. class={styles.shareBtn}
  503. onClick={onShare}
  504. />
  505. )
  506. }}
  507. />
  508. <div class={styles.memberContainer}>
  509. <i class={styles.showBrid}></i>
  510. <div class={styles.userSection}>
  511. <div
  512. class={[
  513. styles.userImgSection,
  514. state.tabActive === 'VIP' ? styles.userVip : styles.userSVip,
  515. userMemberStatus.value === 'PERMANENT' ||
  516. userMemberStatus.value === 'VIP'
  517. ? styles.isVip
  518. : ''
  519. // userMemberStatus.value === 'EXPIREVIP' ? styles.expireVip : ''
  520. ]}
  521. >
  522. <Image
  523. class={styles.userImg}
  524. src={userInfo.value.avatar || iconStudent}
  525. fit="cover"
  526. />
  527. <i class={styles.showMemeber}></i>
  528. </div>
  529. <div class={styles.userInfo}>
  530. <div class={styles.userName}>
  531. <span class={styles.name}>{userInfo.value.username}</span>
  532. {userInfo.value.phone && (
  533. <span class={styles.phone}>({userInfo.value.phone})</span>
  534. )}
  535. </div>
  536. <div class={styles.member_time}>
  537. {userMemberStatus.value === 'PERMANENT' && (
  538. <>
  539. 您已是<span>永久SVIP</span>
  540. </>
  541. )}
  542. {userMemberStatus.value === 'VIP' && (
  543. <>
  544. {state.tabActive}有效期至{' '}
  545. <span>
  546. {state.tabActive === 'VIP'
  547. ? dayjs(userInfo.value.userVip.vipEndDate).format(
  548. 'YYYY-MM-DD'
  549. )
  550. : dayjs(userInfo.value.userVip.svipEndDate).format(
  551. 'YYYY-MM-DD'
  552. )}
  553. </span>
  554. </>
  555. )}
  556. {userMemberStatus.value === 'EXPIREVIP' && (
  557. <>
  558. 您的{state.tabActive}已过期,续费后{state.tabActive}
  559. 权益可继续使用
  560. </>
  561. )}
  562. {userMemberStatus.value === 'NOT_VIP' && (
  563. <>您还未开通{state.tabActive}会员哦~</>
  564. )}
  565. </div>
  566. </div>
  567. </div>
  568. <div class={styles.memberSection}>
  569. {memberInfos.value.hasAll ? (
  570. <>
  571. <div class={styles.member_tabs}>
  572. <div
  573. class={[
  574. styles.member_tab,
  575. state.tabActive === 'VIP' ? styles.member_tab_active : ''
  576. ]}
  577. onClick={() => onChangeTab('VIP')}
  578. >
  579. <div class={[styles.top_tab, styles.top_tab_vip]}>
  580. <i class={[styles.icon_member]}></i>
  581. <span class={styles.icon_text}>
  582. <i class={styles.bottom_line}></i>
  583. </span>
  584. </div>
  585. <div class={styles.vip_member_tip}></div>
  586. {/* 活动显示 */}
  587. {!!activitData.vipCardId &&
  588. activitData.vipType === 'VIP' &&
  589. activitData.extConfig?.title1 && (
  590. <div class={styles.activitTip}>
  591. {activitData.extConfig?.title1}
  592. </div>
  593. )}
  594. </div>
  595. <div
  596. class={[
  597. styles.member_tab,
  598. state.tabActive === 'SVIP' ? styles.member_tab_active : ''
  599. ]}
  600. onClick={() => onChangeTab('SVIP')}
  601. >
  602. <div class={[styles.top_tab, styles.top_tab_svip]}>
  603. <i class={[styles.icon_member]}></i>
  604. <span class={styles.icon_text}>
  605. <i class={styles.bottom_line}></i>
  606. </span>
  607. </div>
  608. <div class={styles.svip_member_tip}></div>
  609. {/* 活动显示 */}
  610. {!!activitData.vipCardId &&
  611. activitData.vipType === 'SVIP' &&
  612. activitData.extConfig?.title1 && (
  613. <div class={styles.activitTip}>
  614. {activitData.extConfig?.title1}
  615. </div>
  616. )}
  617. </div>
  618. </div>
  619. </>
  620. ) : memberInfos.value.onlyVip ? (
  621. <div class={styles.member_tabs}>
  622. <div
  623. class={[
  624. styles.member_tab,
  625. styles.member_tab_active,
  626. styles.member_tab_single
  627. ]}
  628. >
  629. <div class={[styles.top_tab, styles.top_tab_vip]}>
  630. <i class={[styles.icon_member]}></i>
  631. <span class={styles.icon_text}>
  632. <i class={styles.bottom_line}></i>
  633. </span>
  634. </div>
  635. <div class={styles.vip_member_tip}></div>
  636. </div>
  637. </div>
  638. ) : memberInfos.value.onlySVip ? (
  639. // 只有SVIP
  640. <div class={styles.member_tabs}>
  641. <div
  642. class={[
  643. styles.member_tab,
  644. styles.member_tab_active,
  645. styles.member_tab_single
  646. ]}
  647. >
  648. <div class={[styles.top_tab, styles.top_tab_svip]}>
  649. <i class={[styles.icon_member]}></i>
  650. <span class={styles.icon_text}>
  651. <i class={styles.bottom_line}></i>
  652. </span>
  653. </div>
  654. <div class={styles.svip_member_tip}></div>
  655. </div>
  656. </div>
  657. ) : null}
  658. {/* 选择会员模式 */}
  659. {(vipList.value.length > 0 || svipList.value.length > 0) && (
  660. <>
  661. {/* 判断是否有推荐老师 一个会员都没有配置不显示优惠信息 */}
  662. {state.discountTeacher.discount == 1 && (
  663. <div class={styles.memberDiscount}>
  664. <Image
  665. src={state.discountTeacher.avatar || iconTeacher}
  666. class={styles.discountAvatar}
  667. />
  668. <TheNoticeBar class={styles.discountName} isAnimation>
  669. {state.discountTeacher.username}
  670. 老师的
  671. <span>专属优惠~</span>
  672. </TheNoticeBar>
  673. <Image src={iconGift} class={styles.discountGift} />
  674. </div>
  675. )}
  676. <div class={styles.system_list_section}>
  677. <div
  678. class={[
  679. 'system-list',
  680. styles['system-list'],
  681. state.tabActive === 'VIP' ? styles.system_list_vip : '',
  682. memberInfos.value.memberLength === 2
  683. ? styles.list_two
  684. : '',
  685. memberInfos.value.memberLength === 1
  686. ? styles.list_one
  687. : ''
  688. ]}
  689. >
  690. {memberInfos.value.memberLength >= 2 ? (
  691. <>
  692. {state.memberShowList.map((member: any) => (
  693. <div
  694. key={member.id}
  695. class={[
  696. styles['system-item'],
  697. member.id === state.selectMember.id
  698. ? styles.active
  699. : ''
  700. ]}
  701. onClick={() => {
  702. state.selectMember = member
  703. }}
  704. >
  705. {/* 有活动优先展示活动 */}
  706. {activitData.vipCardId === member.id &&
  707. activitData.extConfig?.title2 ? (
  708. <div class={styles.activitTip1}>
  709. {activitData.extConfig?.title2}
  710. </div>
  711. ) : (
  712. /* 只有永久才会有数量提示 */
  713. member.period === 'PERPETUAL' && (
  714. <span class={[styles.iconPermanent]}></span>
  715. )
  716. )}
  717. <p class={styles.s_title}>
  718. {member.title}
  719. {/* 优惠标识 */}
  720. {member.discount === 1 && (
  721. <span class={styles.discountTag}></span>
  722. )}
  723. </p>
  724. <p class={styles.price}>
  725. <span>¥</span>
  726. {moneyFormat(calcSalePrice(member), '0,0[.]00')}
  727. </p>
  728. <del
  729. class={[
  730. styles.originalPrice,
  731. calcSalePrice(member) >= member.originalPrice ||
  732. member.desc
  733. ? styles.originalPriceHide
  734. : ''
  735. ]}
  736. >
  737. ¥{moneyFormat(member.originalPrice, '0,0[.]00')}
  738. </del>
  739. {member.desc && (
  740. <p class={styles.extraTip}>{member.desc}</p>
  741. )}
  742. </div>
  743. ))}
  744. </>
  745. ) : (
  746. <>
  747. {/* 一条数据的样式 */}
  748. {state.memberShowList.map((member: any) => (
  749. <div class={[styles['system-item']]}>
  750. {/* 有活动优先展示活动 */}
  751. {activitData.vipCardId === member.id &&
  752. activitData.extConfig?.title2 ? (
  753. <div class={styles.activitTip1}>
  754. {activitData.extConfig?.title2}
  755. </div>
  756. ) : (
  757. /* 只有永久才会有数量提示 */
  758. member.period === 'PERPETUAL' && (
  759. <span class={[styles.iconPermanent]}></span>
  760. )
  761. )}
  762. {member.discount === 1 && (
  763. <span class={styles.discountTag}></span>
  764. )}
  765. <div class={styles.oneInfo}>
  766. <div class={styles.priceS}>
  767. <p class={styles.price}>
  768. <span>¥</span>
  769. {moneyFormat(
  770. calcSalePrice(member),
  771. '0,0[.]00'
  772. )}
  773. </p>
  774. <p class={styles.s_title}>{member.title}</p>
  775. </div>
  776. <div
  777. class={[
  778. styles.oneMaxNum,
  779. styles.oneMaxNumPrice
  780. ]}
  781. >
  782. ¥{member.originalPrice}
  783. </div>
  784. </div>
  785. <span
  786. class={[
  787. styles.oneBtn,
  788. ['EXPIREVIP', 'VIP', 'PERMANENT'].includes(
  789. userMemberStatus.value
  790. )
  791. ? styles.onBtnRenew
  792. : '',
  793. userMemberStatus.value === 'PERMANENT'
  794. ? styles.onBtnDisbled
  795. : ''
  796. ]}
  797. onClick={() => {
  798. if (userMemberStatus.value === 'PERMANENT')
  799. return
  800. onSubmit()
  801. }}
  802. ></span>
  803. <i class={styles.itemBg}></i>
  804. </div>
  805. ))}
  806. </>
  807. )}
  808. </div>
  809. </div>
  810. </>
  811. )}
  812. {/* 是选择会员 会员天数大于0 */}
  813. {state.tabActive === 'VIP' &&
  814. userInfo.value.userVip?.vipEndDays > 0 && (
  815. <div class={styles.discountTips}>
  816. 购买VIP的会员续费SVIP年度会员,
  817. <span>原VIP会员天数升级为SVIP</span>
  818. </div>
  819. )}
  820. {/* 活动描述 */}
  821. {state.selectMember?.id === activitData.vipCardId &&
  822. activitData.extConfig?.describe && (
  823. <div class={styles.activitTip2}>
  824. <div class={styles.activitTipText}>
  825. {activitData.extConfig?.describe}
  826. </div>
  827. </div>
  828. )}
  829. <MemberInteres type={state.tabActive} />
  830. </div>
  831. </div>
  832. {(vipList.value.length > 0 || svipList.value.length > 0) && (
  833. <ColSticky position="bottom">
  834. <div class={styles.btnGroup}>
  835. <Button
  836. block
  837. color="linear-gradient( 241deg, #FFD984 0%, #FFEAB9 100%)"
  838. class={[
  839. styles.btn,
  840. isPermanent.value ? styles.btnDisabled : ''
  841. ]}
  842. onClick={onSubmit}
  843. >
  844. {btnSubmitText.value}
  845. </Button>
  846. </div>
  847. </ColSticky>
  848. )}
  849. <Popup
  850. v-model:show={state.shareStatus}
  851. style={{ background: 'transparent' }}
  852. >
  853. <ColShare
  854. teacherId={userInfo.value.id}
  855. shareUrl={state.shareUrl}
  856. shareType="vip"
  857. shareLength={2}
  858. >
  859. <div class={styles.shareVip}>
  860. {state.shareDiscount === 1 && (
  861. <div class={styles.tagDiscount}>专属优惠</div>
  862. )}
  863. <img class={styles.icon} src={iconMemberLogo} />
  864. <div class={styles.info}>
  865. <h4 class="van-multi-ellipsis--l2">小酷Ai会员</h4>
  866. <p>海量曲谱、智能评测,专为器乐学习者量身打造</p>
  867. </div>
  868. </div>
  869. </ColShare>
  870. </Popup>
  871. <Popup
  872. v-model:show={state.dialogVisiable}
  873. style={{ background: 'transparent' }}
  874. closeOnClickOverlay={false}
  875. >
  876. <div class={styles.dialogContainer}>
  877. <div class={styles.dialogTitle}>提示</div>
  878. <div class={styles.dialogContent}>产品信息已更新,请重新选择</div>
  879. <div class={styles.dialogBtnGroup}>
  880. <Button
  881. round
  882. type="primary"
  883. block
  884. // class={styles.dialogBtn}
  885. onClick={() => {
  886. _init()
  887. state.dialogVisiable = false
  888. }}
  889. >
  890. 重新选择
  891. </Button>
  892. </div>
  893. </div>
  894. </Popup>
  895. <Popup
  896. v-model:show={state.orderVisiable}
  897. style={{ background: 'transparent' }}
  898. closeOnClickOverlay={false}
  899. >
  900. <div class={styles.dialogContainer}>
  901. <div class={styles.dialogTitle}>提示</div>
  902. <div class={styles.dialogContent}>
  903. 您有待支付的订单,是否继续支付
  904. </div>
  905. <div class={[styles.dialogBtnGroup, styles.orderGroup]}>
  906. <Button round type="default" plain block onClick={onCancelOrder}>
  907. 取消订单
  908. </Button>
  909. <Button
  910. round
  911. type="primary"
  912. block
  913. class={styles.dialogBtn}
  914. onClick={onContinueOrder}
  915. >
  916. 继续支付
  917. </Button>
  918. </div>
  919. </div>
  920. </Popup>
  921. </div>
  922. )
  923. }
  924. })