exchange.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <!-- 积分兑换 -->
  2. <template>
  3. <div class="listBox">
  4. <div v-for="(item, index) in dataList" :key="'exchange' + index">
  5. <div class="cursor-pointer mx-5 mb-4" style="width: 180px;" @click="handleShowDetail(item)">
  6. <v-img width="180" height="180" :src="item.url"></v-img>
  7. <div class="ellipsis mt-2" style="font-size: 14px;">{{ item.name }}</div>
  8. <div class="ellipsis mt-1" style="font-size: 13px;">消耗积分<span class="ml-1" style="color: var(--v-primary-base)">{{ item.point }}</span></div>
  9. </div>
  10. </div>
  11. </div>
  12. <Dialog :visible="showDetail" titleClass="text-h6" :footer="point >= detailItem?.point" :widthType="3" title="详情说明" @submit="handleSubmit" @close="showDetail = false">
  13. <div class="color-primary font-size-20">{{ detailItem.name }}</div>
  14. <div class="tips">
  15. <div>使用说明:积分一经兑换概不退回,敬请谅解。</div>
  16. </div>
  17. <div class="d-flex align-center my-5">
  18. <div class="mr-1">消耗积分</div>
  19. <div class="color-primary">{{ detailItem.point }}</div>
  20. </div>
  21. <div v-if="point >= detailItem.point">
  22. <div class="color-666 mb-5 mt-10">{{ detailItem?.type ? '收货人信息填写' : '联系电话填写' }}</div>
  23. <CtForm ref="CtFormRef" :items="formItems"></CtForm>
  24. </div>
  25. <div v-if="point < detailItem.point" class="text-end color-error font-size-14 cursor-pointer text-decoration-underline" @click="emit('toTaskCenter')">积分不足,快去赚取积分吧</div>
  26. </Dialog>
  27. <Loading :visible="loading" />
  28. </template>
  29. <script setup>
  30. defineOptions({name: 'mall-exchange'})
  31. import { ref } from 'vue'
  32. import { getToken } from '@/utils/auth'
  33. import Dialog from '@/components/CtDialog'
  34. import Snackbar from '@/plugins/snackbar'
  35. import { redeemSubmit } from '@/api/pointsExchange'
  36. import { useUserStore } from '@/store/user'
  37. import { getDict } from '@/hooks/web/useDictionaries'
  38. import { checkPersonBaseInfo } from '@/utils/check'
  39. import dialogExtend from '@/plugins/dialogExtend'
  40. const emit = defineEmits(['login', 'toTaskCenter'])
  41. defineProps({
  42. point: {
  43. type: Number,
  44. default: 0
  45. }
  46. })
  47. const CtFormRef = ref()
  48. const formItems = ref({
  49. options: [
  50. {
  51. type: 'text',
  52. key: 'contactName',
  53. value: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '',
  54. hide: false,
  55. label: '收货人姓名 *',
  56. rules: [v => !!v || '请输入收货人姓名'],
  57. },
  58. {
  59. type: 'text',
  60. key: 'contactPhone',
  61. value: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')).phone : '',
  62. label: '收货人联系电话 *',
  63. outlined: true,
  64. rules: [v => !!v || '请填写收货人联系电话']
  65. },
  66. {
  67. type: 'cascade',
  68. key: 'address',
  69. value: [],
  70. label: '收货地址',
  71. itemText: 'name',
  72. itemValue: 'name',
  73. required: true,
  74. clearable: false,
  75. emitPath: true,
  76. items: []
  77. },
  78. {
  79. type: 'textarea',
  80. key: 'contactAddress',
  81. value: '',
  82. hide: false,
  83. label: '收货详细地址 *',
  84. rules: [ v => !!v || '请填写收货详细地址' ]
  85. }
  86. ]
  87. })
  88. // 数据
  89. const dataList = ref([
  90. { name: '房券-高端酒店房券', point: 12000, url: 'https://minio.menduner.com/dev/menduner/hotalRoomVoucher.png', type: 1 },
  91. { name: '门墩儿酒店英语学习年卡', point: 8000, url: 'https://minio.menduner.com/dev/153f0e8f5b36490a121882bce86f56fb7d56048e4b19eefdb4963084499b6e55.jpg', type: 0 },
  92. { name: '红酒-经典年份葡萄酒', point: 5000, url: 'https://minio.menduner.com/dev/menduner/redWine.png', type: 1 },
  93. { name: '瑞幸咖啡券-瑞幸咖啡精致享受券', point: 2000, url: 'https://minio.menduner.com/dev/menduner/coffee.png', type: 0 },
  94. { name: '减压捏捏乐', point: 500, url: 'https://minio.menduner.com/dev/menduner/pinchMusic.png', type: 1 }
  95. ])
  96. // 期望城市、其它感兴趣的城市
  97. getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
  98. data = data?.length && data || []
  99. formItems.value.options.find(e => e.key === 'address').items = data
  100. })
  101. // 详情说明弹窗
  102. const showDetail = ref(false)
  103. const detailItem = ref({})
  104. const handleShowDetail = (item) =>{
  105. if (!getToken()) {
  106. Snackbar.warning('请先登录')
  107. emit('login')
  108. return
  109. }
  110. if (!checkPersonBaseInfo()) { // 强制填写个人信息
  111. dialogExtend('necessaryInfoDialog').then(() => {
  112. handleShowDetail()
  113. })
  114. return
  115. }
  116. detailItem.value = item
  117. formItems.value.options.forEach(e => {
  118. if (e.key !== 'contactPhone') e.hide = item.type ? false : true
  119. })
  120. showDetail.value = true
  121. }
  122. // 兑换提交
  123. const loading = ref(false)
  124. const handleSubmit = async () =>{
  125. const { valid } = await CtFormRef.value.formRef.validate()
  126. if (!valid) return
  127. const obj = {
  128. ...detailItem.value
  129. }
  130. formItems.value.options.forEach(e => {
  131. if (!e.hide) obj[e.key] = e.value
  132. })
  133. if (obj.type && (!obj.address || !obj.address.length)) return Snackbar.warning('请选择收货地址')
  134. if (obj.type) obj.contactAddress = obj.address.join('') + obj.contactAddress
  135. delete obj.address
  136. if (!obj.contactName) obj.contactName = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '--'
  137. loading.value = true
  138. try {
  139. await redeemSubmit(obj)
  140. Snackbar.success('提交成功')
  141. showDetail.value = false
  142. detailItem.value = {}
  143. await useUserStore().getUserAccountInfo()
  144. } finally {
  145. loading.value = false
  146. }
  147. }
  148. </script>
  149. <style lang="scss" scoped>
  150. .listBox {
  151. display: flex;
  152. flex-wrap: wrap;
  153. }
  154. .ellipsis {
  155. width: 100%;
  156. }
  157. .tips {
  158. margin-top: 20px;
  159. padding: 12px 20px;
  160. border-radius: 5px;
  161. background-color: var(--default-bgc);
  162. // width: 400px;
  163. div {
  164. font-size: 15px;
  165. margin-bottom: 4px;
  166. }
  167. span {
  168. display: inline-block;
  169. font-size: 14px;
  170. margin-bottom: 20px;
  171. }
  172. }
  173. </style>