Onlife/pages/login/forget.vue
2025-04-30 10:47:39 +08:00

675 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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 {
userIsRegister:false,
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;
}
},
watch:{
memail:{
handler(val){
this.userIsRegister = false;
},
immediate:true,
deep:true
},
},
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'
});
this.userIsRegister = true;
}
} else {
return
}
},
async googleemail() {
if(!this.code || this.emaiTip == "發送"){
uni.showToast({
title: '請先寄送並輸入郵箱驗證碼',
icon: 'none'
});
return
}
if (this.googleTip == "發送") {
let num = 60;
let googleTimer = setInterval(() => {
this.googleTip = num + 's';
num--;
if (num < 0) {
this.googleTip = "發送";
clearInterval(googleTimer)
}
}, 1000)
if(this.userIsRegister) return
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>