Prechádzať zdrojové kódy

fix:越南语、印尼语要加上
福利任务没有后退键
邀请好友二维码没生成

urbanu 1 mesiac pred
rodič
commit
c6bbe64784

+ 178 - 13
package-lock.json

@@ -17,6 +17,7 @@
         "normalize.css": "^8.0.1",
         "pinia": "^3.0.3",
         "pinia-plugin-persistedstate": "^4.3.0",
+        "qrcode": "^1.5.4",
         "vant": "^4.7.2",
         "vue": "^3.3.6",
         "vue-clipboard3": "^2.0.0",
@@ -2950,7 +2951,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -2960,7 +2960,6 @@
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
       "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "color-convert": "^2.0.1"
@@ -3483,7 +3482,6 @@
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -3711,7 +3709,6 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
       "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "color-name": "~1.1.4"
@@ -3724,7 +3721,6 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/colorette": {
@@ -4548,7 +4544,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
       "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -4746,6 +4741,12 @@
         "node": ">=0.3.1"
       }
     },
+    "node_modules/dijkstrajs": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+      "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
+      "license": "MIT"
+    },
     "node_modules/dom-serializer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -4993,7 +4994,6 @@
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/emojis-list": {
@@ -5940,7 +5940,6 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": "6.* || 8.* || >= 10.*"
@@ -7087,7 +7086,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -8913,7 +8911,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
       "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -9056,6 +9053,15 @@
         }
       }
     },
+    "node_modules/pngjs": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
+      "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
     "node_modules/posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -9252,6 +9258,150 @@
         "teleport": ">=0.2.0"
       }
     },
+    "node_modules/qrcode": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
+      "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+      "license": "MIT",
+      "dependencies": {
+        "dijkstrajs": "^1.0.1",
+        "pngjs": "^5.0.0",
+        "yargs": "^15.3.1"
+      },
+      "bin": {
+        "qrcode": "bin/qrcode"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/qrcode/node_modules/cliui": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+      "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^6.2.0"
+      }
+    },
+    "node_modules/qrcode/node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "license": "MIT",
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/qrcode/node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qrcode/node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/y18n": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+      "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+      "license": "ISC"
+    },
+    "node_modules/qrcode/node_modules/yargs": {
+      "version": "15.4.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+      "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^6.0.0",
+        "decamelize": "^1.2.0",
+        "find-up": "^4.1.0",
+        "get-caller-file": "^2.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^2.0.0",
+        "set-blocking": "^2.0.0",
+        "string-width": "^4.2.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^18.1.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/qrcode/node_modules/yargs-parser": {
+      "version": "18.1.3",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+      "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+      "license": "ISC",
+      "dependencies": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/qs": {
       "version": "6.14.0",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
@@ -9699,7 +9849,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -9715,6 +9864,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "license": "ISC"
+    },
     "node_modules/resolve": {
       "version": "1.22.10",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -10011,6 +10166,12 @@
         "semver": "bin/semver.js"
       }
     },
