index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. <template>
  2. <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
  3. <view class="custom_activities">
  4. <view class="info-card">
  5. <view class="content-title">
  6. <view v-t="'Home.page_customer.item6'"></view>
  7. </view>
  8. <!-- 主要内容 -->
  9. <view class="main-content">
  10. <scroll-view class="tab" scroll-y :scroll-top="scrollTop" @scrolltolower="loadMore"
  11. refresher-enabled :refresher-triggered="refreshing" @refresherrefresh="onRefresh">
  12. <!-- 加载状态 -->
  13. <view v-if="pictLoading" class="loading-mask">
  14. <view class="loading-content">
  15. <uni-icons type="spinner-cycle" size="30" color="#007aff" class="spin"></uni-icons>
  16. <text class="loading-text">{{ t('common.loading') }}</text>
  17. </view>
  18. </view>
  19. <view class="activity-list">
  20. <uni-row class="demo-uni-row uni-row1">
  21. <!-- 遍历静态活动配置 -->
  22. <ActivityCard v-for="activity in visibleStaticActivities" :key="activity.id"
  23. :config="activity" :state="activityState" :lang="locale"
  24. @action="handleActivityAction" />
  25. <!-- 动态活动列表 -->
  26. <ActivityCard v-for="item in tableData" :key="'dynamic-' + item.id"
  27. :config="createDynamicConfig(item)" :state="activityState" :lang="locale"
  28. @action="handleActivityAction" />
  29. <!-- 赠送活动列表 -->
  30. <ActivityCard v-for="item in tableDataGive" :key="'give-' + item.id"
  31. :config="createGiveConfig(item)" :state="activityState" :lang="locale"
  32. @action="handleActivityAction" />
  33. </uni-row>
  34. </view>
  35. <!-- 加载更多状态 -->
  36. <view v-if="loadingMore" class="loading-more">
  37. <uni-icons type="spinner-cycle" size="20" color="#999" class="spin"></uni-icons>
  38. <text>{{ t('common.loadingMore') }}</text>
  39. </view>
  40. <!-- 没有更多数据 -->
  41. <view v-if="!hasMore && tableData.length > 0" class="no-more">
  42. <text>{{ t('common.noMore') }}</text>
  43. </view>
  44. </scroll-view>
  45. </view>
  46. <!-- 分页 -->
  47. <!-- <view class="crm_pagination" v-if="pagerInfo.rowTotal">
  48. <view class="pagination">
  49. <view class="page-item prev" :class="{ disabled: pagerInfo.current === 1 }"
  50. @click="handlePrevPage">
  51. <uni-icons type="arrowleft" size="16" color="#666"></uni-icons>
  52. <text>{{ t('common.prev') }}</text>
  53. </view>
  54. <view class="page-numbers">
  55. <view v-for="page in visiblePages" :key="page" class="page-number"
  56. :class="{ active: pagerInfo.current === page }" @click="handlePageChange(page)">
  57. {{ page }}
  58. </view>
  59. </view>
  60. <view class="page-item next" :class="{ disabled: pagerInfo.current === pagerInfo.pageTotal }"
  61. @click="handleNextPage">
  62. <text>{{ t('common.next') }}</text>
  63. <uni-icons type="arrowright" size="16" color="#666"></uni-icons>
  64. </view>
  65. </view>
  66. </view> -->
  67. <!-- 所有弹窗组件 -->
  68. <ActivityDialogs :visible="dialogState" :form-data="formData" :login-options="loginOptions"
  69. :surplus-options="surplusOptions" @update:visible="updateDialogVisible"
  70. @action="handleDialogAction" />
  71. </view>
  72. </view>
  73. </cwg-page-wrapper>
  74. </template>
  75. <script setup lang="ts">
  76. import { ref, computed, onMounted } from 'vue'
  77. import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
  78. import { useI18n } from 'vue-i18n'
  79. import ActivityCard from './components/ActivityCard.vue'
  80. import ActivityDialogs from './components/ActivityDialogs.vue'
  81. import { useActivityData } from './composables/useActivityData'
  82. import { useActivityActions } from './composables/useActivityActions'
  83. import { staticActivityConfigs } from './config/activityConfigs'
  84. import Config from '@/config/index'
  85. const { t, locale } = useI18n()
  86. const { Host80 } = Config
  87. // ==================== 数据逻辑 ====================
  88. const {
  89. pictLoading,
  90. tableData,
  91. tableDataGive,
  92. pagerInfo,
  93. loginOptions,
  94. activityState,
  95. country,
  96. isSupportedCountry,
  97. isGuoQin,
  98. overdue,
  99. timeExpireJx,
  100. isAfterSeptember30,
  101. refreshAllActivities,
  102. handlePageChange
  103. } = useActivityData()
  104. // ==================== 操作逻辑 ====================
  105. const {
  106. // 弹窗状态
  107. dialogChinaUnionPay,
  108. dialogChinaUnionPay1,
  109. dialogChinaUnionPayJX,
  110. dialogDealResult,
  111. dialogDealResultJx,
  112. dialogDealResultJxVip,
  113. dialogDealResultCpt,
  114. dialogDealResultJxJYB,
  115. dialogDealResultNoWorries,
  116. dialogNoWorries,
  117. dialogNoWorriesApply,
  118. dialogNewTask,
  119. dialogNewTaskDraw,
  120. dialogSurplusActivity,
  121. dialogSurplusActivity1,
  122. openCalculatorFlag,
  123. // 表单数据
  124. dialogDealResult_form,
  125. dialogDealResultJx_form,
  126. dialogDealResultJx_formVip,
  127. dialogDealResultJx_formJYB,
  128. dialogDealResultCpt_form,
  129. dialogDeal_formNoWorries,
  130. selectedAccount,
  131. valueInfo,
  132. surplusActivityOptions,
  133. surplusActivityLoading,
  134. selectedSurplusActivity,
  135. // 页面跳转
  136. toSingle,
  137. toDeposit,
  138. toActivity24nianzhong,
  139. toActivity24Trading,
  140. toHistoryLuckyDraw,
  141. toTaskList,
  142. goSurplusTaskList,
  143. goMonthlyTaskList,
  144. toDocumentary,
  145. backActivity,
  146. // PDF打开
  147. openPdf,
  148. // 活动操作
  149. toApply23Open,
  150. toApply23,
  151. toApply23Cancel,
  152. toTransform,
  153. toTransformActive,
  154. toRealization,
  155. toRealizationActive,
  156. toApply23Jx,
  157. toApply23JxCancel,
  158. toApply23JxVip,
  159. toApply23JxCancelVip,
  160. toRealizationJx,
  161. toRealizationJxVip,
  162. toApplyCpt,
  163. toApplyCptCancel,
  164. toApply24JYBVip,
  165. toApply24JYBCancelVip,
  166. toRealization24JYBVip,
  167. toApplyNoWorries,
  168. toApplyNoWorriesCancel,
  169. realizationNoWorries,
  170. getSurplusActivityOptions,
  171. confirmSurplusActivity,
  172. confirmSurplusActivity1,
  173. closedialogNewTaskDraw,
  174. toOpenSingle,
  175. cashBack,
  176. calculateIncome
  177. } = useActivityActions(activityState, loginOptions, ref(''), refreshAllActivities)
  178. // ==================== 分页相关 ====================
  179. const refreshing = ref(false)
  180. const loadingMore = ref(false)
  181. const scrollTop = ref(0)
  182. const hasMore = ref(true)
  183. // 计算显示的页码
  184. const visiblePages = computed(() => {
  185. const maxVisible = 5
  186. const half = Math.floor(maxVisible / 2)
  187. let start = Math.max(1, pagerInfo.value.current - half)
  188. let end = Math.min(pagerInfo.value.pageTotal, start + maxVisible - 1)
  189. if (end - start + 1 < maxVisible) {
  190. start = Math.max(1, end - maxVisible + 1)
  191. }
  192. return Array.from({ length: end - start + 1 }, (_, i) => start + i)
  193. })
  194. // 上一页
  195. const handlePrevPage = () => {
  196. if (pagerInfo.value.current > 1) {
  197. handlePageChange(pagerInfo.value.current - 1)
  198. }
  199. }
  200. // 下一页
  201. const handleNextPage = () => {
  202. if (pagerInfo.value.current < pagerInfo.value.pageTotal) {
  203. handlePageChange(pagerInfo.value.current + 1)
  204. }
  205. }
  206. // 加载更多
  207. const loadMore = () => {
  208. if (hasMore.value && !loadingMore.value && pagerInfo.value.current < pagerInfo.value.pageTotal) {
  209. loadingMore.value = true
  210. handlePageChange(pagerInfo.value.current + 1)
  211. setTimeout(() => {
  212. loadingMore.value = false
  213. }, 500)
  214. }
  215. }
  216. // 刷新
  217. const onRefresh = () => {
  218. refreshing.value = true
  219. refreshAllActivities()
  220. setTimeout(() => {
  221. refreshing.value = false
  222. uni.stopPullDownRefresh()
  223. }, 1000)
  224. }
  225. // ==================== 可见活动计算 ====================
  226. const visibleStaticActivities = computed(() => {
  227. return staticActivityConfigs.filter(config => {
  228. if (!config.showCondition) return true
  229. switch (config.showCondition) {
  230. case 'tableDataNewYear24Flag':
  231. return activityState.tableDataNewYear24Flag
  232. case "country === 'CN'":
  233. return country.value === 'CN'
  234. case 'choujiaClose':
  235. return activityState.choujiaClose
  236. case 'tableDataNoWorriesFlag':
  237. return activityState.tableDataNoWorriesFlag
  238. case 'standard':
  239. return activityState.standard
  240. case 'tableData2Flag':
  241. return activityState.tableData2Flag
  242. case 'tableDataCptFlag':
  243. return activityState.tableDataCptFlag
  244. case 'tableData3FlagJYB':
  245. return activityState.tableData3FlagJYB
  246. case '!isAfterSeptember30() && !isSupportedCountry':
  247. return !isAfterSeptember30() && !isSupportedCountry.value
  248. case '!isAfterSeptember30() && isSupportedCountry':
  249. return !isAfterSeptember30() && isSupportedCountry.value
  250. default:
  251. return true
  252. }
  253. })
  254. })
  255. // ==================== 创建动态活动配置 ====================
  256. const createDynamicConfig = (item: any) => {
  257. return {
  258. id: item.id,
  259. title: item.title,
  260. description: item.subTitle,
  261. image: item.image,
  262. time: item.deliveryTime?.split(' ')[0],
  263. hot: item.hot,
  264. onClick: 'toSingle',
  265. onClickParams: [item.id],
  266. buttons: [
  267. {
  268. type: item.startTime && item.endTime && overdue(item.startTime, item.endTime) ? 'red' : 'gray',
  269. text: 'Custom.Activity.Apply',
  270. action: 'applications',
  271. params: [item]
  272. },
  273. {
  274. type: 'check',
  275. text: 'Custom.Activity.List',
  276. action: 'checkActivity',
  277. params: [item]
  278. },
  279. {
  280. type: 'check',
  281. text: 'Custom.Activity.Single',
  282. action: 'toSingle',
  283. params: [item.id]
  284. }
  285. ]
  286. }
  287. }
  288. const createGiveConfig = (item: any) => {
  289. return {
  290. id: item.id,
  291. title: item.title,
  292. description: item.subTitle,
  293. image: item.image,
  294. time: item.revokeDate?.split(' ')[0],
  295. hot: item.hot,
  296. onClick: 'toSingle',
  297. onClickParams: ['newList', item.id],
  298. buttons: [
  299. {
  300. type: item.valid === 1 ? 'red' : 'gray',
  301. text: 'Custom.Activity.Apply',
  302. action: 'toActivity24nianzhong'
  303. },
  304. {
  305. type: 'check',
  306. text: 'Custom.Activity.Single',
  307. action: 'toSingle',
  308. params: ['newList', item.id]
  309. }
  310. ]
  311. }
  312. }
  313. // ==================== 弹窗状态聚合 ====================
  314. const dialogState = {
  315. dialogChinaUnionPay,
  316. dialogChinaUnionPay1,
  317. dialogChinaUnionPayJX,
  318. dialogDealResult,
  319. dialogDealResultJx,
  320. dialogDealResultJxVip,
  321. dialogDealResultCpt,
  322. dialogDealResultJxJYB,
  323. dialogDealResultNoWorries,
  324. dialogNoWorries,
  325. dialogNoWorriesApply,
  326. dialogNewTask,
  327. dialogNewTaskDraw,
  328. dialogSurplusActivity,
  329. dialogSurplusActivity1,
  330. openCalculatorFlag
  331. }
  332. const formData = {
  333. dialogDealResult_form,
  334. dialogDealResultJx_form,
  335. dialogDealResultJx_formVip,
  336. dialogDealResultJx_formJYB,
  337. dialogDealResultCpt_form,
  338. dialogDeal_formNoWorries,
  339. selectedAccount,
  340. valueInfo,
  341. selectedSurplusActivity
  342. }
  343. const surplusOptions = {
  344. options: surplusActivityOptions,
  345. loading: surplusActivityLoading
  346. }
  347. // 更新弹窗可见性
  348. const updateDialogVisible = (key: string, value: boolean) => {
  349. const dialogMap: Record<string, any> = {
  350. dialogChinaUnionPay,
  351. dialogChinaUnionPay1,
  352. dialogChinaUnionPayJX,
  353. dialogDealResult,
  354. dialogDealResultJx,
  355. dialogDealResultJxVip,
  356. dialogDealResultCpt,
  357. dialogDealResultJxJYB,
  358. dialogDealResultNoWorries,
  359. dialogNoWorries,
  360. dialogNoWorriesApply,
  361. dialogNewTask,
  362. dialogNewTaskDraw,
  363. dialogSurplusActivity,
  364. dialogSurplusActivity1,
  365. openCalculatorFlag
  366. }
  367. if (dialogMap[key]) {
  368. dialogMap[key].value = value
  369. }
  370. }
  371. // 处理弹窗操作
  372. const handleDialogAction = (action: string, data?: any) => {
  373. switch (action) {
  374. case 'toApply23':
  375. toApply23()
  376. break
  377. case 'toApply23Cancel':
  378. toApply23Cancel()
  379. break
  380. case 'toTransformActive':
  381. toTransformActive()
  382. break
  383. // ... 其他操作
  384. }
  385. }
  386. // ==================== 活动操作处理 ====================
  387. const handleActivityAction = ({ type, params }: { type: string; params?: any[] }) => {
  388. switch (type) {
  389. case 'toSingle':
  390. toSingle(...(params || []))
  391. break
  392. case 'openCalculator':
  393. openCalculatorFlag.value = true
  394. break
  395. case 'openPdf':
  396. openPdf(params?.[0] || type)
  397. break
  398. case 'openSurplusActivityDialog':
  399. dialogSurplusActivity.value = true
  400. break
  401. case 'openSurplusActivityDialog1':
  402. dialogSurplusActivity1.value = true
  403. break
  404. case 'goMonthlyTaskList':
  405. goMonthlyTaskList()
  406. break
  407. case 'goSurplusTaskList':
  408. goSurplusTaskList()
  409. break
  410. case 'toHistoryLuckyDraw':
  411. toHistoryLuckyDraw()
  412. break
  413. case 'toOpenTask':
  414. dialogNewTaskDraw.value = true
  415. break
  416. case 'toNewTask':
  417. dialogNewTask.value = true
  418. break
  419. case 'toTaskList':
  420. toTaskList()
  421. break
  422. case 'toApplyNoWorriesOpen':
  423. dialogDealResultNoWorries.value = true
  424. break
  425. case 'toRealizationNoWorries':
  426. dialogNoWorries.value = true
  427. break
  428. case 'toActivity24Trading':
  429. toActivity24Trading()
  430. break
  431. case 'toActivity24nianzhong':
  432. toActivity24nianzhong()
  433. break
  434. case 'toApply23Open':
  435. toApply23Open()
  436. break
  437. case 'toTransform':
  438. toTransform()
  439. break
  440. case 'toRealization':
  441. toRealization()
  442. break
  443. case 'toApplyCptOpen':
  444. dialogDealResultCpt.value = true
  445. break
  446. case 'toOpenSingle':
  447. // 处理打开外部链接
  448. break
  449. case 'toApply24JYBOpenVip':
  450. dialogDealResultJxJYB.value = true
  451. break
  452. case 'toRealization24JYBVip':
  453. // 处理变现
  454. break
  455. case 'applications':
  456. // 处理申请
  457. break
  458. case 'checkActivity':
  459. // 处理查看活动
  460. break
  461. case 'openDialog':
  462. if (params?.[0]) {
  463. updateDialogVisible(params[0], true)
  464. }
  465. break
  466. case 'cashBack':
  467. // 处理返现
  468. break
  469. case 'toApply23JxOpen':
  470. dialogDealResultJx.value = true
  471. break
  472. case 'toApply23JxOpenVip':
  473. dialogDealResultJxVip.value = true
  474. break
  475. case 'toApplyCptOpen':
  476. dialogDealResultCpt.value = true
  477. break
  478. case 'toApply24JYBOpenVip':
  479. dialogDealResultJxJYB.value = true
  480. break
  481. case 'toApplyNoWorriesOpen':
  482. dialogDealResultNoWorries.value = true
  483. break
  484. case 'toRealizationJx':
  485. toRealizationJx()
  486. break
  487. case 'toRealizationJxVip':
  488. toRealizationJxVip()
  489. break
  490. case 'toRealization24JYBVip':
  491. toRealization24JYBVip()
  492. break
  493. case 'toRealizationNoWorries':
  494. dialogNoWorries.value = true
  495. break
  496. case 'toOpenSingle':
  497. toOpenSingle(params?.[0])
  498. break
  499. case 'cashBack':
  500. cashBack()
  501. break
  502. default:
  503. console.warn('未知操作类型:', type)
  504. }
  505. }
  506. // ==================== 生命周期 ====================
  507. onLoad(() => {
  508. refreshAllActivities()
  509. })
  510. onPullDownRefresh(() => {
  511. refreshAllActivities()
  512. setTimeout(() => {
  513. uni.stopPullDownRefresh()
  514. }, 1000)
  515. })
  516. onReachBottom(() => {
  517. loadMore()
  518. })
  519. </script>
  520. <style scoped lang="scss">
  521. @import "@/uni.scss";
  522. .custom_activities {
  523. display: flex;
  524. flex-direction: column;
  525. .crm-title-box {
  526. padding: 20rpx 30rpx;
  527. background-color: var(--color-white);
  528. border-bottom: 1rpx solid #e5e5e5;
  529. .tit {
  530. font-size: 32rpx;
  531. font-weight: bold;
  532. color: #333;
  533. }
  534. }
  535. .main-content {
  536. flex: 1;
  537. overflow: hidden;
  538. .tab {
  539. height: 100%;
  540. padding-top: px2rpx(20);
  541. }
  542. }
  543. .loading-mask {
  544. position: absolute;
  545. top: 0;
  546. left: 0;
  547. right: 0;
  548. bottom: 0;
  549. display: flex;
  550. align-items: center;
  551. justify-content: center;
  552. background-color: rgba(255, 255, 255, 0.9);
  553. z-index: 100;
  554. .loading-content {
  555. display: flex;
  556. flex-direction: column;
  557. align-items: center;
  558. padding: 30rpx;
  559. background-color: var(--color-white);
  560. border-radius: 16rpx;
  561. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
  562. .loading-text {
  563. margin-top: 20rpx;
  564. font-size: 28rpx;
  565. color: #666;
  566. }
  567. }
  568. }
  569. .loading-more,
  570. .no-more {
  571. display: flex;
  572. align-items: center;
  573. justify-content: center;
  574. padding: 30rpx;
  575. font-size: 28rpx;
  576. color: #999;
  577. }
  578. .spin {
  579. animation: spin 1s linear infinite;
  580. }
  581. @keyframes spin {
  582. from {
  583. transform: rotate(0deg);
  584. }
  585. to {
  586. transform: rotate(360deg);
  587. }
  588. }
  589. .crm_pagination {
  590. padding: 20rpx;
  591. background-color: var(--color-white);
  592. border-top: 1rpx solid #e5e5e5;
  593. .pagination {
  594. display: flex;
  595. align-items: center;
  596. justify-content: center;
  597. .page-item {
  598. display: flex;
  599. align-items: center;
  600. padding: 0 20rpx;
  601. height: 60rpx;
  602. background-color: var(--color-white);
  603. border: 1rpx solid #dcdfe6;
  604. border-radius: 8rpx;
  605. font-size: 28rpx;
  606. color: #606266;
  607. cursor: pointer;
  608. margin: 0 10rpx;
  609. &.disabled {
  610. opacity: 0.5;
  611. cursor: not-allowed;
  612. pointer-events: none;
  613. }
  614. }
  615. .page-numbers {
  616. display: flex;
  617. gap: 10rpx;
  618. .page-number {
  619. display: flex;
  620. align-items: center;
  621. justify-content: center;
  622. min-width: 60rpx;
  623. height: 60rpx;
  624. padding: 0 8rpx;
  625. background-color: var(--color-white);
  626. border: 1rpx solid #dcdfe6;
  627. border-radius: 8rpx;
  628. font-size: 28rpx;
  629. color: #606266;
  630. cursor: pointer;
  631. &.active {
  632. background-color: #007aff;
  633. border-color: #007aff;
  634. color: #fff;
  635. }
  636. }
  637. }
  638. }
  639. }
  640. .activity-list {
  641. display: flex;
  642. flex-direction: column;
  643. flex-wrap: wrap;
  644. gap: 20rpx;
  645. }
  646. }
  647. </style>