userCard.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div class="user-card" :class="{ 'user-card--goods': props.type === 'goods' }">
  3. <div class="user-card__main">
  4. <div class="user-card__stat">
  5. <span class="user-card__label">剩余乐跑次数</span>
  6. <a-statistic
  7. :value="userCount?.lepao_count ?? 0"
  8. :value-style="{ fontSize: '2rem', fontWeight: 700, color: 'var(--stat-color)' }"
  9. animation
  10. show-group-separator
  11. />
  12. </div>
  13. <div class="user-card__actions">
  14. <a-button
  15. v-if="props.type === 'lepao'"
  16. type="primary"
  17. size="large"
  18. class="action-btn action-btn--primary"
  19. @click="$router.push('/store/goodsList')"
  20. >
  21. <icon-fire /> 去购买
  22. </a-button>
  23. <a-button
  24. v-else
  25. type="primary"
  26. size="large"
  27. class="action-btn action-btn--primary"
  28. @click="$router.push('/lepao/accountList')"
  29. >
  30. <icon-thunderbolt /> 去乐跑
  31. </a-button>
  32. <a-button
  33. v-if="hasPermission('action.goods.sendCount')"
  34. size="large"
  35. class="action-btn"
  36. @click="SendCount"
  37. >
  38. <icon-gift /> 赠送
  39. </a-button>
  40. <a-button size="large" class="action-btn" @click="goSendCountRecords">
  41. <icon-list /> 记录
  42. </a-button>
  43. </div>
  44. </div>
  45. </div>
  46. <a-modal
  47. v-model:visible="visible"
  48. title="赠送乐跑次数"
  49. @cancel="handleCancel"
  50. @before-ok="handleBeforeOk"
  51. :ok-loading="ok_loading"
  52. unmount-on-close
  53. >
  54. <a-form :model="sendform" layout="vertical">
  55. <a-form-item field="username" label="接收人用户名">
  56. <a-input
  57. v-model="sendform.username"
  58. placeholder="如:用户adfg45g"
  59. :max-length="20"
  60. allow-clear
  61. />
  62. </a-form-item>
  63. <a-form-item field="count" label="赠送次数">
  64. <a-input-number v-model="sendform.count" :min="1" :max="9999" mode="button" />
  65. </a-form-item>
  66. </a-form>
  67. </a-modal>
  68. </template>
  69. <script setup>
  70. import { ref, reactive, onUnmounted, onMounted } from 'vue'
  71. import { useRouter } from 'vue-router'
  72. import { getCount, sendCount } from '@/api/goods'
  73. import { Notification, Message } from '@arco-design/web-vue'
  74. import { hasPermission } from '@/utils/permission'
  75. const props = defineProps({
  76. type: { type: String, default: 'lepao' }
  77. })
  78. const userCount = ref({ lepao_count: 0 })
  79. const router = useRouter()
  80. const visible = ref(false)
  81. const ok_loading = ref(false)
  82. const sendform = reactive({ username: '', count: 1 })
  83. const handleBeforeOk = async (done) => {
  84. if (!hasPermission('action.goods.sendCount')) {
  85. Notification.warning({ title: '无权限', content: '当前账号暂无赠送权限' })
  86. return false
  87. }
  88. try {
  89. ok_loading.value = true
  90. if (!sendform.username) {
  91. Message.error('请填写接收人用户名')
  92. return false
  93. }
  94. if (!sendform.count || sendform.count < 1 || sendform.count > 9999) {
  95. Notification.error({ title: '赠送失败', content: '赠送次数需在 1~9999 之间' })
  96. return false
  97. }
  98. const res = await sendCount({ username: sendform.username, count: sendform.count })
  99. if (!res || res.code !== 0) {
  100. Notification.error({ title: '赠送失败', content: res?.msg ?? '请稍后再试' })
  101. return false
  102. }
  103. Message.success(res.msg || '赠送成功')
  104. done()
  105. GetCount()
  106. } catch (error) {
  107. Notification.error({ title: '赠送失败', content: error.message || '请稍后再试' })
  108. return false
  109. } finally {
  110. ok_loading.value = false
  111. }
  112. }
  113. const handleCancel = () => {
  114. visible.value = false
  115. }
  116. const SendCount = () => {
  117. sendform.username = ''
  118. sendform.count = 1
  119. visible.value = true
  120. }
  121. const goSendCountRecords = () => router.push('/store/sendCountRecords')
  122. const GetCount = async () => {
  123. try {
  124. const res = await getCount()
  125. if (!res || res.code !== 0) return
  126. userCount.value = res.data
  127. } catch {
  128. /* 静默失败,避免轮询刷屏 */
  129. }
  130. }
  131. let timer = null
  132. const startPolling = () => {
  133. if (!timer) timer = setInterval(GetCount, 8000)
  134. }
  135. const stopPolling = () => {
  136. if (timer) {
  137. clearInterval(timer)
  138. timer = null
  139. }
  140. }
  141. onMounted(async () => {
  142. await GetCount()
  143. startPolling()
  144. })
  145. onUnmounted(stopPolling)
  146. </script>
  147. <style scoped lang="less">
  148. @import '@/styles/store-theme.less';
  149. .user-card {
  150. --stat-color: rgb(var(--primary-6));
  151. background: @store-card-bg;
  152. border: 1px solid @store-card-border;
  153. border-radius: @store-radius;
  154. box-shadow: @store-shadow;
  155. padding: 20px 24px;
  156. &--goods {
  157. background: linear-gradient(135deg, #f4faf6 0%, #e8f5ec 100%);
  158. --stat-color: @store-primary;
  159. }
  160. &__main {
  161. display: flex;
  162. flex-wrap: wrap;
  163. align-items: center;
  164. justify-content: space-between;
  165. gap: 20px;
  166. }
  167. &__label {
  168. display: block;
  169. font-size: 0.85rem;
  170. color: @store-text-muted;
  171. margin-bottom: 4px;
  172. }
  173. &__actions {
  174. display: flex;
  175. flex-wrap: wrap;
  176. gap: 10px;
  177. }
  178. }
  179. .action-btn {
  180. border-radius: 999px;
  181. &--primary {
  182. background: @store-primary !important;
  183. border-color: @store-primary !important;
  184. }
  185. }
  186. </style>