index.vue 21 KB

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