prizeDraw.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <template>
  2. <div class="prizeDrawBox">
  3. <div class="d-flex flex-column align-center">
  4. <CtDialog :visible="showDialog" :widthType="3" :footer="false" titleClass="text-h6" title="房券抽奖" :closeable="false">
  5. <div class="d-flex flex-column align-center">
  6. <div class="numberBox mb-5">房券抽奖</div>
  7. <gridPage v-if="props.type === '1'" :lotteryId="props.lotteryId" @end="endCallback"></gridPage>
  8. <slotMachinePage v-if="props.type === '2'" :lotteryId="props.lotteryId" height="120" :class="{'mb-3': disabled}" @end="endCallback"></slotMachinePage>
  9. </div>
  10. </CtDialog>
  11. <v-card min-height="300" width="700" class="pa-5" :class="{'mt-3': !disabled}" style="position: relative;">
  12. <div v-if="disabled" class="color-warning text-center" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">活动获取失败,<span class="defaultLink" @click="contactUs">请联系管理员</span>。</div>
  13. <div v-if="isReceive" class="color-warning text-center" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">奖品领取成功,请前往<span class="defaultLink" @click="toMyPrize">我的奖品</span>查看。</div>
  14. <div v-if="showPrize">
  15. <p v-for="(k, i) in prizeData" :key="i" class="color-primary">
  16. {{ k.prize.prompt }}
  17. <span class="color-999">(10天内未领取的,则视为主动放弃当前奖品)</span>
  18. </p>
  19. <p>凭此房券在规定有效期内可享受免费住宿一晚。</p>
  20. <p class="mb-5">请提供收货地址,以便安排房券派送。</p>
  21. <!-- 收货地址 -->
  22. <div>
  23. <v-radio-group v-model="addressSelect" color="primary">
  24. <v-radio v-for="val in address" :key="val.id" :label="val.id === 9999 ? val.label : (val.name + ',' + val.mobile + ',' + val.areaName + val.detailAddress)" :value="val.id"></v-radio>
  25. </v-radio-group>
  26. <v-form v-if="addressSelect === 9999" ref="addressFormRef">
  27. <v-row>
  28. <v-col :cols="6">
  29. <v-text-field v-model="newAddress.name" label="收货人名称 *" color="primary" density="compact" variant="outlined"></v-text-field>
  30. </v-col>
  31. <v-col :cols="6">
  32. <v-text-field v-model="newAddress.mobile" :rules="phoneRules" label="收货人手机号码 *" color="primary" density="compact" variant="outlined"></v-text-field>
  33. </v-col>
  34. </v-row>
  35. <div class="d-flex" style="width: 100%;">
  36. <div class="mt-2" style="color: #777; width: 100px;">省市区 *</div>
  37. <el-cascader
  38. ref="cascaderAddr"
  39. v-model="newAddress.areaId"
  40. size="large"
  41. clearable
  42. class="mb-5"
  43. placeholder="省市区 *"
  44. style="flex: 1;"
  45. :props="{ value: 'id', label: 'name', emitPath: false }"
  46. :options="areaTreeData"
  47. @change="handleChangeArea(item)"
  48. ></el-cascader>
  49. </div>
  50. <v-text-field v-model="newAddress.detailAddress" label="详情地址 *" color="primary" density="compact" variant="outlined"></v-text-field>
  51. </v-form>
  52. <div class="text-center mt-3">
  53. <v-btn color="primary" class="elevation-5" width="180" @click.stop="handleSubmit">确 认</v-btn>
  54. </div>
  55. </div>
  56. </div>
  57. <!-- <div v-else class="color-warning text-center" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">请先进行抽奖</div> -->
  58. </v-card>
  59. </div>
  60. </div>
  61. </template>
  62. <script setup>
  63. defineOptions({ name: 'prizeDraw'})
  64. import gridPage from './prizeDraw/grid.vue'
  65. import slotMachinePage from './prizeDraw/slotMachine.vue'
  66. import { ref } from 'vue'
  67. import { getLuckLotteryRecordByOrderId } from '@/api/mall/prize'
  68. import { getMallUserAddressList } from '@/api/mall/address'
  69. import Snackbar from '@/plugins/snackbar'
  70. import { luckyLotteryRecordReceive } from '@/api/mall/prize'
  71. import { useRouter } from 'vue-router'
  72. import { getDict } from '@/hooks/web/useDictionaries'
  73. const emit = defineEmits(['success'])
  74. const props = defineProps({
  75. lotteryId: [Number, String],
  76. orderId: [Number, String],
  77. type: {
  78. type: String,
  79. default: '1'
  80. }
  81. })
  82. const showDialog = ref(false)
  83. const router = useRouter()
  84. const newAddress = ref({
  85. name: '',
  86. areaId: '',
  87. areaName: '',
  88. mobile: '',
  89. detailAddress: ''
  90. })
  91. const addressSelect = ref()
  92. const disabled = ref(false)
  93. const addressFormRef = ref()
  94. const cascaderAddr = ref()
  95. const areaTreeData = ref([])
  96. getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
  97. data = data?.length && data || []
  98. areaTreeData.value = data
  99. })
  100. const phoneRules = ref([
  101. value => {
  102. if (value) return true
  103. return t('login.mobileNumberPlaceholder')
  104. },
  105. value => {
  106. if (value?.length <= 11 && /^1[3456789]\d{9}$/.test(value)) return true
  107. return t('login.correctPhoneNumber')
  108. }
  109. ])
  110. // 地区选择
  111. const handleChangeArea = () => {
  112. const node = cascaderAddr.value.getCheckedNodes() ? cascaderAddr.value.getCheckedNodes()[0] : null
  113. if (!node) return
  114. newAddress.value.areaName = node.pathLabels.join(' ')
  115. }
  116. // 获取中奖记录、收货地址
  117. const isReceive = ref(false) // 是否已领取
  118. const address = ref([])
  119. const prizeData = ref({})
  120. const getRecord = async () => {
  121. const data = await getLuckLotteryRecordByOrderId(props.orderId)
  122. prizeData.value = data || []
  123. if (!data || !data.length) {
  124. disabled.value = true
  125. return
  126. }
  127. if (data[0].record?.isReceive) {
  128. isReceive.value = true
  129. return
  130. }
  131. const orderIds = localStorage.getItem('handleLotteryOrderIds')
  132. if (orderIds?.length && orderIds.includes(props.orderId)) {
  133. // 已经点击过抽奖按钮,直接展示奖品。例如刷新支付完成页面
  134. showPrize.value = true
  135. } else {
  136. // 刚进入支付完成页面
  137. showDialog.value = true
  138. }
  139. const addressData = await getMallUserAddressList()
  140. address.value = [...addressData, { id: 9999, label: '使用新地址' }] || []
  141. if (addressData && addressData.length) addressSelect.value = addressData[0].id
  142. }
  143. if (props.orderId) getRecord()
  144. const showPrize = ref(false)
  145. const endCallback = () => {
  146. const orderIds = JSON.parse(localStorage.getItem('handleLotteryOrderIds') || '[]')
  147. localStorage.setItem('handleLotteryOrderIds', JSON.stringify([...orderIds, props.orderId])) // 标记已抽奖
  148. showDialog.value = false
  149. showPrize.value = true
  150. }
  151. function checkValue(obj) {
  152. for (let key in obj) {
  153. if (obj.hasOwnProperty(key) && !obj[key]) {
  154. return false
  155. }
  156. }
  157. return true
  158. }
  159. // 领取
  160. const handleSubmit = async () => {
  161. let query = {}
  162. if (addressSelect.value === 9999) {
  163. if (!checkValue(newAddress.value)) return Snackbar.warning('请完善收货信息')
  164. query = newAddress.value
  165. } else query = address.value.find(item => item.id === addressSelect.value)
  166. await luckyLotteryRecordReceive({ id: prizeData.value[0].record.id, receiveInfo: JSON.stringify(query) })
  167. Snackbar.success('奖品领取成功,待商家发货')
  168. router.replace('/recruit/personal/personalCenter/tradeOrder?key=1')
  169. }
  170. const toMyPrize = () => {
  171. router.replace('/recruit/personal/personalCenter/tradeOrder?key=1')
  172. }
  173. const contactUs = () => {
  174. router.replace('/contactUs')
  175. }
  176. </script>
  177. <style scoped lang="scss">
  178. .prizeDrawBox {
  179. padding: 20px 40px;
  180. background-color: var(--default-bgc);
  181. }
  182. .prizeDraw {
  183. margin: 0 auto;
  184. }
  185. .numberBox {
  186. font-size: 20px;
  187. font-family: 'MiSans-Bold';
  188. padding: 2px 38px;
  189. border-bottom: 3px solid var(--v-primary-base);
  190. }
  191. .colorBase {
  192. color: var(--v-primary-base);
  193. margin: 0 6px;
  194. font-size: 22px;
  195. }
  196. </style>