Onlife/pages/login/forget.vue

675 lines
19 KiB
Vue
Raw Normal View History

2025-04-19 15:38:48 +08:00
<template>
<view class="container">
<view class="tab-container">
<view :class="['tab', { active: activeTab === 'register' }]" @tap="activeTab = 'register'">創建錢包</view>
<view :class="['tab', { active: activeTab === 'login' }]" @tap="activeTab = 'login'">導入錢包</view>
</view>
<!-- 創建錢包 -->
<view v-if="activeTab === 'register'" class="content">
<view class="input">
<input v-model="mobile" type="text" placeholder="用戶名" class="input_aa" style="width: 100%;"/>
</view>
<view class="input">
<input v-model="password" :password="showPassword1" data="passworedtype" placeholder="設置登錄密碼"
class="input_aa" />
<img :src="!showPassword1 ? showpwdImg : hidepwdImg" @tap="changePassword(1)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="password1" :password="showPassword2" placeholder="確認登錄密碼" class="input_aa" />
<img :src="!showPassword2 ? showpwdImg : hidepwdImg" @tap="changePassword(2)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="paykey" :password="showPassword3" placeholder="設置支付密碼" class="input_aa" />
<img :src="!showPassword3 ? showpwdImg : hidepwdImg" @tap="changePassword(3)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="paykey1" :password="showPassword4" placeholder="確認支付密碼" class="input_aa" />
<img :src="!showPassword4 ? showpwdImg : hidepwdImg" @tap="changePassword(4)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="email" type="text" placeholder="登錄郵箱" class="input_aa" />
</view>
<!-- 验证码 -->
<view class="input">
<input v-model="code" placeholder="郵箱驗證碼" class="input_aa" />
<view class="sendcode" @click="tosendemail">{{emaiTip}}</view>
</view>
<!-- 谷歌验证码 -->
<view class="input">
<input v-model="googlecode" placeholder="穀歌驗證碼" class="input_aa" />
<view class="sendcode" @click="googleemail">{{googleTip}}</view>
</view>
<view class="input">
<input v-model="pwallet" :disabled="codeIsLen" type="text" placeholder="好友地址" style="width: 100%;"/>
</view>
<button @tap="produceMnemonic" class="btn primary">生成助記詞</button>
</view>
<!-- 導入錢包 -->
<view v-if="activeTab === 'login'" class="content">
<textarea v-model="inputMnemonic" placeholder="請輸入您的助記詞" :maxlength="-1" class="input textarea" />
<view class="input">
<input v-model="mmobile" type="text" placeholder="用户名" style="width: 100%;"/>
</view>
<view class="input">
<input v-model="mpassword" :password="showPassword5" placeholder="設置登錄密碼" class="input_aa" />
<img :src="!showPassword5 ? showpwdImg : hidepwdImg" @tap="changePassword(5)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="mpassword1" :password="showPassword6" placeholder="確認登錄密碼" class="input_aa" />
<img :src="!showPassword6 ? showpwdImg : hidepwdImg" @tap="changePassword(6)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="mpaykey" :password="showPassword7" placeholder="請輸入支付密碼" class="input_aa" />
<img :src="!showPassword7 ? showpwdImg : hidepwdImg" @tap="changePassword(7)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="mpaykey1" :password="showPassword8" placeholder="請確定支付密碼" class="input_aa" />
<img :src="!showPassword8 ? showpwdImg : hidepwdImg" @tap="changePassword(8)"
style="width: 50rpx;height: 50rpx;"></img>
</view>
<view class="input">
<input v-model="memail" type="text" placeholder="登錄郵箱" style="width: 100%;"/>
</view>
<!-- 验证码 -->
<view class="input">
<input v-model="code" data="passworedtype" placeholder="郵箱驗證碼" class="input_aa" />
<view class="sendcode" @click="tosendemail">{{emaiTip}}</view>
</view>
<!-- 谷歌验证码 -->
<view class="input">
<input v-model="googlecode" data="passworedtype" placeholder="穀歌驗證碼" class="input_aa" />
<view class="sendcode" @click="googleemail">{{googleTip}}</view>
</view>
<button @tap="importWallet" class="btn primary">導入錢包</button>
</view>
<view class="googlecode" v-if="googleDialog">
<view class="icon" @click="googleDialog = false">
<u-icon name="close"></u-icon>
</view>
<view class="googletitle">
掃一掃獲取穀歌驗證碼
</view>
<view class="googleimg">
<image :src="googleimg"></image>
</view>
<view class="googlesecret" @click="copyadd()">
{{googlesecret}}
</view>
</view>
<!-- 助記詞彈窗 -->
<view v-if="mnemonic" class="mnemonic-container">
<text class="mnemonic" @tap="copyMnemonic">{{ mnemonic }}</text>
<text class="copy-hint">(點擊複製)</text>
<text class="warning">請妥善保管您的助記詞和密碼切勿洩露給他人</text>
</view>
</view>
</template>
<script>
import {_zhujiLogin,_register,_zhuji,_addresslogin,_emailSend,_getgoogleauthurl} from "@/request/api.js"
const bip39 = require('bip39');
const crypto = require('crypto');
const NodeRSA = require('node-rsa');
import DB from "@/common/sqlite";
import { getAddressAndPrivateKey } from "@/common/proAdress.js";
export default {
data() {
return {
2025-04-30 10:47:39 +08:00
userIsRegister:false,
2025-04-19 15:38:48 +08:00
googleimg: "",
googlesecret: "",
googleDialog: false,
emaiTip: "發送",
googleTip: "發送",
showPassword1:true,
showPassword2:true,
showPassword3:true,
showPassword4:true,
showPassword5:true,
showPassword6:true,
showPassword7:true,
showPassword8:true,
showpwdImg:"static/icon/eye_on.png",
hidepwdImg:"static/icon/eye_off.png",
mobile:"",
password: "",
password1:"",
paykey:"",
paykey1:"",
email: "",
memail: "",
code:"",
googlecode: "",
pid:"",
pwallet:"",
codeIsLen:false,
activeTab: 'register',
mnemonic: '',
privateKey:"",
confirmPassword: '',
inputMnemonic: '',//助記詞
mmobile:"",
mpassword: "",
mpassword1:"",
mpaykey:"",
mpaykey1:"",
provider:null,
}
},
onLoad(e) {
if(e.pwallet){
this.pwallet = e.pwallet;
this.codeIsLen = true;
}
},
2025-04-30 10:47:39 +08:00
watch:{
memail:{
handler(val){
this.userIsRegister = false;
},
immediate:true,
deep:true
},
},
2025-04-19 15:38:48 +08:00
methods: {
copyadd() {
uni.setClipboardData({
data: this.googlesecret,
success: () => {
uni.showToast({
title: '複製成功',
icon: 'success'
})
}
})
},
async tosendemail() {
if (this.emaiTip == "發送") {
let num = 60;
let emaiTimer = setInterval(() => {
this.emaiTip = num + 's';
num--;
if (num < 0) {
this.emaiTip = "發送";
clearInterval(emaiTimer)
}
}, 1000)
let res = await _emailSend({
email: this.activeTab == "register" ? this.email : this.memail,
event: "register"
});
if (res.code === 1) {
uni.showToast({
title: '發送成功',
icon: 'none'
});
}else{
uni.showToast({
title: res.msg,
icon: 'none'
});
2025-04-30 10:47:39 +08:00
this.userIsRegister = true;
2025-04-19 15:38:48 +08:00
}
} else {
return
}
},
async googleemail() {
2025-04-30 10:47:39 +08:00
if(!this.code || this.emaiTip == "發送"){
uni.showToast({
title: '請先寄送並輸入郵箱驗證碼',
icon: 'none'
});
return
}
2025-04-19 15:38:48 +08:00
if (this.googleTip == "發送") {
let num = 60;
let googleTimer = setInterval(() => {
this.googleTip = num + 's';
num--;
if (num < 0) {
this.googleTip = "發送";
clearInterval(googleTimer)
}
}, 1000)
2025-04-30 10:47:39 +08:00
if(this.userIsRegister) return
2025-04-19 15:38:48 +08:00
let res = await _getgoogleauthurl({
email: this.activeTab == "register" ? this.email : this.memail
});
if (res.code === 1) {
uni.showToast({
title: '發送成功',
icon: 'none'
});
this.googleDialog = true;
this.googleimg = res.data.url;
this.googlesecret = res.data.secret;
}else{
uni.showToast({
title: res.msg,
icon: 'none'
});
}
} else {
return
}
},
changePassword(n){
switch(n){
case 1 :
this.showPassword1 = !this.showPassword1;
break;
case 2 :
this.showPassword2 = !this.showPassword2;
break;
case 3 :
this.showPassword3 = !this.showPassword3;
break;
case 4 :
this.showPassword4 = !this.showPassword4;
break;
case 5 :
this.showPassword5 = !this.showPassword5;
break;
case 6 :
this.showPassword6 = !this.showPassword6;
break;
case 7 :
this.showPassword7 = !this.showPassword7;
break;
case 8 :
this.showPassword8 = !this.showPassword8;
break;
}
},
// 使用硬件随机数生成私钥 (模拟硬件随机生成,实际中可以使用硬件设备)
generatePrivateKey() {
const entropy = crypto.randomBytes(32); // 模拟硬件生成的随机数32字节
return entropy;
},
// 从私钥生成 BIP39 助记词24 个词)
generateMnemonic() {
const privateKey = this.generatePrivateKey(); // 生成私钥
const mnemonic = bip39.entropyToMnemonic(privateKey); // 使用 BIP39 生成助记词
return mnemonic;
},
// 双重加密AES + RSA 加密
encryptMnemonicWithAES(mnemonic, password) {
// 将密码通过 SHA-256 哈希函数变换为 32 字节的密钥
const key = crypto.createHash('sha256').update(password).digest();
// 创建一个随机的初始化向量IV
const iv = crypto.randomBytes(16); // AES-CBC 模式下需要 IV
// 使用 AES 加密助记词
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(mnemonic, 'utf8', 'hex');
encrypted += cipher.final('hex');
// 使用 node-rsa 替代内置的 RSA 功能
const rsaKey = new NodeRSA({
b: 2048
});
const encryptedWithRSA = rsaKey.encrypt(encrypted, 'hex');
return {
rsaEncryptedMnemonic: encryptedWithRSA,
privateKey: rsaKey.exportKey('private'),
iv: iv.toString('hex'),
};
},
async produceMnemonic() {
DB.openSqlite();
let sql =
'"email" text,"password" text,"moneyAdress" text,"mnemonic" text,"privateKeyMne" text,"privateKeyPre" text,"privateKeyMoney" text,"mnemonicIV" text,"privateIV" text';
await DB.createTable('regUser', sql);
if (!this.mobile || !this.password || !this.password1 || this.password !== this.password1 || !this
.paykey || !this.paykey1 || this.paykey !== this.paykey1 || !this.email || !this.code || !this.googlecode) {
uni.showToast({
title: '請填寫正確資訊',
icon: 'none'
});
return;
}
uni.showLoading({
title: "註冊中..."
})
let _that = this;
// 生成私钥、助记词、并进行加密
const mnemonic = _that.generateMnemonic();
let userInfos = getAddressAndPrivateKey(mnemonic);
// 加密助记词AES + RSA
const password = _that.paykey; // 用于加密助记词的密码
const encryptedData = _that.encryptMnemonicWithAES(mnemonic, password);
const rsaEncryptedMnemonic = encryptedData.rsaEncryptedMnemonic;
const privateKeyMne = encryptedData.privateKey;
const mnemonicIV = encryptedData.iv;
// 加密私钥
const encryptedData1 = _that.encryptMnemonicWithAES(userInfos.privateKey, password);
const rsaMoneyPri = encryptedData1.rsaEncryptedMnemonic;
const privateKeyPre = encryptedData1.privateKey;
const privateIV = encryptedData1.iv;
const params = {
username: _that.mobile,
email: _that.email,
password: _that.password,
paykey: _that.paykey,
code: _that.code,
googlecode: _that.googlecode,
pid: _that.pid,
wallet: JSON.stringify({
address: userInfos.address
}),
mch: "",
pwallet: _that.pwallet
};
try {
let res = await _register(params);
if (res.code === 1) {
let spl =
`'${_that.email}','${_that.password}','${userInfos.address}','${rsaEncryptedMnemonic}','${privateKeyMne}','${privateKeyPre}','${rsaMoneyPri}','${mnemonicIV}','${privateIV}'`;
let condition =
"'email','password','moneyAdress','mnemonic','privateKeyMne','privateKeyPre','privateKeyMoney','mnemonicIV','privateIV'";
await DB.insertTableData(DB.regTable, spl, condition);
_that.mnemonic = mnemonic;
uni.showToast({
title: "註冊成功",
icon: 'none'
});
uni.hideLoading();
} else {
uni.showToast({
title: res.msg,
icon: 'none'
});
uni.hideLoading()
}
} catch (err) {
uni.showToast({
title: err,
icon: 'none'
});
uni.hideLoading()
}
},
async importWallet() {
DB.openSqlite();
let sql =
'"email" text,"password" text,"moneyAdress" text,"mnemonic" text,"privateKeyMne" text,"privateKeyPre" text,"privateKeyMoney" text,"mnemonicIV" text,"privateIV" text';
await DB.createTable('regUser', sql);
if (!this.inputMnemonic || !this.mmobile || !this.mpassword || !this.mpassword1 || !this.mpaykey || !
this.mpaykey1 || !this.memail || !this.code || !this.googlecode) {
uni.showToast({
title: '請填寫正確資訊',
icon: 'none'
});
return;
}
uni.showLoading({
title: "導入中..."
})
let _that = this;
let obj = getAddressAndPrivateKey(_that.inputMnemonic);
// 加密助记词AES + RSA
const password = _that.mpaykey1; // 用于加密助记词的密码
const encryptedData = _that.encryptMnemonicWithAES(_that.inputMnemonic, password);
const rsaEncryptedMnemonic = encryptedData.rsaEncryptedMnemonic;
const privateKeyMne = encryptedData.privateKey;
const mnemonicIV = encryptedData.iv;
// 加密私钥
const encryptedData1 = _that.encryptMnemonicWithAES(obj.privateKey, password);
const rsaMoneyPri = encryptedData1.rsaEncryptedMnemonic;
const privateKeyPre = encryptedData1.privateKey;
const privateIV = encryptedData1.iv;
let wallet = {
address: obj.address,
type: "Ethereum"
};
const params = {
username: _that.mmobile,
email: _that.memail,
password: _that.mpassword,
paykey: _that.mpaykey,
code: _that.code,
googlecode: _that.googlecode,
pid: _that.pid,
wallet: JSON.stringify(wallet),
mch: "",
pwallet: _that.pwallet
};
let resUser = await _addresslogin({
address: obj.address
});
if (resUser.code === 1) {
uni.hideLoading()
let inputObj = resUser.data.userinfo;
let spl =
`'${inputObj.email}','${inputObj.paykey}','${obj.address}','${rsaEncryptedMnemonic}','${privateKeyMne}','${privateKeyPre}','${rsaMoneyPri}','${mnemonicIV}','${privateIV}'`;
let condition =
"'email','password','moneyAdress','mnemonic','privateKeyMne','privateKeyPre','privateKeyMoney','mnemonicIV','privateIV'";
await DB.insertTableData(DB.regTable, spl, condition);
uni.showModal({
title: "提示",
content: "該助記詞已註冊是否跳轉登錄",
success(rss) {
if (rss.confirm) {
uni.navigateTo({
url: '/pages/login/login?email=' + inputObj.email + '&password=' + _that.mpassword
})
}
}
})
} else {
let resss = await _register(params);
if (resss.code === 1) {
uni.hideLoading()
let user_id = resss.data.userinfo.id;
let spl =
`'${_that.memail}','${_that.mpaykey}','${obj.address}','${rsaEncryptedMnemonic}','${privateKeyMne}','${privateKeyPre}','${rsaMoneyPri}','${mnemonicIV}','${privateIV}'`;
let condition =
"'email','password','moneyAdress','mnemonic','privateKeyMne','privateKeyPre','privateKeyMoney','mnemonicIV','privateIV'";
await DB.insertTableData(DB.regTable, spl, condition);
uni.navigateTo({
url: '/pages/login/login?email=' + _that.memail + '&password=' + _that.mpassword
})
} else {
uni.hideLoading()
uni.showToast({
title: resss.msg,
icon: 'none'
});
}
}
},
beginLogon() {
if (this.codeIsLen) {
window.location.href = "https://onlif.klinygm.com/release/"
} else {
uni.navigateTo({
url: '/pages/login/login?email=' + this.email + '&password=' + this.password
})
}
},
copyMnemonic() {
let _that = this;
let titleMessage = "";
if (_that.codeIsLen) {
titleMessage = "是否下載登錄?"
} else {
titleMessage = "是否跳轉登錄?"
}
uni.setClipboardData({
data: _that.mnemonic,
success: () => {
uni.showModal({
title: "助記詞已複製",
content: titleMessage,
success(res) {
if (res.confirm) {
_that.beginLogon();
}
}
})
}
});
}
}
}
</script>
<style lang="scss">
.container {
padding: 20px;
background: linear-gradient(to bottom, #000033, #51599b);
min-height: 100vh;
position: relative;
}
.sendcode {
text-align: right;
width: 20%;
cursor: pointer;
}
.tab-container {
display: flex;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
}
.tab {
flex: 1;
text-align: center;
padding: 15px 0;
font-size: 16px;
color: #ffffff;
}
.tab.active {
background-color: rgba(0, 122, 255, 0.6);
color: white;
}
.content {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 20px;
}
.btn {
width: 100%;
height: 50px;
line-height: 50px;
text-align: center;
border-radius: 25px;
font-size: 18px;
margin-top: 20px;
}
.btn.primary {
background-color: rgba(0, 122, 255, 0.8);
color: white;
}
.mnemonic-container {
width: 60%;
background-color: #fff;
padding: 15px;
border-radius: 8px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.mnemonic {
font-size: 16px;
line-height: 1.5;
word-break: break-all;
color: #000;
}
.copy-hint {
font-size: 12px;
color: #aaaaaa;
margin-left: 5px;
}
.warning {
color: #ff9900;
font-size: 14px;
margin-top: 10px;
display: block;
}
.input {
width: 95%;
height: 60px;
border: 1px solid rgba(221, 221, 221, 0.3);
border-radius: 8px;
padding: 0 10px;
/* box-sizing: border-box; */
font-size: 16px;
margin-bottom: 10px;
background-color: rgba(255, 255, 255, 0.1);
color: #ffffff;
display: flex;
align-items: center;
}
.input_aa{
width: 90%;
font-size: 16px;
height: 60%;
}
.input.textarea {
height: 100px;
padding-top: 20rpx;
}
.input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.googlecode {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 70vw;
height: 70vw;
background-color: #fff;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.icon {
width: 90%;
display: flex;
justify-content: end;
}
.googleimg {
width: 50vw;
height: 50vw;
padding: 20rpx;
box-sizing: border-box;
display: flex;
image {
width: 100%;
height: 100%;
}
}
}
</style>