Browse Source

feat: "Airwallex SDK"

D Ritchie 2 years ago
parent
commit
0f04117530

+ 2 - 0
package.json

@@ -11,6 +11,7 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
+    "@airwallex/payouts-web-sdk": "^1.3.0",
     "@amap/amap-jsapi-loader": "^1.0.1",
     "@vant/touch-emulator": "^1.4.0",
     "airwallex-payment-elements": "latest",
@@ -20,6 +21,7 @@
     "core-js": "^3.6.5",
     "crypto-js": "^4.1.1",
     "dayjs": "^1.11.7",
+    "js-base64": "^3.7.5",
     "js-md5": "^0.7.3",
     "lib-flexible": "^0.3.2",
     "moment": "^2.29.4",

+ 8 - 1
src/assets/language/en.json

@@ -596,6 +596,12 @@
       "roger": "Do not show again"
     }
   },
+  "airwallex": {
+    "airwallex": "Airwallex",
+    "wallet": "Airwallex wallet",
+    "amount": "Airwallex amount",
+    "payment": "New transfer"
+  },
   "joinpayMch": {
     "withdrawalAccountNo": "Withdrawal account No",
     "merchantNameLabel": "Merchant name",
@@ -1151,7 +1157,8 @@
     "logOutContent": "Are you sure you want to log out",
     "corrEmailPlace": "Please enter the correct email address",
     "corrPhonePlace": "Please enter the correct mobile number",
-    "bindWechat":"Bind WeChat"
+    "bindWechat":"Bind WeChat",
+    "airwallex":"Airwallex"
   },
   "bindWechat": {
     "cancel": "Cancel",

+ 8 - 1
src/assets/language/zh.json

@@ -597,6 +597,12 @@
       "roger":"不再显示"
     }
   },
