Przeglądaj źródła

每日签到功能

urbanu 1 miesiąc temu
rodzic
commit
951c1d2e45
7 zmienionych plików z 155 dodań i 65 usunięć
  1. 45 0
      package-lock.json
  2. 1 0
      package.json
  3. 8 7
      src/api/user.ts
  4. 12 6
      src/locales/en.json
  5. 10 4
      src/locales/vi.json
  6. 10 4
      src/locales/zh.json
  7. 69 44
      src/views/sign-in/index.vue

+ 45 - 0
package-lock.json

@@ -8,6 +8,7 @@
       "name": "demo",
       "version": "1.0.1",
       "dependencies": {
+        "@vueuse/core": "^14.1.0",
         "axios": "^1.5.1",
         "bignumber.js": "^9.3.1",
         "dayjs": "^1.11.18",
@@ -2349,6 +2350,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.21",
+      "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
+      "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
+      "license": "MIT"
+    },
     "node_modules/@vant/popperjs": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/@vant/popperjs/-/popperjs-1.3.0.tgz",
@@ -2681,6 +2688,44 @@
         }
       }
     },
+    "node_modules/@vueuse/core": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.1.0.tgz",
+      "integrity": "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.21",
+        "@vueuse/metadata": "14.1.0",
+        "@vueuse/shared": "14.1.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.0"
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.1.0.tgz",
+      "integrity": "sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.1.0.tgz",
+      "integrity": "sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.0"
+      }
+    },
     "node_modules/@xn-sakina/rml-darwin-arm64": {
       "version": "2.5.1",
       "resolved": "https://registry.npmjs.org/@xn-sakina/rml-darwin-arm64/-/rml-darwin-arm64-2.5.1.tgz",

+ 1 - 0
package.json

@@ -18,6 +18,7 @@
     "build-prod": "vite build --mode production"
   },
   "dependencies": {
+    "@vueuse/core": "^14.1.0",
     "axios": "^1.5.1",
     "bignumber.js": "^9.3.1",
     "dayjs": "^1.11.18",

+ 8 - 7
src/api/user.ts

@@ -100,11 +100,11 @@ export function requestBindSocial(data: { type: string; account: string }): Prom
 
 // 获取签到信息
 export function requestGetSignInfo(): Promise<CommonResponse<{
-  signedDays: number;
-  monthSignedDays: number;
-  todayReward: string;
-  isSigned: boolean;
-  signList: { day: number; reward: string; signed: boolean }[];
+  continuousDays: number;
+  totalDays: number;
+  todaySigned: boolean;
+  signDates: string[];
+  rewards: { day: number; reward: number }[];
 }>> {
   return http.request({
     url: "/api/v1/dt/sign/info",
@@ -114,8 +114,9 @@ export function requestGetSignInfo(): Promise<CommonResponse<{
 
 // 每日签到
 export function requestDailySignIn(): Promise<CommonResponse<{
-  signDay: number;
-  rewardAmount: string;
+  reward: number;
+  continuousDays: number;
+  totalDays: number;
 }>> {
   return http.request({
     url: "/api/v1/dt/sign/do",

+ 12 - 6
src/locales/en.json

@@ -187,13 +187,19 @@
   },
   "signIn": {
     "dailySignIn": "Daily Sign In",
-    "signedDays": "Signed {count} days",
-    "targetDays": "Target {count} days",
-    "todayReward": "Today Reward",
+    "continuousDays": "Consecutive Days",
+    "totalDays": "Total Days",
+    "day": "Day ",
+    "todayReward": "Today's Reward",
     "signInNow": "Sign In Now",
-    "signed": "Signed",
-    "consecutiveDays": "Day {count}",
-    "signInSuccess": "Sign in success, reward +{amount}"
+    "signed": "Signed In",
+    "signInSuccess": "Sign in success, reward +{amount} USDT",
+    "signInFailed": "Sign in failed",
+    "rules": "Sign-in Rules",
+    "rule1": "Sign in daily to earn rewards",
+    "rule2": "Consecutive sign-ins increase rewards (8-day cycle)",
+    "rule3": "Missing a day resets your streak",
+    "rule4": "Rewards credited instantly"
   },
   "notice": {
     "messageCenter": "Message Center",

+ 10 - 4
src/locales/vi.json

@@ -187,13 +187,19 @@
   },
   "signIn": {
     "dailySignIn": "Điểm danh hàng ngày",
-    "signedDays": "Đã điểm danh {count} ngày",
-    "targetDays": "Mục tiêu {count} ngày",
+    "continuousDays": "Ngày liên tiếp",
+    "totalDays": "Tổng ngày",
+    "day": "Ngày ",
     "todayReward": "Thưởng hôm nay",
     "signInNow": "Điểm danh ngay",
     "signed": "Đã điểm danh",
-    "consecutiveDays": "Ngày {count}",
-    "signInSuccess": "Điểm danh thành công, thưởng +{amount}"
+    "signInSuccess": "Điểm danh thành công, thưởng +{amount} USDT",
+    "signInFailed": "Điểm danh thất bại",
+    "rules": "Quy tắc điểm danh",
+    "rule1": "Điểm danh hàng ngày để nhận thưởng",
+    "rule2": "Điểm danh liên tiếp tăng phần thưởng (chu kỳ 8 ngày)",
+    "rule3": "Bỏ lỡ một ngày sẽ đặt lại số ngày liên tiếp",
+    "rule4": "Phần thưởng được cộng ngay lập tức"
   },
   "notice": {
     "messageCenter": "Trung tâm tin nhắn",

+ 10 - 4
src/locales/zh.json

@@ -187,13 +187,19 @@
   },
   "signIn": {
     "dailySignIn": "每日签到",
-    "signedDays": "已签到 {count} 天",
-    "targetDays": "目标 {count} 天",
+    "continuousDays": "连续签到",
+    "totalDays": "累计签到",
+    "day": "第",
     "todayReward": "今日奖励",
     "signInNow": "立即签到",
     "signed": "已签到",
-    "consecutiveDays": "第 {count} 天",
-    "signInSuccess": "签到成功,奖励 +{amount}"
+    "signInSuccess": "签到成功,奖励 +{amount} USDT",
+    "signInFailed": "签到失败",
+    "rules": "签到规则",
+    "rule1": "每日签到可获得奖励",
+    "rule2": "连续签到奖励递增(8天一周期)",
+    "rule3": "断签后连续天数重置",
+    "rule4": "奖励即时到账"
   },
   "notice": {
     "messageCenter": "消息中心",

+ 69 - 44
src/views/sign-in/index.vue

@@ -10,33 +10,33 @@
     <!-- 签到统计 -->
     <div class="sign-stats">
       <div class="stats-item">
-        <span class="value">{{ signInfo?.signedDays || 0 }}</span>
-        <span class="label">{{ $t('signIn.signedDays', { count: signInfo?.signedDays || 0 }) }}</span>
+        <span class="value">{{ signInfo?.continuousDays || 0 }}</span>
+        <span class="label">{{ $t('signIn.continuousDays') }}</span>
       </div>
       <div class="stats-divider"></div>
       <div class="stats-item">
-        <span class="value">{{ signInfo?.targetDays || 7 }}</span>
-        <span class="label">{{ $t('signIn.targetDays', { count: signInfo?.targetDays || 7 }) }}</span>
+        <span class="value">{{ signInfo?.totalDays || 0 }}</span>
+        <span class="label">{{ $t('signIn.totalDays') }}</span>
       </div>
     </div>
 
-    <!-- 签到日历 -->
+    <!-- 签到日历 (8天循环) -->
     <div class="sign-calendar">
       <div
         class="calendar-day"
-        v-for="day in 7"
-        :key="day"
+        v-for="item in rewardList"
+        :key="item.day"
         :class="{
-          signed: day <= (signInfo?.signedDays || 0),
-          today: day === (signInfo?.signedDays || 0) + 1 && !signInfo?.todaySigned
+          signed: isSignedDay(item.day),
+          today: isTodayDay(item.day)
         }"
       >
         <div class="day-icon">
-          <van-icon v-if="day <= (signInfo?.signedDays || 0)" name="success" />
+          <van-icon v-if="isSignedDay(item.day)" name="success" />
           <van-icon v-else name="gift-o" />
         </div>
-        <div class="day-text">{{ $t('signIn.consecutiveDays', { count: day }) }}</div>
-        <div class="day-reward">+{{ getReward(day) }}</div>
+        <div class="day-text">{{ $t('signIn.day') }}{{ item.day }}</div>
+        <div class="day-reward">+{{ item.reward }}</div>
       </div>
     </div>
 
@@ -62,54 +62,83 @@
 
     <!-- 签到规则 -->
     <div class="sign-rules">
-      <div class="rules-title">Sign-in Rules</div>
+      <div class="rules-title">{{ $t('signIn.rules') }}</div>
       <div class="rules-content">
-        <p>1. Sign in every day to earn rewards</p>
-        <p>2. Consecutive sign-ins increase your rewards</p>
-        <p>3. Missing a day will reset your streak</p>
-        <p>4. Rewards are credited to your balance instantly</p>
+        <p>1. {{ $t('signIn.rule1') }}</p>
+        <p>2. {{ $t('signIn.rule2') }}</p>
+        <p>3. {{ $t('signIn.rule3') }}</p>
+        <p>4. {{ $t('signIn.rule4') }}</p>
       </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted } from "vue";
+import { ref, computed, onMounted } from "vue";
 import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { showToast } from "vant";
-import { requestSignInfo, requestSignIn } from "@/api/user";
+import { requestGetSignInfo, requestDailySignIn } from "@/api/user";
 
 const { t } = useI18n();
 const router = useRouter();
 
-const signInfo = ref<SignInfo | null>(null);
+interface SignInfoData {
+  continuousDays: number;
+  totalDays: number;
+  todaySigned: boolean;
+  signDates: string[];
+  rewards: { day: number; reward: number }[];
+}
+
+const signInfo = ref<SignInfoData | null>(null);
 const signing = ref(false);
 
-const rewards = [0.1, 0.2, 0.3, 0.5, 0.8, 1.0, 2.0];
+// 奖励列表(8天一周期)
+const rewardList = computed(() => {
+  return signInfo.value?.rewards || [
+    { day: 1, reward: 0.10 },
+    { day: 2, reward: 0.20 },
+    { day: 3, reward: 0.40 },
+    { day: 4, reward: 0.80 },
+    { day: 5, reward: 1.60 },
+    { day: 6, reward: 3.20 },
+    { day: 7, reward: 6.40 },
+    { day: 8, reward: 12.80 },
+  ];
+});
+
+// 判断某天是否已签到(基于连续天数在8天循环内的位置)
+const isSignedDay = (day: number) => {
+  const continuousDays = signInfo.value?.continuousDays || 0;
+  const cycleDay = continuousDays % 8 || (continuousDays > 0 ? 8 : 0);
+  return day <= cycleDay && signInfo.value?.todaySigned;
+};
 
-const getReward = (day: number) => {
-  return rewards[day - 1] || 0.1;
+// 判断是否是今天要签到的天数
+const isTodayDay = (day: number) => {
+  if (signInfo.value?.todaySigned) return false;
+  const continuousDays = signInfo.value?.continuousDays || 0;
+  const nextDay = (continuousDays % 8) + 1;
+  return day === nextDay;
 };
 
+// 获取今日奖励
 const getTodayReward = () => {
-  const nextDay = (signInfo.value?.signedDays || 0) + 1;
-  return getReward(Math.min(nextDay, 7));
+  const continuousDays = signInfo.value?.continuousDays || 0;
+  const nextDayIndex = continuousDays % 8;
+  const rewards = rewardList.value;
+  return rewards[nextDayIndex]?.reward || 0.10;
 };
 
 const getSignInfo = async () => {
   try {
-    const res = await requestSignInfo();
+    const res = await requestGetSignInfo();
     if (res.code === 200) {
-      signInfo.value = res.data;
+      signInfo.value = res.data as any;
     }
   } catch (e) {
-    signInfo.value = {
-      signedDays: 3,
-      targetDays: 7,
-      todaySigned: false,
-      todayReward: 0.5
-    };
+    console.error('Failed to get sign info:', e);
   }
 };
 
@@ -118,21 +147,17 @@ const handleSignIn = async () => {
 
   signing.value = true;
   try {
-    const res = await requestSignIn();
+    const res = await requestDailySignIn();
     if (res.code === 200) {
-      const reward = res.data?.reward || getTodayReward();
+      const reward = (res.data as any)?.reward || getTodayReward();
       showToast(t('signIn.signInSuccess', { amount: reward }));
       getSignInfo();
     } else {
-      showToast(res.msg || 'Failed');
+      showToast(res.msg || t('signIn.signInFailed'));
     }
   } catch (e) {
-    const reward = getTodayReward();
-    showToast(t('signIn.signInSuccess', { amount: reward }));
-    if (signInfo.value) {
-      signInfo.value.todaySigned = true;
-      signInfo.value.signedDays++;
-    }
+    console.error('Sign in error:', e);
+    showToast(t('signIn.signInFailed'));
   }
   signing.value = false;
 };
@@ -201,8 +226,8 @@ onMounted(() => {
 
 .sign-calendar {
   display: grid;
-  grid-template-columns: repeat(7, 1fr);
-  gap: 8px;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 10px;
   padding: 0 16px;
 
   .calendar-day {