+    "node_modules/set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+      "license": "ISC"
+    },
     "node_modules/set-function-length": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -10759,7 +10920,6 @@
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "emoji-regex": "^8.0.0",
@@ -10860,7 +11020,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -13145,6 +13304,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/which-module": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
+      "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+      "license": "ISC"
+    },
     "node_modules/which-typed-array": {
       "version": "1.1.19",
       "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
     "normalize.css": "^8.0.1",
     "pinia": "^3.0.3",
     "pinia-plugin-persistedstate": "^4.3.0",
+    "qrcode": "^1.5.4",
     "vant": "^4.7.2",
     "vue": "^3.3.6",
     "vue-clipboard3": "^2.0.0",

+ 7 - 1
src/components/LangPopover/index.vue

@@ -75,9 +75,15 @@ defineExpose({
   background-clip: content-box;
   padding: 0;
   color: var(---, #8a8a8e);
-  height: 40px;
+  height: auto;
+  min-height: 40px;
   margin: 4px;
 
+  .van-popover-action__text {
+    text-align: center;
+    justify-content: center;
+  }
+
   .van-hairline--bottom {
     &::after {
       display: none;

+ 4 - 1
src/locales/en.json

@@ -6,10 +6,12 @@
     "cancel": "Cancel",
     "submit": "Submit",
     "save": "Save",
+    "saveSuccess": "Saved successfully",
     "delete": "Delete",
     "edit": "Edit",
     "copy": "Copy",
     "copySuccess": "Copy Success",
+    "error": "Operation failed",
     "all": "All",
     "more": "More",
     "back": "Back",
@@ -241,7 +243,8 @@
     "news": "News",
     "announcement": "Announcement",
     "noMessages": "No messages",
-    "markAllRead": "Mark All Read"
+    "markAllRead": "Mark All Read",
+    "allMarkedRead": "All marked as read"
   },
   "rank": {
     "leaderboard": "Leaderboard",

+ 244 - 3
src/locales/id.json

@@ -1,4 +1,245 @@
 {
-  "首页": "Home",
-  "资产": "assets"
-}
+  "common": {
+    "loading": "Memuat...",
+    "noData": "Tidak ada data",
+    "confirm": "Konfirmasi",
+    "cancel": "Batal",
+    "submit": "Kirim",
+    "save": "Simpan",
+    "saveSuccess": "Berhasil disimpan",
+    "delete": "Hapus",
+    "edit": "Ubah",
+    "copy": "Salin",
+    "copySuccess": "Berhasil disalin",
+    "error": "Operasi gagal",
+    "all": "Semua",
+    "more": "Lebih banyak",
+    "back": "Kembali",
+    "done": "Selesai",
+    "retry": "Coba lagi",
+    "yes": "Ya",
+    "no": "Tidak"
+  },
+  "tab": {
+    "home": "Beranda",
+    "hall": "Aula",
+    "gameShow": "Game",
+    "rank": "Peringkat",
+    "mine": "Saya"
+  },
+  "auth": {
+    "login": "Masuk",
+    "register": "Daftar",
+    "logout": "Keluar",
+    "phone": "Nomor telepon",
+    "email": "Email",
+    "password": "Kata sandi",
+    "confirmPassword": "Konfirmasi kata sandi",
+    "verifyCode": "Kode verifikasi",
+    "getCode": "Dapatkan kode",
+    "inviteCode": "Kode undangan",
+    "inviteCodeOptional": "Kode undangan (Opsional)",
+    "forgotPassword": "Lupa kata sandi?",
+    "resetPassword": "Atur ulang kata sandi",
+    "newPassword": "Kata sandi baru",
+    "loginSuccess": "Berhasil masuk",
+    "registerSuccess": "Berhasil mendaftar",
+    "pleaseInputPhone": "Masukkan nomor telepon",
+    "pleaseInputEmail": "Masukkan email",
+    "pleaseInputPassword": "Masukkan kata sandi",
+    "pleaseInputConfirmPassword": "Konfirmasi kata sandi",
+    "pleaseInputVerifyCode": "Masukkan kode verifikasi",
+    "passwordNotMatch": "Kata sandi tidak cocok",
+    "phoneLogin": "Masuk dengan Telepon",
+    "emailLogin": "Masuk dengan Email",
+    "smsLogin": "Masuk dengan SMS",
+    "thirdPartyLogin": "Masuk pihak ketiga",
+    "noAccount": "Belum punya akun?",
+    "hasAccount": "Sudah punya akun?",
+    "goRegister": "Daftar sekarang",
+    "goLogin": "Masuk sekarang",
+    "agreeTerms": "Saya setuju dengan",
+    "termsOfService": "Ketentuan Layanan",
+    "and": "dan",
+    "privacyPolicy": "Kebijakan Privasi",
+    "or": "atau",
+    "googleLogin": "Masuk dengan Google",
+    "zaloLogin": "Masuk dengan Zalo",
+    "telegramLogin": "Masuk dengan Telegram",
+    "telegramLoginFailed": "Gagal masuk Telegram",
+    "telegramNotConfigured": "Login Telegram tidak dikonfigurasi",
+    "zaloLoginFailed": "Gagal masuk Zalo",
+    "zaloNotConfigured": "Login Zalo tidak dikonfigurasi",
+    "logoutConfirm": "Apakah Anda yakin ingin keluar?"
+  },
+  "home": {
+    "checkIn": "Absen",
+    "welfareTask": "Tugas Bonus",
+    "invitationTask": "Tugas Undangan",
+    "permanentIncome": "Penghasilan Permanen",
+    "taskCenter": "Pusat Tugas",
+    "recommendedTasks": "Direkomendasikan",
+    "taskList": "Daftar Tugas",
+    "platformIssuedEarnings": "Penghasilan dikeluarkan",
+    "currentlyCompletedTasks": "Tugas yang diselesaikan",
+    "unit": "Unit"
+  },
+  "task": {
+    "taskDetail": "Detail Tugas",
+    "taskPrice": "Hadiah",
+    "taskDifficulty": "Kesulitan",
+    "easy": "Mudah",
+    "normal": "Normal",
+    "hard": "Sulit",
+    "remaining": "Tersisa",
+    "claimed": "Diklaim",
+    "deadline": "Batas waktu",
+    "reviewTime": "Waktu peninjauan",
+    "taskRequirements": "Persyaratan",
+    "taskSteps": "Langkah-langkah",
+    "imageMaterial": "Materi gambar",
+    "videoMaterial": "Materi video",
+    "submitRequirements": "Persyaratan submit",
+    "claimTask": "Ambil tugas",
+    "goSubmit": "Sudah diambil, klik untuk submit",
+    "viewProgress": "Lihat progress",
+    "completed": "Selesai",
+    "viewReason": "Lihat alasan",
+    "reClaim": "Ambil lagi",
+    "soldOut": "Habis",
+    "uploadScreenshot": "Unggah screenshot",
+    "uploadAtLeast": "Unggah minimal {count} gambar",
+    "remark": "Catatan",
+    "remarkPlaceholder": "Catatan opsional",
+    "submitTask": "Submit tugas",
+    "abandonTask": "Batalkan tugas",
+    "abandonConfirm": "Yakin ingin membatalkan tugas ini?",
+    "submitSuccess": "Berhasil submit",
+    "claimSuccess": "Berhasil mengambil"
+  },
+  "taskStatus": {
+    "all": "Semua",
+    "inProgress": "Sedang berjalan",
+    "pendingReview": "Menunggu review",
+    "completed": "Selesai",
+    "rejected": "Ditolak",
+    "abandoned": "Dibatalkan"
+  },
+  "material": {
+    "materialCenter": "Pusat Materi",
+    "image": "Gambar",
+    "text": "Teks",
+    "video": "Video",
+    "download": "Unduh",
+    "searchMaterial": "Cari materi"
+  },
+  "user": {
+    "myWallet": "Dompet Saya",
+    "points": "Poin",
+    "withdraw": "Tarik",
+    "security": "Keamanan",
+    "messages": "Pesan",
+    "records": "Riwayat",
+    "customerService": "Layanan Pelanggan",
+    "aboutUs": "Tentang Kami",
+    "materialCenter": "Pusat Materi",
+    "personalInfo": "Info Pribadi",
+    "avatar": "Avatar",
+    "nickname": "Nama panggilan",
+    "bindPhone": "Hubungkan telepon",
+    "bindEmail": "Hubungkan email",
+    "realName": "Nama asli",
+    "idCard": "KTP",
+    "changePassword": "Ubah kata sandi",
+    "oldPassword": "Kata sandi lama",
+    "paymentAccount": "Akun pembayaran"
+  },
+  "finance": {
+    "balance": "Saldo",
+    "totalWithdraw": "Total penarikan",
+    "totalIncome": "Total pendapatan",
+    "withdrawAmount": "Jumlah penarikan",
+    "minWithdraw": "Minimum: {amount}",
+    "withdrawFee": "Biaya: {rate}%",
+    "actualAmount": "Jumlah diterima",
+    "selectAccount": "Pilih akun",
+    "withdrawRecord": "Riwayat penarikan",
+    "incomeRecord": "Riwayat pendapatan",
+    "transactionRecord": "Riwayat transaksi",
+    "taskIncome": "Pendapatan tugas",
+    "commissionIncome": "Pendapatan komisi",
+    "referralBonus": "Bonus referral",
+    "withdrawReturn": "Pengembalian penarikan",
+    "withdrawDeduct": "Potongan penarikan",
+    "pending": "Tertunda",
+    "approved": "Disetujui",
+    "rejected": "Ditolak",
+    "withdrawSuccess": "Permintaan penarikan telah dikirim"
+  },
+  "team": {
+    "inviteFriends": "Undang Teman",
+    "myTeam": "Tim Saya",
+    "teamIncome": "Pendapatan Tim",
+    "myInviteCode": "Kode undangan saya",
+    "promotionLink": "Link promosi",
+    "saveQRCode": "Simpan QR Code",
+    "copyLink": "Salin link",
+    "inviteRewardDesc": "Undang teman untuk mendapatkan hadiah",
+    "directReferral": "Referral langsung",
+    "todayNew": "Hari ini",
+    "monthNew": "Bulan ini",
+    "totalMembers": "Total anggota",
+    "todayCommission": "Komisi hari ini",
+    "monthCommission": "Komisi bulan ini",
+    "totalCommission": "Total komisi",
+    "memberList": "Daftar anggota",
+    "joinTime": "Tanggal bergabung",
+    "contribution": "Kontribusi"
+  },
+  "signIn": {
+    "dailySignIn": "Absen Harian",
+    "continuousDays": "Hari berturut-turut",
+    "totalDays": "Total hari",
+    "day": "Hari ",
+    "todayReward": "Hadiah hari ini",
+    "signInNow": "Absen sekarang",
+    "signed": "Sudah absen",
+    "signInSuccess": "Berhasil absen, hadiah +{amount} USDT",
+    "signInFailed": "Gagal absen",
+    "rules": "Aturan absen",
+    "rule1": "Absen setiap hari untuk mendapatkan hadiah",
+    "rule2": "Absen berturut-turut meningkatkan hadiah (siklus 8 hari)",
+    "rule3": "Melewatkan satu hari akan mereset hari berturut-turut",
+    "rule4": "Hadiah langsung dikreditkan"
+  },
+  "notice": {
+    "messageCenter": "Pusat Pesan",
+    "systemNotice": "Notifikasi sistem",
+    "news": "Berita",
+    "announcement": "Pengumuman",
+    "noMessages": "Tidak ada pesan",
+    "markAllRead": "Tandai sudah dibaca",
+    "allMarkedRead": "Semua ditandai sudah dibaca"
+  },
+  "rank": {
+    "leaderboard": "Papan Peringkat",
+    "taskRank": "Peringkat tugas",
+    "inviteRank": "Peringkat undangan",
+    "daily": "Harian",
+    "weekly": "Mingguan",
+    "monthly": "Bulanan",
+    "myRank": "Peringkat saya",
+    "completedCount": "Diselesaikan"
+  },
+  "contact": {
+    "contactUs": "Hubungi Kami",
+    "ourService": "Layanan Kami",
+    "service247": "Dukungan 24/7",
+    "technicalSupport": "Dukungan teknis",
+    "onlineConsult": "Konsultasi online",
+    "emailSupport": "Dukungan email",
+    "socialMedia": "Media sosial",
+    "contactEmail": "Email kontak",
+    "contactTelegram": "Telegram kontak"
+  }
+}

+ 4 - 1
src/locales/vi.json

@@ -6,10 +6,12 @@
     "cancel": "Hủy",
     "submit": "Gửi",
     "save": "Lưu",
+    "saveSuccess": "Lưu thành công",
     "delete": "Xóa",
     "edit": "Sửa",
     "copy": "Sao chép",
     "copySuccess": "Sao chép thành công",
+    "error": "Thao tác thất bại",
     "all": "Tất cả",
     "more": "Xem thêm",
     "back": "Quay lại",
@@ -216,7 +218,8 @@
     "news": "Tin tức",
     "announcement": "Công bố",
     "noMessages": "Không có tin nhắn",
-    "markAllRead": "Đánh dấu đã đọc"
+    "markAllRead": "Đánh dấu đã đọc",
+    "allMarkedRead": "Đã đánh dấu tất cả đã đọc"
   },
   "rank": {
     "leaderboard": "Bảng xếp hạng",

+ 4 - 1
src/locales/zh.json

@@ -6,10 +6,12 @@
     "cancel": "取消",
     "submit": "提交",
     "save": "保存",
+    "saveSuccess": "保存成功",
     "delete": "删除",
     "edit": "编辑",
     "copy": "复制",
     "copySuccess": "复制成功",
+    "error": "操作失败",
     "all": "全部",
     "more": "更多",
     "back": "返回",
@@ -241,7 +243,8 @@
     "news": "新闻",
     "announcement": "公告",
     "noMessages": "暂无消息",
-    "markAllRead": "全部已读"
+    "markAllRead": "全部已读",
+    "allMarkedRead": "已全部标记为已读"
   },
   "rank": {
     "leaderboard": "排行榜",

+ 8 - 11
src/store/modules/langStore.js

@@ -6,27 +6,24 @@ import { Locale } from "vant";
 
 import zhCN from "vant/es/locale/lang/zh-CN";
 import enUS from "vant/es/locale/lang/en-US";
+import viVN from "vant/es/locale/lang/vi-VN";
+import idID from "vant/es/locale/lang/id-ID";
 
 let langObj = {
   zhCN,
-  enUS
+  enUS,
+  viVN,
+  idID
 };
 
 export const useLangStore = defineStore("langStore", () => {
   const { locale } = useI18n();
 
   const langList = ref([
-    // { text: "繁体中文", key: "zhHK", vantUiLangKey: "zhHK" },
     { text: "简体中文", key: "zh", vantUiLangKey: "zhCN" },
-    { text: "English", key: "en", vantUiLangKey: "enUS" }
-    // { text: "繁体", key: "zhHK", vantUiLangKey: "zhHK" },
-    // { text: "Malaysian", key: "ms", vantUiLangKey: "enUS" },
-    // { text: "Indonesia", key: "id", vantUiLangKey: "idID" },
-    // { text: "Tiếng Việt", key: "vi", vantUiLangKey: "viVN" },
-    // { text: "日本", key: "ja", vantUiLangKey: "jaJP" },
-    // { text: "한국인", key: "ko", vantUiLangKey: "koKR" },
-    // { text: "แบบไทย", key: "th", vantUiLangKey: "thTH" },
-    // { text: "हिंदी", key: "hi", vantUiLangKey: "hiIN" }
+    { text: "English", key: "en", vantUiLangKey: "enUS" },
+    { text: "Tiếng Việt", key: "vi", vantUiLangKey: "viVN" },
+    { text: "Indonesia", key: "id", vantUiLangKey: "idID" }
   ]);
 
   const getInitLangItem = () => {

+ 5 - 5
src/views/notice/index.vue

@@ -96,7 +96,7 @@ import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { showToast } from "vant";
 import dayjs from "dayjs";
-import { requestNotices, requestMarkNoticeRead } from "@/api/user";
+import { requestNotices, requestMarkNoticeRead, requestMarkAllNoticeRead } from "@/api/user";
 
 const { t } = useI18n();
 const router = useRouter();
@@ -195,12 +195,12 @@ const getNotices = async (isRefresh = false) => {
 
 const markAllRead = async () => {
   try {
-    await requestMarkNoticeRead({ all: true });
+    await requestMarkAllNoticeRead();
     noticeList.value.forEach(n => n.isRead = true);
-    showToast('All marked as read');
+    showToast(t('notice.allMarkedRead'));
   } catch (e) {
     noticeList.value.forEach(n => n.isRead = true);
-    showToast('All marked as read');
+    showToast(t('notice.allMarkedRead'));
   }
 };
 
@@ -210,7 +210,7 @@ const goDetail = async (item: NoticeInfo) => {
 
   if (!item.isRead) {
     try {
-      await requestMarkNoticeRead({ id: item.id });
+      await requestMarkNoticeRead(item.id);
       item.isRead = true;
     } catch (e) {
       item.isRead = true;

+ 40 - 2
src/views/task/list.vue

@@ -1,5 +1,12 @@
 <template>
   <div class="task-list-page">
+    <!-- 页面标题栏 -->
+    <div class="page-header">
+      <van-icon name="arrow-left" @click="goBack" />
+      <span>{{ pageTitle }}</span>
+      <span></span>
+    </div>
+
     <!-- 顶部搜索 -->
     <div class="search-bar">
       <van-search
@@ -69,14 +76,30 @@
 </template>
 
 <script setup lang="ts">
-import { ref, watch, onMounted } from "vue";
-import { useRouter } from "vue-router";
+import { ref, watch, computed, onMounted } from "vue";
+import { useRouter, useRoute } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { requestTaskList, requestTaskCategories } from "@/api/task";
 import defaultIcon from "@/assets/images/common/no-data.svg";
 
 const { t } = useI18n();
 const router = useRouter();
+const route = useRoute();
+
+// 根据路由参数判断页面类型
+const taskType = computed(() => route.query.type as string || '');
+
+// 页面标题
+const pageTitle = computed(() => {
+  if (taskType.value === 'welfare') {
+    return t('home.welfareTask');
+  }
+  return t('task.taskList');
+});
+
+const goBack = () => {
+  router.back();
+};
 
 const keyword = ref('');
 const categoryId = ref(0);
@@ -210,6 +233,21 @@ onMounted(() => {
   padding-bottom: 80px;
 }
 
+.page-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px;
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+
+  .van-icon {
+    font-size: 20px;
+    cursor: pointer;
+  }
+}
+
 .search-bar {
   padding: 12px 16px 0;
 

+ 66 - 17
src/views/team/invite.vue

@@ -28,9 +28,11 @@
     <!-- 二维码 -->
     <div class="qrcode-section">
       <div class="qrcode-box">
-        <div class="qrcode-placeholder" ref="qrcodeRef">
-          <van-icon name="qr" />
-          <span>QR Code</span>
+        <div class="qrcode-container">
+          <canvas ref="qrcodeCanvas"></canvas>
+          <div v-if="!qrcodeGenerated" class="qrcode-loading">
+            <van-loading size="24px" />
+          </div>
         </div>
       </div>
       <van-button type="default" block round @click="saveQRCode">
@@ -72,20 +74,22 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, onMounted } from "vue";
+import { ref, computed, onMounted, watch, nextTick } from "vue";
 import { useRouter } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { showToast } from "vant";
 import { useClipboard } from "@vueuse/core";
 import { useUserStore } from "@/store/modules/userStore";
 import { requestTeamInfo } from "@/api/team";
+import QRCode from "qrcode";
 
 const { t } = useI18n();
 const router = useRouter();
 const userStore = useUserStore();
 const { copy } = useClipboard();
 
-const qrcodeRef = ref<HTMLElement | null>(null);
+const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
+const qrcodeGenerated = ref(false);
 const inviteCode = ref('');
 const baseUrl = window.location.origin + window.location.pathname;
 
@@ -93,6 +97,33 @@ const inviteLink = computed(() => {
   return `${baseUrl}#/register?smid=${inviteCode.value}`;
 });
 
+// 生成二维码
+const generateQRCode = async () => {
+  if (!qrcodeCanvas.value || !inviteLink.value) return;
+
+  try {
+    await QRCode.toCanvas(qrcodeCanvas.value, inviteLink.value, {
+      width: 160,
+      margin: 2,
+      color: {
+        dark: '#000000',
+        light: '#ffffff'
+      }
+    });
+    qrcodeGenerated.value = true;
+  } catch (err) {
+    console.error('Failed to generate QR code:', err);
+  }
+};
+
+// 监听 inviteLink 变化,重新生成二维码
+watch(inviteLink, async (newLink) => {
+  if (newLink && inviteCode.value) {
+    await nextTick();
+    generateQRCode();
+  }
+});
+
 const getTeamInfo = async () => {
   try {
     const res = await requestTeamInfo();
@@ -137,7 +168,21 @@ const copyLink = async () => {
 };
 
 const saveQRCode = () => {
-  showToast('QR Code saved');
+  if (!qrcodeCanvas.value) {
+    showToast(t('common.error'));
+    return;
+  }
+
+  try {
+    const link = document.createElement('a');
+    link.download = `invite_qrcode_${inviteCode.value}.png`;
+    link.href = qrcodeCanvas.value.toDataURL('image/png');
+    link.click();
+    showToast(t('common.saveSuccess'));
+  } catch (err) {
+    console.error('Failed to save QR code:', err);
+    showToast(t('common.error'));
+  }
 };
 
 const goBack = () => {
@@ -152,8 +197,11 @@ const goTeamIncome = () => {
   router.push('/team-income');
 };
 
-onMounted(() => {
-  getTeamInfo();
+onMounted(async () => {
+  await getTeamInfo();
+  // 延迟生成二维码,确保 canvas 已渲染
+  await nextTick();
+  generateQRCode();
 });
 </script>
 
@@ -246,25 +294,26 @@ onMounted(() => {
     justify-content: center;
     margin-bottom: 16px;
 
-    .qrcode-placeholder {
+    .qrcode-container {
+      position: relative;
       width: 180px;
       height: 180px;
       display: flex;
-      flex-direction: column;
       align-items: center;
       justify-content: center;
       background: #fff;
       border-radius: 12px;
+      padding: 10px;
 
-      .van-icon {
-        font-size: 60px;
-        color: #333;
-        margin-bottom: 8px;
+      canvas {
+        display: block;
       }
 
-      span {
-        font-size: 14px;
-        color: #666;
+      .qrcode-loading {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
       }
     }
   }