index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. <template>
  2. <view class="sign">
  3. <!-- #ifdef MP -->
  4. <NavBar titleText="签到" :iconColor="iconColor" :textColor="iconColor" :isScrolling="isScrolling" showBack></NavBar>
  5. <!-- #endif -->
  6. <view class="headerBg" :style="{ backgroundImage: `url(${imgHost}/statics/images/users/sign-bg.png)` }"></view>
  7. <view class="sign-content">
  8. <view class="continue">
  9. <view class="top">连续签到</view>
  10. <view class="bottom">
  11. <text class="num">{{ continuousSignDays }}</text>
  12. </view>
  13. </view>
  14. <view v-if="signRemindSwitch" class="remind">
  15. <view class="top">
  16. <switch :checked="!!signRemindStatus" color="#FAAD14" style="transform:scale(0.7)" @change="noticeChange" />
  17. </view>
  18. <view class="bottom">签到提醒</view>
  19. </view>
  20. <view class="total" :style="{ backgroundImage: `url(${imgHost}/statics/images/users/sign-total.png)` }">
  21. <view class="">
  22. <text class="num">{{ cumulativeSignDays }}</text>
  23. </view>
  24. <view class="bubble">
  25. <template v-for="(item,index) in bubble">
  26. <view v-if="item.value" :key="index" class="item">
  27. <view class="top" :style="{ backgroundImage: `url(${imgHost}/statics/images/users/sign-bubble.png)` }">{{ item.value }}</view>
  28. <view class="bottom">{{ item.name }}</view>
  29. </view>
  30. </template>
  31. </view>
  32. </view>
  33. <view class="look" @click="toggle('bottom')">
  34. <view class="inner">
  35. <text class="txt">查看我的签到日历</text>
  36. <text class="txt iconfont icon-ic_rightarrow"></text>
  37. </view>
  38. </view>
  39. <view :class="{ disabled: userInfo.is_day_sgin }" :style="{
  40. backgroundImage: userInfo.is_day_sgin ? `url(${imgHost}/statics/images/users/sign-disabled-btn-bg.png)` : `url(${imgHost}/statics/images/users/sign-btn-bg.png)`,
  41. }" class="now">
  42. <view v-if="userInfo.is_day_sgin" class="btn">签到成功</view>
  43. <view v-if="!userInfo.is_day_sgin" class="btn" @click="goSign">立即签到</view>
  44. <view v-if="!userInfo.is_day_sgin" class="hand" :style="{ backgroundImage: `url(${imgHost}/statics/images/users/hand.png)` }"></view>
  45. </view>
  46. <view class="board">
  47. <view class="board-inner acea-row row-middle">
  48. <scroll-view class="scroll-view" scroll-x="true">
  49. <view v-for="(item,index) in signList" :ley="index" class="item">
  50. <view class="top">
  51. <image v-if="item.is_sign" src="../static/sign-today.png" class="image"></image>
  52. <image v-else-if="item.type == 1" src="../../../static/images/sign-icon-01.png" class="image"></image>
  53. <image v-else-if="item.type == 2" src="../../../static/images/sign-icon-02.png" class="image"></image>
  54. </view>
  55. <view class="bottom">{{ item.day }}</view>
  56. </view>
  57. </scroll-view>
  58. </view>
  59. </view>
  60. <view class="record">
  61. <view class="acea-row row-between head">
  62. <view class="">签到记录</view>
  63. <navigator class="link" url="/pages/users/user_sgin_list/index" hover-class="none">
  64. 更多
  65. <text class="iconfont icon-ic_rightarrow"></text>
  66. </navigator>
  67. </view>
  68. <view class="body" v-if="signRecord.length">
  69. <view v-for="(item,index) in signRecord" :key="index" class="acea-row item">
  70. <image src="../static/sign-record.png" class="image"></image>
  71. <view class="text">
  72. <view class="">{{ item.title }}</view>
  73. <view class="time">{{ item.add_time }}</view>
  74. </view>
  75. <text class="num" :style="{
  76. backgroundImage: item.number ? 'url(../../../static/images/sign-icon-01.png)' : 'url(../../../static/images/sign-icon-02.png)'
  77. }">+{{item.number || item.exp_num}}</text>
  78. </view>
  79. </view>
  80. <emptyPage title="暂无签到记录~" v-else></emptyPage>
  81. </view>
  82. </view>
  83. <uni-popup ref="popup" background-color="rgba(255,255,255,0)" @change="change">
  84. <view class="popup-content">
  85. <view class="daynumber">
  86. 已连续签到
  87. <text class="sum-count">{{ sumCount }}</text>
  88. </view>
  89. <base-calendar v-if="calendarVisible" :yearMonth="targetDate" :dataSource="signData" @dateChange="getSignCalendar" @clickChange="clickSign"></base-calendar>
  90. </view>
  91. </uni-popup>
  92. </view>
  93. </template>
  94. <script>
  95. import {
  96. mapGetters
  97. } from 'vuex';
  98. import {
  99. postSignUser,
  100. getSignConfig,
  101. setSignIntegral,
  102. signRemind,
  103. getSignList,
  104. getSignCalendar
  105. } from '@/api/user.js';
  106. import BaseCalendar from '@/components/BaseCalendar.vue';
  107. import emptyPage from '@/components/emptyPage.vue';
  108. import {
  109. HTTP_REQUEST_URL
  110. } from '@/config/app';
  111. // #ifdef MP
  112. import NavBar from '@/components/NavBar.vue';
  113. import {
  114. openSignSubscribe
  115. } from '@/utils/SubscribeMessage.js';
  116. // #endif
  117. export default {
  118. components: {
  119. BaseCalendar,
  120. emptyPage,
  121. // #ifdef MP
  122. NavBar,
  123. // #endif
  124. },
  125. data() {
  126. return {
  127. imgHost: HTTP_REQUEST_URL,
  128. continuousSignDays: 0,
  129. cumulativeSignDays: 0,
  130. bubble: [],
  131. signList: [],
  132. userInfo: {},
  133. signRecord: [],
  134. signRemindStatus: 0,
  135. signRemindSwitch: 0,
  136. // 日历组件数据
  137. targetDate: parseInt(new Date().getFullYear()) + '-' + parseInt(new Date().getMonth() + 1), //本月
  138. sumCount: 0,
  139. signData: [],
  140. // #ifdef MP
  141. iconColor: '#333333',
  142. isScrolling: false,
  143. // #endif
  144. calendarVisible: false,
  145. };
  146. },
  147. computed: mapGetters(['isLogin']),
  148. onLoad() {
  149. if (this.isLogin) {
  150. this.getUserInfo();
  151. this.getSignSysteam();
  152. this.getSignList();
  153. //获取当前用户当前任务的签到状态
  154. this.getSignCalendar(this.targetDate);
  155. }
  156. },
  157. onPageScroll(e) {
  158. // #ifdef MP
  159. if (e.scrollTop > 50) {
  160. this.isScrolling = true;
  161. this.iconColor = '#333333';
  162. } else if (e.scrollTop < 50) {
  163. this.isScrolling = false;
  164. this.iconColor = '#FFFFFF';
  165. }
  166. // #endif
  167. },
  168. methods: {
  169. // 日历签到弹窗
  170. toggle(type) {
  171. // open 方法传入参数 等同在 uni-popup 组件上绑定 type属性
  172. this.$refs.popup.open(type);
  173. this.getSignCalendar(this.targetDate);
  174. },
  175. // 弹窗状态改变
  176. change(e) {
  177. console.log('当前模式:' + e.type + ',状态:' + e.show);
  178. this.calendarVisible = e.show
  179. },
  180. // 日历操作
  181. clickSign(day) {
  182. this.signData.push(day);
  183. this.sumCount++;
  184. },
  185. //当模板的时候可以直接引入,然后触发子组件事件到父界面去控制数据
  186. //获取当前用户该任务的签到数组
  187. getData(yearAndMonth) {
  188. let y = yearAndMonth.split('-')[0];
  189. let m = yearAndMonth.split('-')[1];
  190. //this.$http.postHttp("Comment/GetRecord", {//可以通过后台接口去获取你的打卡数据
  191. // Year: y,
  192. // Month: m,
  193. // }, (res) => {
  194. this.sumCount = 88; //res.SumCount
  195. if (yearAndMonth === this.targetDate) {
  196. //这是我造的假数据!!!
  197. const num = ['1', '04', '5', '13', '14', '15'],
  198. newSign = [],
  199. today = new Date().getDate();
  200. for (let i = 0; i < 6; i++) {
  201. if (parseInt(num[i]) > today) {
  202. // 过滤掉今天之后的日子
  203. break;
  204. }
  205. newSign.push(this.targetDate + '-' + num[i]);
  206. }
  207. // console.log(newSign);//-------------最后传给组件的格式看这里
  208. this.signData = newSign;
  209. } else {
  210. this.signData = [];
  211. }
  212. // })
  213. },
  214. getSignSysteam: function() {
  215. getSignConfig().then((res) => {
  216. const {
  217. continuousSignDays,
  218. cumulativeSignDays,
  219. signRemindStatus,
  220. signRemindSwitch,
  221. signData,
  222. signList,
  223. signMode,
  224. } = res.data;
  225. let weekday = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
  226. const day = new Date().getDay();
  227. weekday[day ? day - 1 : 6] = '今天';
  228. if (signMode == 1) {
  229. signList.forEach((item, index) => {
  230. item.day = weekday[index];
  231. });
  232. }
  233. this.signList = signList;
  234. this.continuousSignDays = continuousSignDays;
  235. this.cumulativeSignDays = cumulativeSignDays;
  236. this.signRemindStatus = !!signRemindStatus;
  237. this.signRemindSwitch = signRemindSwitch;
  238. let name = '';
  239. let value = '';
  240. let bubble = Object.keys(signData).map((key) => {
  241. name = '';
  242. value = signData[key];
  243. switch (key) {
  244. case 'sign_point':
  245. name = '签到领积分';
  246. break;
  247. case 'sign_exp':
  248. name = '签到领经验';
  249. break;
  250. }
  251. return {
  252. name,
  253. value
  254. };
  255. });
  256. this.bubble = bubble;
  257. });
  258. },
  259. getUserInfo: function() {
  260. let that = this;
  261. postSignUser({
  262. sign: 1
  263. }).then((res) => {
  264. that.$set(that, 'userInfo', res.data);
  265. });
  266. },
  267. goSign: function(e) {
  268. let that = this,
  269. sum_sgin_day = that.userInfo.sum_sgin_day;
  270. if (that.userInfo.is_day_sgin)
  271. return this.$util.Tips({
  272. title: '您今日已签到!'
  273. });
  274. setSignIntegral()
  275. .then((res) => {
  276. this.$util.Tips({
  277. title: res.data.sign_exp ? `获得${res.data.sign_exp}经验` : `获得${res.data.sign_point}积分`
  278. });
  279. that.getSignSysteam();
  280. that.getSignList();
  281. this.getUserInfo();
  282. this.getSignCalendar(this.targetDate);
  283. }).catch(err => {
  284. return this.$util.Tips({
  285. title: err
  286. });
  287. });
  288. },
  289. noticeChange(e) {
  290. // #ifdef MP
  291. openSignSubscribe().then(() => {
  292. this.signRemind(Number(e.detail.value));
  293. });
  294. // #endif
  295. // #ifndef MP
  296. this.signRemind(Number(e.detail.value));
  297. // #endif
  298. },
  299. signRemind(value) {
  300. signRemind(value).then((res) => {
  301. this.$util.Tips({
  302. title: `签到提醒${value ? '开启' : '关闭'}`
  303. });
  304. });
  305. },
  306. getSignList: function() {
  307. let that = this;
  308. getSignList({
  309. page: 1,
  310. limit: 10
  311. }).then((res) => {
  312. that.$set(that, 'signRecord', res.data);
  313. });
  314. },
  315. getSignCalendar(yearAndMonth) {
  316. getSignCalendar({
  317. time: yearAndMonth
  318. }).then(res => {
  319. const {
  320. continuousSignDays,
  321. signList,
  322. } = res.data;
  323. this.sumCount = continuousSignDays;
  324. let signDayList = signList.filter(item => item.is_sign);
  325. let signDateList = signDayList.map(item => {
  326. return item.day.split('/')[1];
  327. });
  328. let signData = signDateList.map(item => {
  329. return yearAndMonth + '-' + item;
  330. });
  331. this.signData = signData;
  332. });
  333. },
  334. }
  335. };
  336. </script>
  337. <style lang="scss" scoped>
  338. .headerBg {
  339. position: absolute;
  340. top: 0;
  341. left: 0;
  342. width: 100%;
  343. height: 1056rpx;
  344. background-repeat: no-repeat;
  345. background-size: 100% 100%;
  346. }
  347. .sign-content {
  348. position: relative;
  349. padding-top: 44rpx;
  350. }
  351. .continue {
  352. position: absolute;
  353. top: 26rpx;
  354. left: 0;
  355. width: 112rpx;
  356. text-align: center;
  357. font-weight: 500;
  358. font-size: 20rpx;
  359. color: #981007;
  360. .top {
  361. height: 48rpx;
  362. border-radius: 24rpx 24rpx 0 0;
  363. background: linear-gradient(180deg, #fff4c7 0%, #f9efe6 97%);
  364. line-height: 48rpx;
  365. }
  366. .bottom {
  367. height: 58rpx;
  368. border-radius: 0 0 24rpx 24rpx;
  369. background: linear-gradient(90deg, #fbeffc 0%, #e7f9ff 100%);
  370. line-height: 58rpx;
  371. }
  372. .num {
  373. margin-right: 2rpx;
  374. font-size: 40rpx;
  375. }
  376. }
  377. .bubble {
  378. font-weight: 500;
  379. font-size: 22rpx;
  380. line-height: 30rpx;
  381. color: #4851a3;
  382. .item {
  383. position: absolute;
  384. white-space: nowrap;
  385. animation: kf 1.2s linear .8s infinite alternate;
  386. &:nth-child(1) {
  387. top: 190rpx;
  388. right: 378rpx;
  389. }
  390. &:nth-child(2) {
  391. top: 150rpx;
  392. left: 396rpx;
  393. .top {
  394. width: 105rpx;
  395. height: 108rpx;
  396. line-height: 105rpx;
  397. }
  398. }
  399. &:nth-child(3) {
  400. top: 46rpx;
  401. right: 312rpx;
  402. .top {
  403. width: 82rpx;
  404. height: 83rpx;
  405. line-height: 82rpx;
  406. }
  407. }
  408. &:nth-child(4) {
  409. top: 24rpx;
  410. left: 318rpx;
  411. .top {
  412. width: 80rpx;
  413. height: 83rpx;
  414. line-height: 80rpx;
  415. }
  416. }
  417. }
  418. .top {
  419. width: 96rpx;
  420. height: 99rpx;
  421. margin: 0 auto 4rpx;
  422. background-repeat: no-repeat;
  423. background-size: 100% 100%;
  424. text-align: center;
  425. font-size: 32rpx;
  426. line-height: 96rpx;
  427. color: #333333;
  428. }
  429. }
  430. .remind {
  431. position: absolute;
  432. top: 394rpx;
  433. right: 0;
  434. padding: 0 16rpx 12rpx 32rpx;
  435. border-radius: 46rpx 0 0 46rpx;
  436. background: linear-gradient(90deg, #fbeffc 0%, #e7f9ff 100%);
  437. box-shadow: inset 0rpx 2rpx 0rpx 0rpx #ffffff;
  438. .bottom {
  439. text-align: center;
  440. font-size: 22rpx;
  441. line-height: 30rpx;
  442. color: #981007;
  443. }
  444. }
  445. .total {
  446. position: relative;
  447. width: 378rpx;
  448. height: 275rpx;
  449. padding: 144rpx 88rpx 0;
  450. margin: 0 auto;
  451. // background-image: url('../static/sign-total.png');
  452. background-repeat: no-repeat;
  453. background-size: 100% 100%;
  454. text-align: center;
  455. font-weight: 600;
  456. font-size: 24rpx;
  457. line-height: 34rpx;
  458. color: #e93323;
  459. .num {
  460. font-size: 80rpx;
  461. line-height: 80rpx;
  462. }
  463. }
  464. .look {
  465. width: 296rpx;
  466. border: 1px solid transparent;
  467. border-radius: 24rpx;
  468. margin: 24rpx auto 0;
  469. background-image: linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.6));
  470. background-clip: border-box;
  471. transform: rotateZ(360deg);
  472. .inner {
  473. height: 44rpx;
  474. border-radius: 22rpx;
  475. background: linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.4) 100%);
  476. text-align: center;
  477. font-size: 22rpx;
  478. line-height: 44rpx;
  479. color: #333333;
  480. }
  481. .iconfont {
  482. font-size: 24rpx;
  483. }
  484. }
  485. .now {
  486. position: relative;
  487. width: 438rpx;
  488. height: 160rpx;
  489. padding: 15rpx 0 0 18rpx;
  490. margin: 43rpx auto 0;
  491. background-position: center;
  492. background-repeat: no-repeat;
  493. background-size: 100% 100%;
  494. &.disabled {
  495. .btn {
  496. color: #f5f5f5;
  497. }
  498. }
  499. .btn {
  500. width: 402rpx;
  501. height: 124rpx;
  502. text-align: center;
  503. font-weight: 500;
  504. font-size: 40rpx;
  505. line-height: 124rpx;
  506. color: #ffffff;
  507. }
  508. .hand {
  509. position: absolute;
  510. top: 63rpx;
  511. right: 4rpx;
  512. width: 108rpx;
  513. height: 116rpx;
  514. background-position: left top;
  515. background-repeat: no-repeat;
  516. background-size: contain;
  517. }
  518. }
  519. .board {
  520. border-radius: 24rpx;
  521. margin: 30rpx 32rpx 0;
  522. border: 1px solid transparent;
  523. background: linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.8));
  524. background-clip: border-box;
  525. overflow: hidden;
  526. .board-inner {
  527. background: linear-gradient(-90deg, #f1fbfd 0%, #ffe8f5 100%);
  528. }
  529. .scroll-view {
  530. white-space: nowrap;
  531. box-sizing: border-box;
  532. text-align: center;
  533. }
  534. .item {
  535. display: inline-flex;
  536. flex-direction: column;
  537. justify-content: center;
  538. width: 64rpx;
  539. height: 160rpx;
  540. text-align: center;
  541. font-weight: 500;
  542. font-size: 22rpx;
  543. line-height: 30rpx;
  544. color: #3d3d3d;
  545. +.item {
  546. margin-left: 30rpx;
  547. }
  548. &:last-child {
  549. .top::after {
  550. display: none;
  551. }
  552. .top {
  553. transform: scale(1.3);
  554. }
  555. }
  556. }
  557. .top {
  558. position: relative;
  559. z-index: 1;
  560. width: 48rpx;
  561. height: 48rpx;
  562. margin: 0 auto 16rpx;
  563. font-weight: 500;
  564. font-size: 22rpx;
  565. line-height: 30rpx;
  566. color: #3d3d3d;
  567. &::after {
  568. content: '';
  569. position: absolute;
  570. top: 50%;
  571. left: 50%;
  572. z-index: 1;
  573. width: 92rpx;
  574. height: 2rpx;
  575. border-top: 2rpx dashed #e93323;
  576. }
  577. .image {
  578. position: relative;
  579. z-index: 2;
  580. width: 100%;
  581. height: 100%;
  582. }
  583. }
  584. }
  585. .record {
  586. border-radius: 24rpx;
  587. margin: 32rpx 20rpx;
  588. background: #ffffff;
  589. .head {
  590. padding: 32rpx 32rpx 20rpx;
  591. font-weight: 500;
  592. font-size: 32rpx;
  593. line-height: 44rpx;
  594. color: #222222;
  595. .link {
  596. font-weight: 400;
  597. font-size: 24rpx;
  598. color: #999999;
  599. }
  600. .iconfont {
  601. font-size: 24rpx;
  602. }
  603. }
  604. .body {
  605. .item {
  606. padding: 20rpx 34rpx 22rpx 24rpx;
  607. }
  608. .image {
  609. width: 80rpx;
  610. height: 80rpx;
  611. }
  612. .text {
  613. flex: 1;
  614. padding-left: 16rpx;
  615. font-size: 28rpx;
  616. line-height: 40rpx;
  617. color: #333333;
  618. }
  619. .time {
  620. margin-top: 8rpx;
  621. font-size: 24rpx;
  622. line-height: 34rpx;
  623. color: #999999;
  624. }
  625. .num {
  626. padding-right: 44rpx;
  627. margin-top: 10rpx;
  628. background-image: url('../static/sign-coin.png');
  629. background-position: right top;
  630. background-repeat: no-repeat;
  631. background-size: 36rpx 36rpx;
  632. font-size: 32rpx;
  633. line-height: 44rpx;
  634. color: #e93323;
  635. }
  636. }
  637. }
  638. // 日历
  639. .daynumber {
  640. color: #333333;
  641. padding: 34rpx 0 0 32rpx;
  642. font-size: 36rpx;
  643. font-weight: 500;
  644. .sum-count {
  645. font-size: 44rpx;
  646. font-family: D-DIN-PRO-SemiBold, D-DIN-PRO;
  647. font-weight: 600;
  648. color: #e93323;
  649. padding: 0 4rpx;
  650. }
  651. }
  652. .popup-content {
  653. position: relative;
  654. z-index: 2;
  655. background-color: #fff;
  656. border-radius: 30rpx 30rpx 0 0;
  657. &::before {
  658. content: "";
  659. position: absolute;
  660. top: 0;
  661. right: 0;
  662. left: 0;
  663. z-index: -1;
  664. height: 248rpx;
  665. background: linear-gradient(to right, #FEE3DE, #B5ECF8);
  666. filter: blur(92px);
  667. }
  668. }
  669. @keyframes kf {
  670. 0% {
  671. transform: translateY(8rpx);
  672. }
  673. 100% {
  674. transform: translateY(0);
  675. }
  676. }
  677. </style>