Explorar o código

Merge branch 'master' of https://git.nanodreamtech.com/wkw/wallet_app

吴sir hai 2 semanas
pai
achega
d3b9635e03

+ 2 - 2
.env

@@ -8,7 +8,7 @@ VITE_DEV_PATH='https://wallet.angeltokens.io'
 VITE_PRO_PATH='https://wallet.angeltokens.io'
 
 
-VITE_PRO_IM_PATH='http://192.168.0.59:8888'
-VITE_DEV_IM_PATH='http://192.168.0.59:8888'
+VITE_PRO_IM_PATH='https://nim.angeltokens.io'
+VITE_DEV_IM_PATH='https://nim.angeltokens.io'
 
 VITE_PRO_BACKEND_PATH='https://backend.angeltoken.net'

+ 2 - 1
.gitignore

@@ -29,4 +29,5 @@ coverage
 
 *.tsbuildinfo
 android
-ios
+ios
+bin

+ 8 - 0
README.md

@@ -86,4 +86,12 @@ ext {
     androidxEspressoCoreVersion = '3.6.1'
     cordovaAndroidVersion = '10.1.1'
 }
+```
+
+```
+{
+  "version": "1.0.2",               // 新 JS bundle 的版本号
+  "url": "https://yourdomain.com/updates/v1.0.2.zip", // 真实 ZIP 包地址
+  "minBinaryVersion": "1.0.0"       // 要求原生壳最低版本
+}
 ```

+ 2 - 0
android/app/capacitor.build.gradle

@@ -11,12 +11,14 @@ apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
 dependencies {
     implementation project(':aparajita-capacitor-biometric-auth')
     implementation project(':capacitor-app')
+    implementation project(':capacitor-barcode-scanner')
     implementation project(':capacitor-device')
     implementation project(':capacitor-inappbrowser')
     implementation project(':capacitor-local-notifications')
     implementation project(':capacitor-push-notifications')
     implementation project(':capacitor-status-bar')
     implementation project(':capacitor-toast')
+    implementation project(':capgo-capacitor-updater')
 
 }
 

+ 8 - 3
capacitor.config.ts

@@ -1,7 +1,7 @@
 import { CapacitorConfig } from '@capacitor/cli';
-// import { updateVersion } from './scripts/sync-version';
+import { updateVersion } from './scripts/sync-version';
 
-// updateVersion()
+updateVersion()
 
 
 
@@ -21,6 +21,11 @@ const config: CapacitorConfig = {
     // 注意:原JSON中嵌套了重复的"plugins"键,已修正
     PushNotifications: {
       presentationOptions: ['badge', 'sound', 'alert']
+    },
+
+    CapacitorUpdater: {
+      autoUpdate: false, // 自行控制 
+      statsUrl: '',   // 统计上报: POST /api/stats 接收 JSON(内容是插件的事件日志),返回 200 即可。
     }
   },
   // iOS专属配置
@@ -46,7 +51,7 @@ const config: CapacitorConfig = {
 // 开发服务器配置(热更新用)
 if (process.env.DAPP_BUILD != "1") {
   config.server = {
-    url: 'http://192.168.0.70:5173',
+    url: 'http://192.168.0.59:5173',
     cleartext: true,          // 允许HTTP明文通信(仅开发环境)
     allowNavigation: ['*']    // 允许任意URL导航
   }

+ 8 - 3
package.json

@@ -4,14 +4,15 @@
   "private": true,
   "type": "module",
   "scripts": {
-    "dev": "vite --host", 
+    "dev": "vite --host",
+    "updata": "node ./updataSetVersion.js",
     "s": "cross-env DAPP_BUILD=1 npx cap sync",
-    "a": "npx cap run android --live-reload --host=192.168.0.70 --port=5173",
+    "a": "npx cap run android --live-reload --host=192.168.0.59 --port=5173",
     "app": "npx cap run android --live-reload --host=192.168.0.70 --port=5173",
     "ios": "npx cap run ios --live-reload --host=192.168.0.59 --port=5173",
     "android": "npx cap add android && npx cap sync",
     "android:clean": "(cd android && ./gradlew clean)",
-    "build": "vite build",
+    "build": "vite build && npm run updata",
     "build:apk": "npm run s && cross-env DAPP_BUILD=1 npx cap copy  && (cd android && ./gradlew assembleRelease)",
     "build:apk:windows": "vite build && npx cap copy && cd android && .\\gradlew.bat build",
     "icon": "npx capacitor-assets generate --android",
@@ -21,6 +22,7 @@
     "@aparajita/capacitor-biometric-auth": "^9.0.0",
     "@capacitor/android": "^7.2.0",
     "@capacitor/app": "^7.0.1",
+    "@capacitor/barcode-scanner": "^2.0.3",
     "@capacitor/cli": "^7.2.0",
     "@capacitor/core": "^7.2.0",
     "@capacitor/device": "^7.0.1",
@@ -30,6 +32,7 @@
     "@capacitor/push-notifications": "^7.0.1",
     "@capacitor/status-bar": "^7.0.1",
     "@capacitor/toast": "^7.0.1",
+    "@capgo/capacitor-updater": "^7.8.7",
     "axios": "^1.10.0",
     "clipboard": "^2.0.11",
     "crypto-js": "^4.2.0",
@@ -40,6 +43,7 @@
     "pinia": "^3.0.1",
     "pinia-plugin-persistedstate": "^4.4.1",
     "qrcode.vue": "^3.6.0",
+    "semver": "^7.7.2",
     "sharp": "^0.34.2",
     "typescript": "^5.8.3",
     "vant": "^4.9.20",
@@ -54,6 +58,7 @@
     "@capacitor/assets": "^3.0.5",
     "@vitejs/plugin-vue": "^5.2.3",
     "@vitejs/plugin-vue-jsx": "^5.0.1",
+    "adm-zip": "^0.5.16",
     "code-inspector-plugin": "^0.20.12",
     "cordova-plugin-console": "^1.1.0",
     "cross-env": "^7.0.3",

+ 11 - 4
src/App.vue

@@ -12,15 +12,15 @@
   </div>
 </template>
 
-<script setup> 
-import { appStart } from "@/hooks/updataApp";
+<script setup>  
+import { checkAndUpdate } from "@/updater/index";
+
 import { getNotchHeight } from "@/utils/statusBar";
 const route = useRoute();
 const router = useRouter();
 
 const height = ref(0);
-const notchStyle = ref({});
-// const appMainViewStyle = ref({});
+const notchStyle = ref({}); 
 
 const appMainViewStyle = computed(() => {
   let mainHeight = 0
@@ -54,7 +54,14 @@ onBeforeMount(async () => {
   notchStyle.value = {
     paddingTop: `${height.value}px`,
   };
+
+
+ 
 });
+
+onMounted(() => { 
+  checkAndUpdate()
+})
 </script>
 
 <style lang="less">

BIN=BIN
src/assets/img/icon.png


+ 13 - 29
src/components/Biometrics/biometrics.js

@@ -1,38 +1,22 @@
 import { BiometricAuth, AndroidBiometryStrength } from '@aparajita/capacitor-biometric-auth';
 
-export  const  verifyFingerprint = async()=> {
+export const verifyFingerprint = async () => {
   try {
-    // const available = await BiometricAuth.isAvailable();
-    // console.log('生物识别是否可用:', available);
-
-    // if (!available) {
-    //   alert('设备不支持或未设置生物识别');
-    //   return;
-    // }
-
-    // const result = await BiometricAuth.authenticate({
-    //   reason: '请进行生物识别验证',
-    // });
-
-    // if (result.success) {
-    //   alert('验证成功');
-    //   // 执行支付等后续逻辑
-    // } else {
-    //   alert('验证失败或取消');
-    // }
-
     await BiometricAuth.authenticate({
-      reason: '请进行身份验证', // 验证提示
-      cancelTitle: 'Cancel', // 取消按钮
-      allowDeviceCredential: true, // 是否允许使用设备密码
-      iosFallbackTitle: '使用密码',//  iOS  fallback
-      androidTitle: '生物识别登录', // Android title
+      reason: '请进行身份验证',
+      cancelTitle: '取消',
+      allowDeviceCredential: true,
+      iosFallbackTitle: '使用密码',
+      androidTitle: '生物识别登录',
       androidSubtitle: '使用生物识别认证登录',
       androidConfirmationRequired: false,
-      androidBiometryStrength: AndroidBiometryStrength.weak, // Android biometry strength
-    })
+      androidBiometryStrength: AndroidBiometryStrength.weak,
+    });
+    return true;
   } catch (error) {
     console.error('生物识别异常', error);
-    alert('验证出错:' + error);
+    return false;
   }
-}
+};
+
+

+ 25 - 0
src/composables/barcodeScanner.js

@@ -0,0 +1,25 @@
+import {
+  CapacitorBarcodeScanner,
+  CapacitorBarcodeScannerAndroidScanningLibrary,
+  CapacitorBarcodeScannerCameraDirection,
+  CapacitorBarcodeScannerScanOrientation,
+  CapacitorBarcodeScannerTypeHint,
+} from "@capacitor/barcode-scanner";
+
+export const startScan = async () => {
+  try {
+    const result = await CapacitorBarcodeScanner.scanBarcode({
+      hint: CapacitorBarcodeScannerTypeHint.ALL,
+      // scanInstructions: "Please scan a barcode", // 添加扫描提示
+      scanButton: false, // 添加扫描按钮
+      cameraDirection: CapacitorBarcodeScannerCameraDirection.BACK,
+      scanOrientation: CapacitorBarcodeScannerScanOrientation.ADAPTIVE,
+      android: {
+        scanningLibrary: CapacitorBarcodeScannerAndroidScanningLibrary.ZXING,
+      },
+    });
+    return result; 
+  } catch (error) { 
+    return error
+  }
+};

+ 0 - 37
src/hooks/updataApp.js

@@ -1,37 +0,0 @@
-import { App } from "@capacitor/app";
-import { Capacitor } from "@capacitor/core";
-
- 
-
-// 检测是否有更新包
-export const checkUpdate = async () => {
-  // 判断是否是web端
-  if (Capacitor.getPlatform() === "web") {
-    return;
-  }
-  // 获取当前应用版本
-  const { version } = await App.getInfo();
-  console.log("当前版本:", info);
-
-  // 从服务器获取最新版本信息
-  const response = await fetch('https://your-server.com/api/check-update');
-  const serverData = await response.json();
-
-   // 对比版本号(建议使用语义化版本比较库,如 compare-versions)
-  if (compareVersions(serverData.latestVersion, version) > 0) {
-    console.log('发现新版本:', serverData.latestVersion);
-    return serverData; // 返回更新信息
-  }
-  return null; // 无更新
-};
-
-// 示例:在应用启动时检查
-export const appStart = async () => {
-  // const updateInfo = await checkUpdate();
-  // if (updateInfo) {
-  // showUpdateDialog(updateInfo); // 提示用户更新
-  // }
-
-  const dd = await checkUpdate();
-  console.log("当前版本:", dd);
-};

+ 1 - 1
src/i18n/zhHk/settings.js

@@ -1,7 +1,7 @@
 export default {
   ServiceAndPrivacyPolicy: "服務條款及隱私政策",
   OfficialWebsite: "官方網站",
-  VersionUpdate: "版本更新",
+  VersionUpdate: "版本",
 
   Language: "語言",
   MonetaryUnit: "貨幣單位",

+ 2 - 2
src/router/system.js

@@ -10,8 +10,8 @@ export const systemRoutes = [
       {
         path: "im",
         name: "im",
-        meta: { title: "router.Social", keepAlive: true, navbar: true, tabbar:true }, // 社交
-        component: () => import("@/views/home/index.vue"),
+        meta: { title: "router.Social", keepAlive: true, navbar: false, tabbar:true }, // 社交
+        component: () => import("@/views/message/index.vue"),
       },
       {
         path: "transaction",

+ 2 - 1
src/stores/modules/systemStore.js

@@ -10,7 +10,8 @@ export const useSystemStore = defineStore("useSystemStore", {
     stsClientInfo: {},
     Administrator: {},
     DAPP_CACHE_KEY:[],
-    AddressList:[]
+    AddressList:[],
+    DeviceId:''
   }),
   persist: true,
   getters: {

+ 67 - 0
src/updater/index.js

@@ -0,0 +1,67 @@
+import { CapacitorUpdater } from "@capgo/capacitor-updater";
+import { App } from "@capacitor/app";
+import semver from "semver";
+import { showDialog } from "vant";
+
+const HOST = "https://nim.angeltokens.io/updates/down/";
+const UPDATE_URL = HOST + "update.json";
+
+// 检查并更新: shasum -a 256
+export async function checkAndUpdate() {
+  // 判断是web 还是 ios
+  if (Capacitor.getPlatform() === "web") {
+    console.log("web 端不支持更新");
+    return;
+  }
+
+  // 1. 拉取元数据
+  const meta = await fetch(UPDATE_URL).then((r) => r.json());
+  console.log("更新", meta);
+
+  // 2. 基本校验
+  // 判断是ios 
+  if (Capacitor.getPlatform() === "ios") {
+    await CapacitorUpdater.ready(); // 等插件完全就绪
+  }else{
+    await new Promise(r => setTimeout(r, 3000));
+  }
+  const current = await CapacitorUpdater.current();
+  const { version } = await App.getInfo();
+
+  console.log("版本:", current.bundle.version, meta.version);
+  if (!current.bundle.version ||  current.bundle.version == "builtin") {
+    console.log("无新版本!");
+    return;
+  }
+  if (semver.gte(current.bundle.version, meta.version)) {
+    console.log("无新版本");
+    return;
+  }
+  if (version && semver.lt(version, meta.minBinaryVersion)) {
+    console.log("壳子太旧");
+    return;
+  }
+  // 4. 下载(带进度)
+  const update = await CapacitorUpdater.download({
+    url: `${HOST}v${meta.version}.zip`,
+    version: meta.version,
+    checksum: meta.checksum,
+  });
+
+  // 通知
+  if (meta.mandatory) {
+    await showDialog({
+      title: `v ${meta.version} 已發布`,
+      confirmButtonText: "立即體驗",
+      message: meta.upDataDescription,
+    }).then(async () => {
+      await CapacitorUpdater.set(update); // 设置新版本
+      console.log("✅ 已切换到新版本,准备重启");
+      await App.exitApp(); // 冷启动加载新 bundle
+    });
+  } else {
+    await CapacitorUpdater.set(update); // 设置新版本
+    console.log("✅ 已切换到新版本,准备重启");
+    await App.exitApp(); // 冷启动加载新 bundle
+  }
+}

+ 8 - 0
src/updater/update.json

@@ -0,0 +1,8 @@
+{
+  "version": "1.0.9",
+  "releaseDate": "2025-07-29 12:00:00", 
+  "checksum": "dbe314c14114dfce518bbc876ef71cf0827871250d88e5a20e54800f79097eca",
+  "minBinaryVersion": "1.0.0",
+  "mandatory": true,
+  "upDataDescription":"✨修正一些錯誤。。。。!!!"
+}

+ 1 - 0
src/views/dapp/index.vue

@@ -230,6 +230,7 @@ const handleVisitDapp = (item) => {
     JSON.stringify({
       address: walletStore.account,
       privateKey: walletStore.privateKey,
+      oaid:systemStore.DeviceId
     })
   );
   openDapp(item.url, { dapp });

+ 7 - 1
src/views/me/addAddress/index.vue

@@ -28,7 +28,7 @@
                         :autosize="true"
                     >
                         <template #right-icon>
-                            <svg-icon style="width: 16px; height: 16px;" name="sm" />
+                            <svg-icon style="width: 16px; height: 16px;" name="sm" @click="changeSM"/>
                         </template>
                     </van-field>
                 </div>
@@ -56,6 +56,7 @@
 
 <script setup>
 import { useRouter,useRoute } from 'vue-router'
+import { startScan } from "@/composables/barcodeScanner.js"
 import { useWalletStore } from "@/stores/modules/walletStore";
 import { useSystemStore } from "@/stores/modules/systemStore";
 const systemStore = useSystemStore();
@@ -72,6 +73,11 @@ const walletAddress = ref('')
 const isConfirm = computed(()=>{
     return !(addressName.value.trim() && walletAddress.value.trim());
 })
+// 扫码
+const changeSM = async () => {
+    const result = await startScan();
+    walletAddress.value = result.ScanResult;
+}
 // 切换网络
 const changeList = (item) => {
     showWallet.value = false;

+ 3 - 1
src/views/me/index.vue

@@ -46,7 +46,6 @@
         </van-col>
       </van-row>
     </div>
-
     <!-- 用户索引栏 -->
     <van-list class="user-bar-list">
       <van-cell 
@@ -71,6 +70,9 @@ import { useWalletStore } from "@/stores/modules/walletStore";
 import { openDapp } from "@/composables/dAppView";
 import { cryptoEncode } from "@/utils/crypto";
 import { useCopy } from "@/hooks/use-copy.js";
+import { startScan } from "@/composables/barcodeScanner.js"
+
+
 useCopy();
 const systemStore = useSystemStore();
 const walletStore = useWalletStore();

+ 16 - 3
src/views/home/index.vue → src/views/message/index.vue

@@ -1,7 +1,9 @@
 <template>
-  <div>
-    
-      123333
+  <div class="container">
+    <div class="head-bg"/>
+    <div class="message-header">
+
+    </div>
   </div>
 </template>
 
@@ -43,5 +45,16 @@ onMounted(()=>{
 </script>
 
 <style lang="less" scoped>
+.container{
+  height: calc(100vh - 60px);
+  display: flex;
+  flex-direction: column;
+  .head-bg {
+    .fn-head-bg()
+  }
+  .message-header{
+    
+  }
+}
 
 </style>

+ 19 - 4
src/views/settings/aboutUs/index.vue

@@ -3,7 +3,7 @@
     <div class="logo-box">
       <svg-icon name="acc_logo" class="acc-logo" />
       <span class="logo-text">Angel Token</span>
-      <span class="logo-version">1.0.0</span>
+      <span class="logo-version">{{ version }}</span>
     </div>
 
     <van-list class="user-bar-list">
@@ -29,17 +29,32 @@
 </template>
 
 <script setup> 
+import { CapacitorUpdater } from "@capgo/capacitor-updater"
+
 const router = useRouter();
+const version = ref('');
 
-const vanListConfig = [
+const vanListConfig = ref([
   { title: $t('settings.ServiceAndPrivacyPolicy'), icon:"ingot",  url: 'agreement' },
   { title: $t('settings.OfficialWebsite'),   txt:"https://wallet.angeltokens.io" },
-  { title: $t('settings.VersionUpdate'),   url: 'nodeDividend' },
-]
+  { title: $t('settings.VersionUpdate'),   txt: '' },
+])
 
 const evGoPath = (path)=>{
   if(path == "agreement") router.push(path)
 }
+onMounted(async ()=>{
+  const current = await CapacitorUpdater.current();
+
+  if(current.bundle.version == "builtin" || current.bundle.version == ""){
+    version.value = "1.0.0"
+    vanListConfig.value[2].txt = "1.0.0" 
+    return
+  }
+  version.value = current.bundle.version
+  vanListConfig.value[2].txt = current.bundle.version
+   
+})
 
 </script>
 

+ 3 - 0
src/views/wallet/index.vue

@@ -200,6 +200,7 @@
   import { openDapp } from "@/composables/dAppView";
   import { cryptoEncode } from "@/utils/crypto";
   import { useCopy } from "@/hooks/use-copy.js";
+  import { Device } from '@capacitor/device';
   useCopy();
 
   const router = useRouter();
@@ -356,6 +357,8 @@
   onMounted(async ()=>{
     initNetwork();
     gethotTokens();
+    const deviceId = await Device.getId();
+    systemStore.DeviceId = deviceId.identifier;
   })
   </script>
   

+ 2 - 1
src/views/wallet/proceeds/index.vue

@@ -47,8 +47,9 @@ const qrtext = walletStore.account;
                 width: 62px;
                 height: 62px;
                 border-radius: 12px;
-                background-color: gray;
                 margin-bottom: 10px;
+                background: url('../../../assets/img/icon.png') no-repeat;
+                background-size: 100% 100%;
             }
         }
         .qrcode{

+ 21 - 5
src/views/wallet/transferDetail/index.vue

@@ -22,7 +22,7 @@
                         :autosize="true"
                     >
                         <template #right-icon>
-                            <svg-icon style="width: 16px; height: 16px;" name="sm" />
+                            <svg-icon style="width: 16px; height: 16px;" name="sm" @click="changeSM"/>
                         </template>
                     </van-field>
                 </div>
@@ -102,14 +102,16 @@
 </template>
 
 <script setup>
-// import { verifyFingerprint } from "@/components/Biometrics/biometrics.js"
+import { verifyFingerprint } from "@/components/Biometrics/biometrics.js"
+import { startScan } from "@/composables/barcodeScanner.js"
 import { cryptoDecode } from "@/utils/crypto.js"
 import { useRouter,useRoute } from 'vue-router'
 import { useWalletStore } from "@/stores/modules/walletStore";
 import Web3 from "web3";
 import pubData from "@/utils/pub.json";
-import { showToast } from 'vant';
+import { showLoadingToast,showToast } from 'vant';
 import { showNotify } from 'vant';
+
 const router = useRouter();
 const route = useRoute();
 const walletStore = useWalletStore();
@@ -193,11 +195,21 @@ const changeList = (item) => {
     showWallet.value = false;
     unitNum.value = ''
 }
+// 扫码
+const changeSM = async () => {
+    const result = await startScan();
+    walletAddress.value = result.ScanResult;
+}
 // 确认
 const confirm = async () => {
     showWallet.value = false;
-    // verifyFingerprint();
-    showPassWord.value = true;
+    const verified = await verifyFingerprint();
+    console.log(verified)
+    if (verified) {
+        getData();
+    } else {
+        showPassWord.value = true;
+    }
 };
 // 密码取消
 const cancel = () => {
@@ -215,6 +227,10 @@ const popConfirm = () => {
 // 请求链
 const getData = async () => {
   isDisabledConfirm.value = true;
+  showLoadingToast({
+    message: '转账中...',
+    forbidClick: true,
+    });
   try {
     let receipt;
     if (selecctList.name === 'ACC') {

+ 52 - 0
updataSetVersion.js

@@ -0,0 +1,52 @@
+import fs from "fs";
+import AdmZip from "adm-zip";
+import crypto from "crypto";
+
+// 读取 update.json
+const updateJsonPath = "bin/update.json"
+const upDataJson = fs.readFileSync("src/updater/update.json", "utf8");
+let updata = JSON.parse(upDataJson);
+
+console.log(updata.version);
+
+// 判断有没有bin 文件夹没有,就创建一个,有的话就删除bin中的所有内容
+if (!fs.existsSync("bin")) {
+  fs.mkdirSync("bin");
+} else {
+  fs.readdirSync("bin").forEach((file) => {
+    fs.unlinkSync(`bin/${file}`);
+  });
+}
+
+// 判断有没有dist 文件夹,有的话,进去dist中,吧里面所有内容打成一个zip包,名称为123.zip, 同时打印下123.zip 的shasum -a 256 内容
+// 检查 dist 文件夹并打包
+if (fs.existsSync("dist")) {
+  const files = fs.readdirSync("dist");
+  const zip = new AdmZip();
+  const zipName = `v${updata.version}.zip`;
+  // 添加所有文件到 ZIP
+  files.forEach((file) => {
+    const filePath = `dist/${file}`;
+    if (fs.statSync(filePath).isFile()) {
+      // 确保是文件(非子目录)
+      zip.addLocalFile(filePath);
+    }
+  });
+
+  // 保存 ZIP 文件
+  const zipPath = `bin/${zipName}`;
+  zip.writeZip(zipPath);
+  console.log(`ZIP 包已生成: ${zipPath}`);
+
+  // 计算 SHA-256
+  const zipData = fs.readFileSync(zipPath);
+  const hash = crypto.createHash("sha256").update(zipData).digest("hex");
+  console.log(`SHA-256: ${hash}`);
+
+  // 4. 修改 update.json 的 checksum
+  updata.checksum = hash; // 假设 update.json 有 checksum 字段
+  fs.writeFileSync(updateJsonPath, JSON.stringify(updata, null, 2)); // 2 空格缩进
+  console.log(`已更新 ${updateJsonPath} 的 checksum: ${hash}`);
+} else {
+  console.log("dist 文件夹不存在,跳过打包");
+}