+  "airwallex": {
+    "airwallex": "空中云汇",
+    "wallet": "空中云汇钱包",
+    "amount": "钱包金额",
+    "payment": "付款转账"
+  },
   "joinpayMch": {
     "withdrawalAccountNo": "提现账号",
     "merchantNameLabel": "商户名称",
@@ -1155,7 +1161,8 @@
     "logOutContent":"确定需要退出登录吗",
     "corrEmailPlace":"请输入正确的邮箱",
     "corrPhonePlace":"请输入正确的手机号",
-    "bindWechat":"绑定微信"
+    "bindWechat":"绑定微信",
+    "airwallex":"空中云汇"
   },
   "bindWechat": {
     "cancel": "取消",

BIN
src/assets/user/airwallexIcon.png


+ 417 - 78
src/router/index.js

@@ -1,134 +1,469 @@
-import { createRouter, createWebHashHistory } from 'vue-router'
-import { getLocal } from '@/common/js/utils';
+import { createRouter, createWebHashHistory } from "vue-router";
+import { getLocal } from "@/common/js/utils";
 
 const router = createRouter({
   // hash模式:createWebHashHistory,history模式:createWebHistory
   history: createWebHashHistory(),
   routes: [
     // 首页
-    { path: '/', redirect: '/home' },
-    { path: '/home', name: 'home', component: () => import('@/views/home/index.vue'), meta: { index: 1 } },
+    { path: "/", redirect: "/home" },
+    // Home 页面
+    {
+      path: "/home",
+      name: "home",
+      component: () => import("@/views/home/index.vue"),
+      meta: { index: 1 },
+    },
     // 登录页面
-    { path: '/login', name: 'login', component: () => import('@/views/login.vue'), meta: { index: 1, noLogin: true } },
+    {
+      path: "/login",
+      name: "login",
+      component: () => import("@/views/login.vue"),
+      meta: { index: 1, noLogin: true },
+    },
     // 微信登录跳板
-    { path: '/wxLogin', name: 'wxLogin', component: () => import('@/views/wxLogin.vue'), meta: { index: 1, noLogin: true } },
+    {
+      path: "/wxLogin",
+      name: "wxLogin",
+      component: () => import("@/views/wxLogin.vue"),
+      meta: { index: 1, noLogin: true },
+    },
     // 注册页面
-    { path: '/register', name: 'register', component: () => import('@/views/register.vue'), meta: { index: 1, noLogin: true } },
+    {
+      path: "/register",
+      name: "register",
+      component: () => import("@/views/register.vue"),
+      meta: { index: 1, noLogin: true },
+    },
     // 忘记密码页面
-    { path: '/forgetpassword', name: 'forgetPassword', component: () => import('@/views/forgetPassword'), meta: { index: 1, noLogin: true } },
+    {
+      path: "/forgetpassword",
+      name: "forgetPassword",
+      component: () => import("@/views/forgetPassword"),
+      meta: { index: 1, noLogin: true },
+    },
     // 绑定微信
-    { path: '/bindWechat', name: 'bindWechat', component: () => import('@/views/bindWechat.vue'), meta: {index: 1, noLogin: true}},
+    {
+      path: "/bindWechat",
+      name: "bindWechat",
+      component: () => import("@/views/bindWechat.vue"),
+      meta: { index: 1, noLogin: true },
+    },
     // 修改密码页面
-    { path: '/changepassword', name: 'changePassword', component: () => import('@/views/changePassword'), meta: { index: 1, noLogin: true } },
+    {
+      path: "/changepassword",
+      name: "changePassword",
+      component: () => import("@/views/changePassword"),
+      meta: { index: 1, noLogin: true },
+    },
     // 设备页面
-    { path: '/device', name: 'device', component: () => import('@/views/device/index'), meta: { index: 1 } },
+    {
+      path: "/device",
+      name: "device",
+      component: () => import("@/views/device/index"),
+      meta: { index: 1 },
+    },
     // 设备详情
-    { path: '/deviceSet', name: 'deviceSet', component: () => import('@/views/device/deviceSet'), meta: { index: 1 } },
+    {
+      path: "/deviceSet",
+      name: "deviceSet",
+      component: () => import("@/views/device/deviceSet"),
+      meta: { index: 1 },
+    },
     // 编辑标签
-    { path: '/editTag', name: 'editTag', component: () => import('@/views/device/tagSet/index.vue'), meta: { index: 1 } },
+    {
+      path: "/editTag",
+      name: "editTag",
+      component: () => import("@/views/device/tagSet/index.vue"),
+      meta: { index: 1 },
+    },
     // { path: '/deviceOpr', name: 'deviceOpr', component: () => import('@/views/device/deviceOper2.vue'), meta: { index: 1 } },
     // 音量调节
-    { path: '/modulation', name: 'modulation', component: () => import('@/views/device/modulation.vue'), meta: { index: 1} },
-	// 远程开门
-	{ path: '/openDoor', name: 'openDoor', component: () => import('@/views/device/openDoor.vue'), meta: { index: 1} },
+    {
+      path: "/modulation",
+      name: "modulation",
+      component: () => import("@/views/device/modulation.vue"),
+      meta: { index: 1 },
+    },
+    // 远程开门
+    {
+      path: "/openDoor",
+      name: "openDoor",
+      component: () => import("@/views/device/openDoor.vue"),
+      meta: { index: 1 },
+    },
     // 远程做糖
-    { path: '/doSugar', name: 'doSugar', component: () => import('@/views/device/doSugar.vue'), meta: { index: 1} },
+    {
+      path: "/doSugar",
+      name: "doSugar",
+      component: () => import("@/views/device/doSugar.vue"),
+      meta: { index: 1 },
+    },
     // 今日做糖列表
-    { path: '/toDaySugarList', name: 'toDaySugarList', component: () => import('@/views/device/toDaySugarList.vue'), meta: { index: 1} },
+    {
+      path: "/toDaySugarList",
+      name: "toDaySugarList",
+      component: () => import("@/views/device/toDaySugarList.vue"),
+      meta: { index: 1 },
+    },
     // 定时开关
-    { path: '/alarmClock', name: 'alarmClock', component: () => import('@/views/device/alarmClock.vue'), meta: { index: 1} },
+    {
+      path: "/alarmClock",
+      name: "alarmClock",
+      component: () => import("@/views/device/alarmClock.vue"),
+      meta: { index: 1 },
+    },
     // 设置闹钟
-    { path: '/alarmClockAdd', name: 'alarmClockAdd', component: () => import('@/views/device/alarmClockSet/index.vue'), meta: { index: 1} },
-    { path: '/alarmClockSet', name: 'alarmClockSet', component: () => import('@/views/device/alarmClockSet/index.vue'), meta: { index: 1} },
+    {
+      path: "/alarmClockAdd",
+      name: "alarmClockAdd",
+      component: () => import("@/views/device/alarmClockSet/index.vue"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/alarmClockSet",
+      name: "alarmClockSet",
+      component: () => import("@/views/device/alarmClockSet/index.vue"),
+      meta: { index: 1 },
+    },
     // 参数设置
-    { path: '/paramsSet', name: 'paramsSet', component: () => import('@/views/device/paramsSet/index.vue'), meta: { index: 1} },
-    { path: '/paramsSetInfo', name: 'paramsSetInfo', component: () => import('@/views/device/paramsSet/paramsSetInfo.vue'), meta: { index: 1} },
+    {
+      path: "/paramsSet",
+      name: "paramsSet",
+      component: () => import("@/views/device/paramsSet/index.vue"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/paramsSetInfo",
+      name: "paramsSetInfo",
+      component: () => import("@/views/device/paramsSet/paramsSetInfo.vue"),
+      meta: { index: 1 },
+    },
     // 添加分销人
-    { path: '/saveProportion', name: 'saveProportion', component: () => import('@/views/device/saveProportion/index.vue'), meta: { index: 1} },
+    {
+      path: "/saveProportion",
+      name: "saveProportion",
+      component: () => import("@/views/device/saveProportion/index.vue"),
+      meta: { index: 1 },
+    },
     // 设备充值
-    { path: '/recharge', name: 'recharge', component: () => import('@/views/device/recharge.vue'), meta: { index: 1} },
+    {
+      path: "/recharge",
+      name: "recharge",
+      component: () => import("@/views/device/recharge.vue"),
+      meta: { index: 1 },
+    },
     // 机器排行
-    { path: '/robotranking', name: 'robotranking', component: () => import('@/views/robotRanking.vue'), meta: { index: 1 } },
+    {
+      path: "/robotranking",
+      name: "robotranking",
+      component: () => import("@/views/robotRanking.vue"),
+      meta: { index: 1 },
+    },
     // 个人中心
-    { path: '/user', name: 'user', component: () => import('@/views/user.vue'), meta: { index: 1 } },
+    {
+      path: "/user",
+      name: "user",
+      component: () => import("@/views/user.vue"),
+      meta: { index: 1 },
+    },
     // 备用提现账号
-    { path: '/shandeMch', name: 'shandeMch', component: () => import('@/views/shandeMch/index'), meta: { index: 1 } },
+    {
+      path: "/shandeMch",
+      name: "shandeMch",
+      component: () => import("@/views/shandeMch/index"),
+      meta: { index: 1 },
+    },
     // 提现账号
-    { path: '/joinpayMch', name: 'joinpayMch', component: () => import('@/views/joinpayMch/index'), meta: { index: 1 } },
+    {
+      path: "/joinpayMch",
+      name: "joinpayMch",
+      component: () => import("@/views/joinpayMch/index"),
+      meta: { index: 1 },
+    },
     // 自充值
-    { path: '/uniPay', name: 'uniPay', component: () => import('@/views/uniPay/index'), meta: { index: 1 } },
+    {
+      path: "/uniPay",
+      name: "uniPay",
+      component: () => import("@/views/uniPay/index"),
+      meta: { index: 1 },
+    },
     // 优惠码
-    { path: '/discountCode', name: 'discountCode', component: () => import('@/views/discountCode/index'), meta: { index: 1 } },
-    { path: '/payCode', name: 'payCode', component: () => import('@/views/discountCode/payCode'), meta: { index: 1 } },
+    {
+      path: "/discountCode",
+      name: "discountCode",
+      component: () => import("@/views/discountCode/index"),
+      meta: { index: 1 },
+    },
+    // Airwallex 钱包
+    {
+      path: "/airwallex",
+      name: "airwallex",
+      component: () => import("@/views/airwallex/index.vue"),
+      meta: { index: 1 },
+    },
+    // Airwallex 提现
+    {
+      path: "/airwallexPayment",
+      name: "airwallexPayment",
+      component: () => import("@/views/airwallex/payment.vue"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/payCode",
+      name: "payCode",
+      component: () => import("@/views/discountCode/payCode"),
+      meta: { index: 1 },
+    },
     // 分销设置
-    { path: '/distributionSet', name: 'distributionSet', component: () => import('@/views/distributionSet/index'), meta: { index: 1 } },
-    { path: '/distributionDetail', name: 'distributionDetail', component: () => import('@/views/distributionSet/detail'), meta: { index: 1 } },
+    {
+      path: "/distributionSet",
+      name: "distributionSet",
+      component: () => import("@/views/distributionSet/index"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/distributionDetail",
+      name: "distributionDetail",
+      component: () => import("@/views/distributionSet/detail"),
+      meta: { index: 1 },
+    },
     // 账号权限
-    { path: '/accountPer', name: 'accountPer', component: () => import('@/views/accountPer/index'), meta: { index: 1 } },
+    {
+      path: "/accountPer",
+      name: "accountPer",
+      component: () => import("@/views/accountPer/index"),
+      meta: { index: 1 },
+    },
     // 添加账号
-    { path: '/accountPerAdd', name: 'accountPerAdd', component: () => import('@/views/accountPer/add'), meta: { index: 1 } },
+    {
+      path: "/accountPerAdd",
+      name: "accountPerAdd",
+      component: () => import("@/views/accountPer/add"),
+      meta: { index: 1 },
+    },
     // 角色权限
-    { path: '/role', name: 'role', component: () => import('@/views/role/index'), meta: { index: 1 } },
-    { path: '/roleSet', name: 'roleSet', component: () => import('@/views/role/add'), meta: { index: 1 } },
+    {
+      path: "/role",
+      name: "role",
+      component: () => import("@/views/role/index"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/roleSet",
+      name: "roleSet",
+      component: () => import("@/views/role/add"),
+      meta: { index: 1 },
+    },
     // 商户管理
-    { path: '/merchantManage', name: 'merchantManage', component: () => import('@/views/merchantManage/index'), meta: { index: 1 } },
-    { path: '/merchantSet', name: 'merchantSet', component: () => import('@/views/merchantManage/set'), meta: { index: 1 } },
+    {
+      path: "/merchantManage",
+      name: "merchantManage",
+      component: () => import("@/views/merchantManage/index"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/merchantSet",
+      name: "merchantSet",
+      component: () => import("@/views/merchantManage/set"),
+      meta: { index: 1 },
+    },
     // 广告管理
-    { path: '/advertManage', name: 'advertManage', component: () => import('@/views/advertManage/index'), meta: { index: 1 } },
-    { path: '/advertSet', name: 'advertSet', component: () => import('@/views/advertManage/adSet.vue'), meta: { index: 1 } },
+    {
+      path: "/advertManage",
+      name: "advertManage",
+      component: () => import("@/views/advertManage/index"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/advertSet",
+      name: "advertSet",
+      component: () => import("@/views/advertManage/adSet.vue"),
+      meta: { index: 1 },
+    },
     // 广告规则
-    { path: '/advertRule', name: 'advertRule', component: () => import('@/views/advertManage/advertRule/index.vue'), meta: { index: 1 } },
+    {
+      path: "/advertRule",
+      name: "advertRule",
+      component: () => import("@/views/advertManage/advertRule/index.vue"),
+      meta: { index: 1 },
+    },
     // 广告规则新增
-    { path: '/advertRuleAdd', name: 'advertRuleAdd', component: () => import('@/views/advertManage/advertRule/add.vue'), meta: { index: 1 } },
-    { path: '/advertRuleAddScreen', name: 'advertRuleAddScreen', component: () => import('@/views/advertManage/advertRule/screen.vue'), meta: { index: 1 } },
+    {
+      path: "/advertRuleAdd",
+      name: "advertRuleAdd",
+      component: () => import("@/views/advertManage/advertRule/add.vue"),
+      meta: { index: 1 },
+    },
+    {
+      path: "/advertRuleAddScreen",
+      name: "advertRuleAddScreen",
+      component: () => import("@/views/advertManage/advertRule/screen.vue"),
+      meta: { index: 1 },
+    },
     // apk管理
-    { path: '/apkManage', name: 'apkManage', component: () => import('@/views/apkManage/index'), meta: { index: 1 } },
+    {
+      path: "/apkManage",
+      name: "apkManage",
+      component: () => import("@/views/apkManage/index"),
+      meta: { index: 1 },
+    },
     // apk管理-新增
-    { path: '/apkManageAdd', name: 'apkManageAdd', component: () => import('@/views/apkManage/add'), meta: { index: 1 } },
+    {
+      path: "/apkManageAdd",
+      name: "apkManageAdd",
+      component: () => import("@/views/apkManage/add"),
+      meta: { index: 1 },
+    },
     // 报警历史
-    { path: '/alarmHistory', name: 'alarmHistory', component: () => import('@/views/alarmHistory/index'), meta: { index: 1 } },
+    {
+      path: "/alarmHistory",
+      name: "alarmHistory",
+      component: () => import("@/views/alarmHistory/index"),
+      meta: { index: 1 },
+    },
     // 订单导出
-    { path: '/orderExport', name: 'orderExport', component: () => import('@/views/orderExport/index'), meta: { index: 1 } },
+    {
+      path: "/orderExport",
+      name: "orderExport",
+      component: () => import("@/views/orderExport/index"),
+      meta: { index: 1 },
+    },
     // 杉德分账导出
-    { path: '/subLedgerManage', name: 'subLedgerManage', component: () => import('@/views/subLedgerManage/index'), meta: { index: 1 } },
+    {
+      path: "/subLedgerManage",
+      name: "subLedgerManage",
+      component: () => import("@/views/subLedgerManage/index"),
+      meta: { index: 1 },
+    },
     // 任务消息
-    { path: '/taskMessage', name: 'taskMessage', component: () => import('@/views/taskMessage/index'), meta: { index: 1 } },
+    {
+      path: "/taskMessage",
+      name: "taskMessage",
+      component: () => import("@/views/taskMessage/index"),
+      meta: { index: 1 },
+    },
     // 设备审批
-    { path: '/taskEquipment', name: 'taskEquipment', component: () => import('@/views/taskMessage/equipment'), meta: { index: 1 } },
+    {
+      path: "/taskEquipment",
+      name: "taskEquipment",
+      component: () => import("@/views/taskMessage/equipment"),
+      meta: { index: 1 },
+    },
     // 提现审批
-    { path: '/taskJoinPayMchCheck', name: 'taskJoinPayMchCheck', component: () => import('@/views/taskMessage/joinpayMchCheck'), meta: { index: 1 } },
+    {
+      path: "/taskJoinPayMchCheck",
+      name: "taskJoinPayMchCheck",
+      component: () => import("@/views/taskMessage/joinpayMchCheck"),
+      meta: { index: 1 },
+    },
     // 提现审批详情
-    { path: '/taskJoinPayMchCheckInfo', name: 'taskJoinPayMchCheckInfo', component: () => import('@/views/taskMessage/joinpayMchCheck/info'), meta: { index: 1 } },
+    {
+      path: "/taskJoinPayMchCheckInfo",
+      name: "taskJoinPayMchCheckInfo",
+      component: () => import("@/views/taskMessage/joinpayMchCheck/info"),
+      meta: { index: 1 },
+    },
     // 分销审批
-    { path: '/taskProportion', name: 'taskProportion', component: () => import('@/views/taskMessage/proportion'), meta: { index: 1 } },
+    {
+      path: "/taskProportion",
+      name: "taskProportion",
+      component: () => import("@/views/taskMessage/proportion"),
+      meta: { index: 1 },
+    },
     // 订单中心
-    { path: '/orderCenter', name: 'orderCenter', component: () => import('@/views/orderCenter/index'), meta: { index: 1 } },
+    {
+      path: "/orderCenter",
+      name: "orderCenter",
+      component: () => import("@/views/orderCenter/index"),
+      meta: { index: 1 },
+    },
     // 测试
-    { path: '/test', name: 'test', component: () => import('@/views/test'), meta: { index: 1 } },
+    {
+      path: "/test",
+      name: "test",
+      component: () => import("@/views/test"),
+      meta: { index: 1 },
+    },
     // 标签管理
-    { path: '/labelMan', name: 'labelMan', component: () => import('@/views/labelMan/index'), meta: { index: 1 } },
+    {
+      path: "/labelMan",
+      name: "labelMan",
+      component: () => import("@/views/labelMan/index"),
+      meta: { index: 1 },
+    },
     // 标签管理-新增
-    { path: '/labelManAdd', name: 'labelManAdd', component: () => import('@/views/labelMan/add'), meta: { index: 1 } },
+    {
+      path: "/labelManAdd",
+      name: "labelManAdd",
+      component: () => import("@/views/labelMan/add"),
+      meta: { index: 1 },
+    },
     // 修改价格
-    { path: '/modifyPrice', name: 'modifyPrice', component: () => import('@/views/device/modifyPrice/index'), meta: { index: 1 } },
-	// 屏蔽/展示商品
-	{ path: '/showGoods', name: 'showGoods', component: () => import('@/views/device/showGoods/index'), meta: { index: 1 } },
-	// 修改机器密码
-	{ path: '/devicePassword', name: 'devicePassword', component: () => import('@/views/device/devicePassword/index'), meta: { index: 1 } },
+    {
+      path: "/modifyPrice",
+      name: "modifyPrice",
+      component: () => import("@/views/device/modifyPrice/index"),
+      meta: { index: 1 },
+    },
+    // 屏蔽/展示商品
+    {
+      path: "/showGoods",
+      name: "showGoods",
+      component: () => import("@/views/device/showGoods/index"),
+      meta: { index: 1 },
+    },
+    // 修改机器密码
+    {
+      path: "/devicePassword",
+      name: "devicePassword",
+      component: () => import("@/views/device/devicePassword/index"),
+      meta: { index: 1 },
+    },
     // 查看定位
-    { path: '/viewPosition', name: 'viewPosition', component: () => import('@/views/device/viewPosition/index'), meta: { index: 1 } },  
+    {
+      path: "/viewPosition",
+      name: "viewPosition",
+      component: () => import("@/views/device/viewPosition/index"),
+      meta: { index: 1 },
+    },
     // 设备编辑-编辑规则
-    { path: '/editAdRule', name: 'editAdRule', component: () => import('@/views/device/editAdRule/index'), meta: { index: 1 } },
+    {
+      path: "/editAdRule",
+      name: "editAdRule",
+      component: () => import("@/views/device/editAdRule/index"),
+      meta: { index: 1 },
+    },
     // 跳转空中云汇
-    { path: '/hpp', name: 'Hpp', component: () => import('@/views/Hpp.vue'), meta: { index: 1, noLogin: true } },
+    {
+      path: "/hpp",
+      name: "Hpp",
+      component: () => import("@/views/Hpp.vue"),
+      meta: { index: 1, noLogin: true },
+    },
     // 定制logo
-    { path: '/customLogo', name: 'customLogo', component: () => import('@/views/device/customLogo.vue'), meta: { index: 1} },
+    {
+      path: "/customLogo",
+      name: "customLogo",
+      component: () => import("@/views/device/customLogo.vue"),
+      meta: { index: 1 },
+    },
     // 查看日志
-    { path: '/viewLogs', name: 'viewLogs', component: () => import('@/views/device/viewLogs/index'), meta: { index: 1 } },
-	// 账户操作
-	{ path: '/accountOperation', name: 'accountOperation', component: () => import('@/views/accountOperation/index'), meta: { index: 1 } },
+    {
+      path: "/viewLogs",
+      name: "viewLogs",
+      component: () => import("@/views/device/viewLogs/index"),
+      meta: { index: 1 },
+    },
+    // 账户操作
+    {
+      path: "/accountOperation",
+      name: "accountOperation",
+      component: () => import("@/views/accountOperation/index"),
+      meta: { index: 1 },
+    },
     // apk管理,广告管理,订单导出 不是所有的帐号能看到
-  ]
+  ],
 });
 // 路由守卫处理
 router.beforeEach((to, from, next) => {
@@ -136,18 +471,22 @@ router.beforeEach((to, from, next) => {
   if (to.meta.noLogin) {
     next();
   } else {
-    const user = getLocal('loginUser');
-    if (!user || user === '') {
+    const user = getLocal("loginUser");
+    if (!user || user === "") {
       // 没有登录信息跳转登录页面
-      router.push('/login');
+      router.push("/login");
     } else {
       const userObject = JSON.parse(user);
       // 登录信息异常跳转登录页面
-      if (!userObject) { router.push('/login'); }
-      if (typeof userObject.id !== 'number') { router.push('/login'); }
+      if (!userObject) {
+        router.push("/login");
+      }
+      if (typeof userObject.id !== "number") {
+        router.push("/login");
+      }
     }
     next();
   }
 });
 
-export default router;
+export default router;

+ 6 - 0
src/service/airwallex/index.js

@@ -0,0 +1,6 @@
+import axios from '../../utils/axios';
+
+// 获取 AuthCode
+export function getAuthCode(getAuthCodeDTO) {
+    return axios.post(`/SZWL-SERVER/api/airwallexPay/AuthAnAccount`, getAuthCodeDTO);
+}

+ 22 - 0
src/styles/airwallex/index.less

@@ -0,0 +1,22 @@
+.airwallexWallet {
+  background-color: #f8f8f8;
+}
+
+.airwallexWallet .amount {
+  margin-top: 40px;
+}
+
+.airwallexWallet .airwallexWalletBtn{
+  width: 100%;
+  margin-top: 20px;
+  text-align: center;
+}
+.airwallexWallet .airwallexWalletBtn .register {
+  background-color: #4d6add;
+  border-radius: 17px;
+  height: 34px;
+  width: 220px;
+  margin-top: 60px;
+  font-size: 15px;
+  font-family: PingFangSC-Medium;
+}

+ 11 - 0
src/styles/user/index.less

@@ -158,6 +158,17 @@
                         right: 0.2rem;
                         top: 0.4rem;
                     }
+                    
+                    &.airwallexIcon::after {
+                        content: "";
+                        position: absolute;
+                        background: #fff url("../../assets/user/airwallexIcon.png") top center no-repeat;
+                        background-size: 100%;
+                        width: 0.55rem;
+                        height: 0.55rem;
+                        right: 0.2rem;
+                        top: 0.4rem;
+                    }
 
                     &.shandeMchIcon::after {
                         content: "";

+ 41 - 0
src/views/airwallex/index.vue

@@ -0,0 +1,41 @@
+<template>
+    <div class="airwallexWallet">
+        <s-header :name="$t('airwallex.wallet')" :noback="false"></s-header>
+        <div class="amount">
+            <van-cell-group inset>
+                <van-cell center :title="$t('airwallex.amount')" :value="amount" label="描述信息:可提取的金额" />
+            </van-cell-group>
+        </div>
+        <div class="airwallexWalletBtn">
+            <van-button round type="primary" class="register" @click="handleWithdraw">提现</van-button>
+        </div>
+    </div>
+</template>
+
+<script>
+import { useRouter } from 'vue-router';
+import sHeader from "@/components/SimpleHeader";
+import { styleUrl } from '../../common/js/utils';
+
+export default {
+    setup() {
+        const router = useRouter();
+
+        // TODO: 设置默认金额为 100
+        const amount = 100; 
+        styleUrl("airwallex")
+        const handleWithdraw = () => {
+
+            // 导航到提现页面
+            router.push('/airwallexPayment');
+        };
+
+        return {
+            amount,
+            handleWithdraw
+        };
+    },
+    components: { sHeader },
+
+}
+</script>

+ 148 - 0
src/views/airwallex/payment.vue

@@ -0,0 +1,148 @@
+<template>
+    <div class="airwallexPayment">
+        <s-header :name="$t('airwallex.payment')" :noback="false"></s-header>
+        <div id="container"></div>
+    </div>
+</template>
+  
+<script>
+import { init, createElement } from '@airwallex/payouts-web-sdk';
+import { Base64 } from 'js-base64';
+import { getAuthCode } from '@/service/airwallex/index';
+import { styleUrl } from '../../common/js/utils';
+import sHeader from "@/components/SimpleHeader";
+import { ref, onMounted } from 'vue';
+
+export default {
+    components: { sHeader },
+    setup() {
+        styleUrl("airwallex")
+        const ready = ref(false);
+        const element = ref(null);
+
+        onMounted(async () => {
+            // 生成 code_verifier 和 code_challenge
+            const codeVerifier = generateCodeVerifier();
+            const codeChallenge = await generateCodeChallengeFromVerifier(codeVerifier);
+
+            // 发送请求获取 AuthCode
+            const response = await getAuthCodeFun(codeChallenge);
+            // 初始化 SDK
+            await init({
+                langKey: 'en',
+                env: 'Staging',
+                authCode: response.authCode,
+                clientId: 'IBi-pVO4SSGzXIDHRYfVOA',
+                codeVerifier: codeVerifier,
+            });
+
+            element.value = createElement(type, options);
+            element.value.mount('#container');
+
+            element.value.on('ready', () => {
+                ready.value = true;
+                // 在组件准备就绪后处理逻辑
+            });
+
+            element.value.on('change', ({ value }) => {
+                // 处理表单值变化事件
+                console.log(value);
+            });
+
+            element.value.on('formState', ({ loading, validating, errors }) => {
+                // 处理表单状态变化事件;
+                console.log(loading, validating, errors);
+            });
+        });
+        
+
+        const handleFormSubmit = async () => {
+            if (ready.value) {
+                const formResult = await element.value.submit();
+                // 处理表单结果
+                console.log(formResult);
+            }
+        };
+
+        // Generate 生成 code_verifier
+        const dec2hex = (dec) => {
+            return ('0' + dec.toString(16)).slice(-2);
+        };
+
+        const generateCodeVerifier = () => {
+            // generate random length for code_verifier which should be between 43 and 128
+            // 生成长度介于43到128之间的随机长度的 code_verifier
+            const length = Math.random() * (129 - 43) + 43;
+            const array = new Uint32Array(length / 2);
+            window.crypto.getRandomValues(array);
+
+            return Array.from(array, dec2hex).join('');
+        };
+
+        const sha256 = (plain) => {
+            const encoder = new TextEncoder();
+            const data = encoder.encode(plain);
+            return window.crypto.subtle.digest('SHA-256', data);
+        };
+
+        const base64urlencode = (hashed) => {
+            const bytes = new Uint8Array(hashed);
+            const base64encoded = Base64.fromUint8Array(bytes, true);
+            return base64encoded;
+        };
+
+        const generateCodeChallengeFromVerifier = async (codeVerifier) => {
+            const hashed = await sha256(codeVerifier);
+            const base64encoded = base64urlencode(hashed);
+            return base64encoded;
+        };
+
+        const getAuthCodeFun = async (codeChallenge) => {
+            // 发送请求获取 AuthCode
+            const getAuthCodeDTO = {
+                codeChallenge: codeChallenge,
+                scope: [
+                    "w:awx_action:onboarding"
+                ]
+            };
+            const response = await getAuthCode(getAuthCodeDTO);
+            return response.data;
+        };
+
+
+        // type 字段:payoutForm,beneficiaryForm
+        const type = "payoutForm";
+        // options 字段:非必须
+        const options = {
+            // 设置默认字段值(可选)
+            defaultValues: {
+                field1: 'value1',
+                field2: 'value2',
+                // ...
+            },
+            // 自定义组件样式和行为(可选)
+            customizations: {
+                // 自定义样式
+                styles: {
+                    // ...
+                },
+                // 自定义字段验证逻辑
+                fieldValidators: {
+                    // ...
+                },
+                // ...
+            },
+            // 设置组件的主题(可选)
+            theme: {
+                // ...
+            },
+        };
+
+
+        return {
+            element,
+            handleFormSubmit
+        };
+    },
+};
+</script>

+ 61 - 106
src/views/user.vue

@@ -1,10 +1,6 @@
 <template>
   <div class="page flex-col">
-    <s-header
-        :name="sys ? sys.title : $t('public.sysName')"
-        :noback="true"
-        :isFixed="false"
-    ></s-header>
+    <s-header :name="sys ? sys.title : $t('public.sysName')" :noback="true" :isFixed="false"></s-header>
     <div class="userPageBox">
       <div class="userBaseBox">
         <div class="baseRow flex-row justify-between">
@@ -33,112 +29,82 @@
             <span class="userInfoLeft">{{ $t("user.region") }}: </span>
             <div class="cust_vantBorder">
               <div class="filedInpPad">
-                <van-field
-                    @click-input="fieldValueInpClk"
-                    readonly
-                    clearable
-                    v-model="fieldValue"
-                    :placeholder="$t('user.regionPlace')"
-                >
+                <van-field @click-input="fieldValueInpClk" readonly clearable v-model="fieldValue"
+                  :placeholder="$t('user.regionPlace')">
                   <template #right-icon>
                     <div class="l-flex-RC">
-                      <van-icon
-                          v-if="fieldValue"
-                          @click="
-                          fieldValue = '';
-                          accountDetail.areaId = '';
-                        "
-                          class="o-mr-6"
-                          name="clear"
-                      />
-                      <van-icon @click="fieldValueInpClk" name="arrow-down"/>
+                      <van-icon v-if="fieldValue" @click="
+                        fieldValue = '';
+                      accountDetail.areaId = '';
+                      " class="o-mr-6" name="clear" />
+                      <van-icon @click="fieldValueInpClk" name="arrow-down" />
                     </div>
                   </template>
                 </van-field>
               </div>
             </div>
-            <van-icon name="edit" class="editIcon" @click="editClk(4)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(4)" />
           </div>
           <div v-else class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.region") }}: </span>
             <span>{{ fieldValue }}</span>
-            <van-icon name="edit" class="editIcon" @click="editClk(4)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(4)" />
           </div>
           <!-- 手机号 -->
           <div v-if="!phoneNumberShow" class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.phoneNumber") }}: </span>
             <div class="cust_vantBorder">
-              <van-field
-                  class="relationClass"
-                  v-model="cofficentForm.phone"
-                  :placeholder="$t('user.phoneNumberPlace')"
-              >
+              <van-field class="relationClass" v-model="cofficentForm.phone" :placeholder="$t('user.phoneNumberPlace')">
                 <template #button>
-                  <van-button
-                      type="primary"
-                      @click="mailboxChg(cofficentForm.phone, 3)"
-                  >{{ $t("user.confirmLog") }}
+                  <van-button type="primary" @click="mailboxChg(cofficentForm.phone, 3)">{{ $t("user.confirmLog") }}
                   </van-button>
                 </template>
               </van-field>
             </div>
-            <van-icon name="edit" class="editIcon" @click="editClk(3)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(3)" />
           </div>
           <div v-else class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.phoneNumber") }}: </span>
             <span>{{ accountDetail.phone }}</span>
-            <van-icon name="edit" class="editIcon" @click="editClk(3)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(3)" />
           </div>
           <!-- 邮箱 -->
           <div v-if="!mailboxShow" class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.mailbox") }}: </span>
             <div class="cust_vantBorder">
-              <van-field
-                  class="relationClass"
-                  v-model="cofficentForm.mailBox"
-                  :placeholder="$t('user.mailboxPlace')"
-              >
+              <van-field class="relationClass" v-model="cofficentForm.mailBox" :placeholder="$t('user.mailboxPlace')">
                 <template #button>
-                  <van-button
-                      type="primary"
-                      @click="mailboxChg(cofficentForm.mailBox, 2)"
-                  >{{ $t("user.confirmLog") }}
+                  <van-button type="primary" @click="mailboxChg(cofficentForm.mailBox, 2)">{{ $t("user.confirmLog") }}
                   </van-button>
                 </template>
               </van-field>
             </div>
-            <van-icon name="edit" class="editIcon" @click="editClk(2)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(2)" />
           </div>
           <div v-else class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.mailbox") }}: </span>
             <span>{{ accountDetail.email }}</span>
-            <van-icon name="edit" class="editIcon" @click="editClk(2)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(2)" />
           </div>
           <!-- 关联上级 -->
           <div v-if="!relationType" class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.associateParent") }}: </span>
             <div class="cust_vantBorder">
-              <van-field
-                  class="relationClass"
-                  v-model="cofficentForm.associateParent"
-                  :placeholder="$t('user.associateParentPlace')"
-              >
+              <van-field class="relationClass" v-model="cofficentForm.associateParent"
+                :placeholder="$t('user.associateParentPlace')">
                 <template #button>
-                  <van-button
-                      type="primary"
-                      @click="mailboxChg(cofficentForm.associateParent, 1)"
-                  >{{ $t("user.confirmLog") }}
-                  </van-button
-                  >
+                  <van-button type="primary" @click="mailboxChg(cofficentForm.associateParent, 1)">{{
+                    $t("user.confirmLog") }}
+                  </van-button>
                 </template>
               </van-field>
             </div>
-            <van-icon name="edit" class="editIcon" @click="editClk(1)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(1)" />
           </div>
           <div v-else class="userInfo l-flex-between">
             <span class="userInfoLeft">{{ $t("user.associateParent") }}: </span>
             <span>{{ relationAdminName }}</span>
-            <van-icon name="edit" class="editIcon" @click="editClk(1)"/>
+            <van-icon name="edit" class="editIcon" @click="editClk(1)" />
           </div>
         </div>
       </div>
@@ -151,11 +117,7 @@
         </div>
         <div class="operListBox">
           <!-- 任务消息 -->
-          <div
-              v-if="roleCheck()"
-              class="taskListRow flex-col"
-              @click="pushPageList('/taskMessage')"
-          >
+          <div v-if="roleCheck()" class="taskListRow flex-col" @click="pushPageList('/taskMessage')">
             <div class="taskIcon taskMessageIcon"></div>
             <div class="taskRight">
               <div class="taskTitle">{{ $t("user.taskMessage") }}</div>
@@ -163,16 +125,21 @@
           </div>
 
           <!-- 提现帐号 -->
-          <div
-              class="taskListRow flex-col"
-              @click="pushPageList('/joinpayMch')"
-          >
+          <div class="taskListRow flex-col" @click="pushPageList('/joinpayMch')">
             <div class="taskIcon joinPayMchIcon"></div>
             <div class="taskRight">
               <div class="taskTitle">{{ $t("user.withdrawalAccountNo") }}</div>
             </div>
           </div>
 
+          <!-- Airwallex 钱包 -->
+          <div class="taskListRow flex-col" @click="pushPageList('/airwallex')">
+            <div class="taskIcon airwallexIcon"></div>
+            <div class="taskRight">
+              <div class="taskTitle">{{ $t("user.airwallex") }}</div>
+            </div>
+          </div>
+
           <!-- 备用提现账号 -->
           <!--          <div class="taskListRow flex-col" @click="pushPageList('/shandeMch')">
                       <div class="taskIcon shandeMchIcon"></div>
@@ -195,10 +162,7 @@
           </div>
 
           <!-- 修改密码 -->
-          <div
-              class="taskListRow flex-col"
-              @click="pushPageList('/changepassword')"
-          >
+          <div class="taskListRow flex-col" @click="pushPageList('/changepassword')">
             <div class="taskIcon changePasswordIcon"></div>
             <div class="taskRight">
               <div class="taskTitle">{{ $t("user.changePassword") }}</div>
@@ -224,22 +188,13 @@
     </div>
     <nav-bar></nav-bar>
     <!-- 退出登录弹窗 -->
-    <kDialog
-        :dialogTitle="$t('user.logOutTips')"
-        :cancelBtnTxt="$t('user.cancelLog')"
-        :confirmBtnTxt="$t('user.confirmLog')"
-        ref="kDialogRef"
-        :dialogContent="$t('user.logOutContent')"
-        @confirmclk="confirmClk"
-    >
+    <kDialog :dialogTitle="$t('user.logOutTips')" :cancelBtnTxt="$t('user.cancelLog')"
+      :confirmBtnTxt="$t('user.confirmLog')" ref="kDialogRef" :dialogContent="$t('user.logOutContent')"
+      @confirmclk="confirmClk">
     </kDialog>
     <!-- 地区弹窗 -->
-    <kCascader
-        @getareaname="getAreaName"
-        :selectId="accountDetail.areaId"
-        @areapopfinish="areaPopFinish"
-        ref="kCascaderRef"
-    ></kCascader>
+    <kCascader @getareaname="getAreaName" :selectId="accountDetail.areaId" @areapopfinish="areaPopFinish"
+      ref="kCascaderRef"></kCascader>
   </div>
 </template>
 
@@ -247,11 +202,11 @@
 // 导入地区弹窗
 import kCascader from "@/components/commom/kCascader/index.vue";
 // 导入接口
-import {getAdmin} from "@/service/merchantManage";
-import {updateAdmin} from "@/service/merchantManage";
+import { getAdmin } from "@/service/merchantManage";
+import { updateAdmin } from "@/service/merchantManage";
 // 引入弹窗
 import kDialog from "@/components/commom/kDialog/index.vue";
-import {onMounted, reactive, ref} from "vue";
+import { onMounted, reactive, ref } from "vue";
 import sHeader from "@/components/SimpleHeader";
 import navBar from "@/components/NavBar";
 import {
@@ -259,17 +214,17 @@ import {
   $M_EmailAvailable,
   $M_PhoneTest,
 } from "@/common/js/utils";
-import {useRouter} from "vue-router";
-import {tAdminGetRelation, tAdminSetRelationAdmin} from "@/service/user";
-import {Toast} from "vant";
-import {useI18n} from "vue-i18n";
-import {styleUrl} from "../common/js/utils";
+import { useRouter } from "vue-router";
+import { tAdminGetRelation, tAdminSetRelationAdmin } from "@/service/user";
+import { Toast } from "vant";
+import { useI18n } from "vue-i18n";
+import { styleUrl } from "../common/js/utils";
 
 export default {
-  components: {sHeader, navBar, kDialog, kCascader},
+  components: { sHeader, navBar, kDialog, kCascader },
   setup() {
     // 引入语言
-    const {t} = useI18n();
+    const { t } = useI18n();
     // 账户信息
     const accountDetail = ref({});
     // 控制都去显示隐藏
@@ -330,7 +285,7 @@ export default {
           if (!e) {
             Toast(t("user.associateParentPlace"));
           } else {
-            const {data} = await tAdminSetRelationAdmin({
+            const { data } = await tAdminSetRelationAdmin({
               adminId: user.id,
               username: e,
             });
@@ -353,7 +308,7 @@ export default {
               id: user.id,
               email: e,
             };
-            const {data} = await updateAdmin(params);
+            const { data } = await updateAdmin(params);
             mailboxShow.value = true;
             if (data.code === "00000") {
               Toast(data.message);
@@ -373,7 +328,7 @@ export default {
               id: user.id,
               phone: e,
             };
-            const {data} = await updateAdmin(params);
+            const { data } = await updateAdmin(params);
             phoneNumberShow.value = true;
             if (data.code === "00000") {
               Toast(data.message);
@@ -387,7 +342,7 @@ export default {
     };
     // 获取账户详情
     const getAcccountDetail = () => {
-      getAdmin({id: user.id}).then((res) => {
+      getAdmin({ id: user.id }).then((res) => {
         accountDetail.value = res.data.data;
 
         // 查询地址回显
@@ -407,12 +362,12 @@ export default {
         setTimeout(() => {
           router.push({
             path: "login",
-            query: {relation_admin_id: sys.value.relationAdminId},
+            query: { relation_admin_id: sys.value.relationAdminId },
           });
         }, 200);
       } else {
         setTimeout(() => {
-          router.push({path: "login"});
+          router.push({ path: "login" });
         }, 200);
       }
     };
@@ -431,7 +386,7 @@ export default {
       getAcccountDetail();
     });
     const gettAdminGetRelation = async () => {
-      const {data} = await tAdminGetRelation({
+      const { data } = await tAdminGetRelation({
         relationAdminId: user.relationAdminId,
       });
       if (typeof data === "string") {
@@ -439,13 +394,13 @@ export default {
       }
     };
     const operUnipay = () => {
-      router.push({path: "uniPay"});
+      router.push({ path: "uniPay" });
     };
     const onperExitSys = () => {
       kDialogRef.value.openDialog();
     };
     const pushPageList = (url) => {
-      router.push({path: url});
+      router.push({ path: url });
     };
     const roleCheck = () => {
       if (user.isAdmined) {
@@ -471,7 +426,7 @@ export default {
         id: user.id,
         areaId: e.selectId,
       };
-      const {data} = await updateAdmin(params);
+      const { data } = await updateAdmin(params);
       areaShow.value = true;
       if (data.code === "00000") {
         Toast(data.message);