init
This commit is contained in:
commit
2b8870a40e
51 changed files with 5845 additions and 0 deletions
721
pages/index/index.js
Normal file
721
pages/index/index.js
Normal file
|
@ -0,0 +1,721 @@
|
|||
// index.js
|
||||
const {
|
||||
generateCode,
|
||||
getRemainingSeconds,
|
||||
formatTime,
|
||||
syncTokens
|
||||
} = require('../../utils/util');
|
||||
const storage = require('../../utils/storage');
|
||||
const eventManager = require('../../utils/eventManager');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
tokens: [],
|
||||
remainingSeconds: {}, // 存储每个令牌的剩余时间
|
||||
loading: true,
|
||||
syncing: false,
|
||||
error: null
|
||||
},
|
||||
|
||||
onLoad: function() {
|
||||
// 初始化页面数据
|
||||
this.loadTokens();
|
||||
|
||||
// 监听令牌更新事件
|
||||
this.tokensUpdatedListener = (tokens) => {
|
||||
this.loadTokens();
|
||||
};
|
||||
eventManager.on('tokensUpdated', this.tokensUpdatedListener);
|
||||
|
||||
// 初始化页面路由
|
||||
this.pageRoutes = {
|
||||
form: '/pages/form/form',
|
||||
edit: '/pages/edit/edit'
|
||||
};
|
||||
},
|
||||
|
||||
onShow: function() {
|
||||
// 先加载令牌列表
|
||||
this.loadTokens();
|
||||
// 然后延迟100ms刷新验证码,确保tokens已经加载
|
||||
setTimeout(() => {
|
||||
this.refreshTokens();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
onPullDownRefresh: function() {
|
||||
this.refreshTokens().then(() => {
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
async loadTokens() {
|
||||
try {
|
||||
this.setData({ loading: true, error: null });
|
||||
|
||||
// 从本地存储加载令牌
|
||||
const tokens = await wx.getStorageSync('tokens') || [];
|
||||
// console.log('[DEBUG] Loaded tokens count:', tokens.length);
|
||||
|
||||
// 统计令牌类型
|
||||
const typeCounts = tokens.reduce((acc, token) => {
|
||||
acc[token.type] = (acc[token.type] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
// console.log('[DEBUG] Token types:', typeCounts);
|
||||
|
||||
// 初始化剩余时间
|
||||
const remainingSeconds = {};
|
||||
tokens.forEach(token => {
|
||||
if (token.type === 'totp') {
|
||||
const period = token.period || 30;
|
||||
const remaining = getRemainingSeconds(period);
|
||||
// console.log(`[DEBUG] Token ${token.id} period=${period}, remaining=${remaining}`);
|
||||
remainingSeconds[token.id] = remaining;
|
||||
}
|
||||
});
|
||||
|
||||
// 获取当前时间戳
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
// 先更新验证码
|
||||
let tokensToSet = [...tokens]; // 创建tokens的副本
|
||||
if (tokensToSet.length > 0) {
|
||||
const updatedTokens = await this.updateTokenCodes(tokensToSet);
|
||||
if (Array.isArray(updatedTokens)) {
|
||||
tokensToSet = updatedTokens;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置状态
|
||||
this.setData({
|
||||
tokens: tokensToSet,
|
||||
remainingSeconds,
|
||||
loading: false
|
||||
});
|
||||
|
||||
// 设置定时器,定期更新TOTP令牌
|
||||
this.setupRefreshTimer();
|
||||
} catch (error) {
|
||||
console.error('Failed to load tokens:', error);
|
||||
this.setData({
|
||||
error: '加载令牌失败: ' + error.message,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async refreshTokens() {
|
||||
try {
|
||||
const currentTokens = [...this.data.tokens]; // 创建tokens的副本
|
||||
const updatedTokens = await this.updateTokenCodes(currentTokens);
|
||||
|
||||
// 更新剩余时间
|
||||
const remainingSeconds = {};
|
||||
updatedTokens.forEach(token => {
|
||||
if (token.type === 'totp') {
|
||||
remainingSeconds[token.id] = getRemainingSeconds(token.period || 30);
|
||||
}
|
||||
});
|
||||
|
||||
this.setData({
|
||||
tokens: updatedTokens,
|
||||
remainingSeconds
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh tokens:', error);
|
||||
this.setData({
|
||||
error: '刷新令牌失败: ' + error.message
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setupRefreshTimer() {
|
||||
// 清除现有的定时器
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer);
|
||||
}
|
||||
|
||||
// console.log('[DEBUG] Setting up refresh timer...');
|
||||
|
||||
// 设置新的定时器,每秒检查一次
|
||||
this.refreshTimer = setInterval(() => {
|
||||
// console.log('[DEBUG] Timer tick - checking tokens...');
|
||||
|
||||
const currentTokens = [...this.data.tokens]; // 创建tokens的副本
|
||||
const currentRemainingSeconds = this.data.remainingSeconds || {};
|
||||
const remainingSeconds = {};
|
||||
const tokensToUpdate = [];
|
||||
|
||||
currentTokens.forEach(token => {
|
||||
// 处理所有支持的OTP类型
|
||||
const supportedTypes = ['totp', 'hotp', 'otpauth'];
|
||||
if (!supportedTypes.includes(token.type)) {
|
||||
// console.log(`[DEBUG] Skipping unsupported token type: ${token.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log(`[DEBUG] Processing ${token.type.toUpperCase()} token ${token.id || token.name}`);
|
||||
|
||||
// 针对不同类型做特殊处理
|
||||
switch(token.type) {
|
||||
case 'totp':
|
||||
// 标准TOTP处理逻辑
|
||||
break;
|
||||
case 'hotp':
|
||||
// HOTP特定处理
|
||||
// console.log(`[DEBUG] HOTP token detected, counter: ${token.counter}`);
|
||||
break;
|
||||
case 'otpauth':
|
||||
// 扫描二维码生成的令牌
|
||||
// console.log(`[DEBUG] OTPAUTH URI token detected`);
|
||||
break;
|
||||
}
|
||||
|
||||
const period = token.period || 30;
|
||||
const remaining = getRemainingSeconds(period);
|
||||
const prevRemaining = currentRemainingSeconds[token.id];
|
||||
|
||||
// console.log(`[DEBUG] Token ${token.id}: prev=${prevRemaining}, curr=${remaining}`);
|
||||
|
||||
// 更新剩余时间
|
||||
remainingSeconds[token.id] = remaining;
|
||||
|
||||
// 在以下情况下更新token:
|
||||
// 1. 剩余时间为0(新的时间窗口开始)
|
||||
// 2. 之前没有剩余时间记录(首次加载)
|
||||
// 3. 时间窗口发生变化(prevRemaining > remaining,说明跨越了时间窗口)
|
||||
if (remaining === 0 || prevRemaining === undefined || (prevRemaining > 0 && remaining < prevRemaining)) {
|
||||
// console.log(`[DEBUG] Token ${token.id} needs update`);
|
||||
tokensToUpdate.push({...token}); // 创建token的副本
|
||||
}
|
||||
});
|
||||
|
||||
// 只在有变化时更新剩余时间
|
||||
if (Object.keys(remainingSeconds).length > 0) {
|
||||
// console.log('[DEBUG] Updating remainingSeconds:', remainingSeconds);
|
||||
this.setData({
|
||||
'remainingSeconds': {
|
||||
...currentRemainingSeconds,
|
||||
...remainingSeconds
|
||||
}
|
||||
}, () => {
|
||||
// console.log('[DEBUG] remainingSeconds updated');
|
||||
});
|
||||
}
|
||||
|
||||
// 只更新需要更新的token,并传入当前时间戳
|
||||
if (tokensToUpdate.length > 0) {
|
||||
// console.log('[DEBUG] Updating tokens:', tokensToUpdate.length);
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
this.updateSelectedTokens(tokensToUpdate, timestamp);
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// 只更新选定的token
|
||||
async updateSelectedTokens(tokensToUpdate, timestamp) {
|
||||
try {
|
||||
const currentTokens = this.data.tokens;
|
||||
// 获取当前时间戳,如果没有传入
|
||||
const currentTimestamp = timestamp || Math.floor(Date.now() / 1000);
|
||||
|
||||
// 为每个token计算正确的时间戳
|
||||
const updatePromises = tokensToUpdate.map(token => {
|
||||
if (token.type === 'totp') {
|
||||
// 计算时间窗口的开始时间
|
||||
const period = token.period || 30;
|
||||
const windowStart = Math.floor(currentTimestamp / period) * period;
|
||||
return this.updateTokenCode(token, windowStart);
|
||||
} else {
|
||||
// 对于HOTP类型,直接使用当前时间戳
|
||||
return this.updateTokenCode(token, currentTimestamp);
|
||||
}
|
||||
});
|
||||
|
||||
const updatedTokens = await Promise.all(updatePromises);
|
||||
|
||||
// 创建token ID到更新后token的映射
|
||||
const updatedTokenMap = {};
|
||||
updatedTokens.forEach(token => {
|
||||
updatedTokenMap[token.id] = token;
|
||||
});
|
||||
|
||||
// 更新tokens数组中的特定token
|
||||
const newTokens = currentTokens.map(token =>
|
||||
updatedTokenMap[token.id] ? updatedTokenMap[token.id] : token
|
||||
);
|
||||
|
||||
this.setData({ tokens: newTokens });
|
||||
} catch (error) {
|
||||
console.error('更新选定token失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async updateTokenCodes(tokens) {
|
||||
if (!Array.isArray(tokens) || tokens.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取当前时间戳
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
// 并行更新所有令牌的验证码,为每个令牌计算其时间窗口的开始时间
|
||||
const updatePromises = tokens.map(token => {
|
||||
if (token.type === 'totp') {
|
||||
// 计算时间窗口的开始时间
|
||||
const period = token.period || 30;
|
||||
const windowStart = Math.floor(currentTimestamp / period) * period;
|
||||
return this.updateTokenCode(token, windowStart);
|
||||
} else {
|
||||
// 对于HOTP类型,直接使用当前时间戳
|
||||
return this.updateTokenCode(token, currentTimestamp);
|
||||
}
|
||||
});
|
||||
|
||||
const updatedTokens = await Promise.all(updatePromises);
|
||||
return updatedTokens;
|
||||
} catch (error) {
|
||||
console.error('更新验证码失败:', error);
|
||||
// 出错时返回原始tokens,避免undefined
|
||||
return tokens;
|
||||
}
|
||||
},
|
||||
|
||||
async updateTokenCode(token, timestamp) {
|
||||
try {
|
||||
// 生成新的验证码,使用传入的统一时间戳
|
||||
const code = await generateCode({
|
||||
type: token.type,
|
||||
secret: token.secret,
|
||||
algorithm: token.algorithm || 'SHA1',
|
||||
digits: token.digits || 6,
|
||||
period: token.period || 30,
|
||||
counter: token.counter,
|
||||
timestamp: timestamp // 使用传入的统一时间戳
|
||||
});
|
||||
|
||||
// 更新令牌数据
|
||||
token.code = code;
|
||||
token.lastUpdate = formatTime(new Date());
|
||||
|
||||
return token;
|
||||
} catch (error) {
|
||||
console.error(`更新令牌 ${token.id} 失败:`, error);
|
||||
token.error = error.message;
|
||||
return token;
|
||||
}
|
||||
},
|
||||
|
||||
async refreshHotpToken(event) {
|
||||
const tokenId = event.currentTarget.dataset.tokenId;
|
||||
const token = this.data.tokens.find(t => t.id === tokenId);
|
||||
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
// 增加计数器值
|
||||
const newCounter = (token.counter || 0) + 1;
|
||||
|
||||
// 生成新的验证码
|
||||
const code = await generateCode({
|
||||
type: 'hotp',
|
||||
secret: token.secret,
|
||||
algorithm: token.algorithm || 'SHA1',
|
||||
counter: newCounter,
|
||||
digits: token.digits || 6
|
||||
});
|
||||
|
||||
// 更新令牌数据
|
||||
token.code = code;
|
||||
token.counter = newCounter;
|
||||
token.lastUpdate = formatTime(new Date());
|
||||
|
||||
// 更新存储和状态
|
||||
const tokens = this.data.tokens.map(t =>
|
||||
t.id === token.id ? token : t
|
||||
);
|
||||
|
||||
await wx.setStorageSync('tokens', tokens);
|
||||
this.setData({ tokens });
|
||||
|
||||
// 同步到云端
|
||||
if (this.data.syncing) {
|
||||
await syncTokens(tokens);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`刷新HOTP令牌 ${token.id} 失败:`, error);
|
||||
this.setData({
|
||||
error: '刷新HOTP令牌失败: ' + error.message
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async syncWithCloud() {
|
||||
try {
|
||||
this.setData({ syncing: true, error: null });
|
||||
|
||||
// 获取本地令牌
|
||||
const localTokens = this.data.tokens;
|
||||
|
||||
// 同步令牌
|
||||
const syncedTokens = await syncTokens(localTokens);
|
||||
|
||||
// 更新所有令牌的验证码
|
||||
await this.updateTokenCodes(syncedTokens);
|
||||
|
||||
// 更新存储和状态
|
||||
await wx.setStorageSync('tokens', syncedTokens);
|
||||
this.setData({
|
||||
tokens: syncedTokens,
|
||||
syncing: false
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('同步失败:', error);
|
||||
this.setData({
|
||||
error: '同步失败: ' + error.message,
|
||||
syncing: false
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onUnload: function() {
|
||||
// 清除定时器
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer);
|
||||
}
|
||||
|
||||
// 移除令牌更新事件监听
|
||||
if (this.tokensUpdatedListener) {
|
||||
eventManager.off('tokensUpdated', this.tokensUpdatedListener);
|
||||
}
|
||||
},
|
||||
|
||||
// 处理令牌点击事件
|
||||
handleTokenTap(event) {
|
||||
const tokenId = event.currentTarget.dataset.tokenId;
|
||||
const token = this.data.tokens.find(t => t.id === tokenId);
|
||||
|
||||
if (!token) return;
|
||||
|
||||
// 复制验证码到剪贴板
|
||||
wx.setClipboardData({
|
||||
data: token.code,
|
||||
success: () => {
|
||||
wx.showToast({
|
||||
title: '验证码已复制',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 显示添加令牌菜单
|
||||
showAddTokenMenu() {
|
||||
wx.showActionSheet({
|
||||
itemList: ['扫描二维码', '手动添加'],
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0: // 扫描二维码
|
||||
wx.scanCode({
|
||||
onlyFromCamera: false,
|
||||
scanType: ['qrCode'],
|
||||
success: (res) => {
|
||||
try {
|
||||
// 解析二维码内容
|
||||
const qrContent = res.result;
|
||||
// 如果是otpauth://格式的URL
|
||||
if (qrContent.startsWith('otpauth://')) {
|
||||
// 小程序兼容的URL解析
|
||||
const [protocolAndPath, search] = qrContent.split('?');
|
||||
const [protocol, path] = protocolAndPath.split('://');
|
||||
const type = protocol.replace('otpauth:', '');
|
||||
|
||||
// 解析路径部分
|
||||
const decodedPath = decodeURIComponent(path.substring(1)); // 移除开头的/
|
||||
let [issuer, remark] = decodedPath.split(':');
|
||||
if (!remark) {
|
||||
remark = issuer;
|
||||
issuer = '';
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
const params = {};
|
||||
if (search) {
|
||||
search.split('&').forEach(pair => {
|
||||
const [key, value] = pair.split('=');
|
||||
if (key && value) {
|
||||
params[key] = decodeURIComponent(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 从参数中获取issuer(如果存在)
|
||||
if (params.issuer) {
|
||||
issuer = params.issuer;
|
||||
}
|
||||
|
||||
// 验证secret参数
|
||||
if (!params.secret) {
|
||||
wx.showToast({
|
||||
title: '无效的二维码:缺少secret参数',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 将otpauth类型转换为实际类型
|
||||
let validType = type.toLowerCase();
|
||||
if (validType === 'otpauth') {
|
||||
// 从URI路径中提取实际类型
|
||||
validType = path.split('/')[0].toLowerCase();
|
||||
}
|
||||
|
||||
// 构建表单数据,确保数字类型参数正确转换
|
||||
const formData = {
|
||||
type: validType,
|
||||
issuer,
|
||||
remark,
|
||||
secret: params.secret,
|
||||
algorithm: params.algorithm || 'SHA1',
|
||||
digits: params.digits ? parseInt(params.digits, 10) : 6,
|
||||
period: validType === 'totp' ? (params.period ? parseInt(params.period, 10) : 30) : undefined,
|
||||
counter: validType === 'hotp' ? (params.counter ? parseInt(params.counter, 10) : 0) : undefined
|
||||
};
|
||||
|
||||
// 验证必要参数
|
||||
if (formData.digits < 6 || formData.digits > 8) {
|
||||
formData.digits = 6;
|
||||
console.warn('验证码位数无效,已设置为默认值6');
|
||||
}
|
||||
|
||||
if (validType === 'totp' && (formData.period < 15 || formData.period > 60)) {
|
||||
formData.period = 30;
|
||||
console.warn('TOTP周期无效,已设置为默认值30秒');
|
||||
}
|
||||
|
||||
// 检查必要参数是否完整
|
||||
if (formData.secret && formData.type) {
|
||||
// 直接添加令牌
|
||||
this.addTokenDirectly(formData).then(() => {
|
||||
wx.showToast({
|
||||
title: '令牌已添加',
|
||||
icon: 'success'
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error('添加令牌失败:', error);
|
||||
// 失败后跳转到表单页面
|
||||
const queryString = Object.entries(formData)
|
||||
.filter(([_, value]) => value !== undefined)
|
||||
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
||||
.join('&');
|
||||
|
||||
wx.navigateTo({
|
||||
url: `${this.pageRoutes.form}?${queryString}`
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 参数不完整,跳转到表单页面
|
||||
const queryString = Object.entries(formData)
|
||||
.filter(([_, value]) => value !== undefined)
|
||||
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
||||
.join('&');
|
||||
|
||||
wx.navigateTo({
|
||||
url: `${this.pageRoutes.form}?${queryString}`
|
||||
});
|
||||
}
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: '无效的二维码格式',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析二维码失败:', error);
|
||||
wx.showToast({
|
||||
title: '解析二维码失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('扫描二维码失败:', error);
|
||||
if (error.errMsg.includes('cancel')) {
|
||||
// 用户取消扫码,不显示错误提示
|
||||
return;
|
||||
}
|
||||
wx.showToast({
|
||||
title: '扫描失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 1: // 手动添加
|
||||
wx.navigateTo({
|
||||
url: '/pages/form/form',
|
||||
events: {
|
||||
// 监听form页面的事件
|
||||
tokenAdded: () => {
|
||||
this.loadTokens();
|
||||
}
|
||||
},
|
||||
success: () => {
|
||||
// 跳转成功后的回调
|
||||
wx.showToast({
|
||||
title: '请填写令牌信息',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 编辑令牌
|
||||
editToken(event) {
|
||||
const id = event.currentTarget.dataset.tokenId;
|
||||
wx.navigateTo({
|
||||
url: `${this.pageRoutes.edit}?token_id=${id}`
|
||||
});
|
||||
},
|
||||
|
||||
// 删除令牌
|
||||
deleteToken(event) {
|
||||
const id = event.currentTarget.dataset.id;
|
||||
const that = this;
|
||||
|
||||
wx.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除这个令牌吗?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
// 从本地存储中删除
|
||||
wx.getStorage({
|
||||
key: 'tokens',
|
||||
success(res) {
|
||||
const tokens = res.data.filter(token => token.id !== id);
|
||||
wx.setStorage({
|
||||
key: 'tokens',
|
||||
data: tokens,
|
||||
success() {
|
||||
// 更新UI
|
||||
that.setData({ tokens });
|
||||
wx.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 如果正在同步,更新云端
|
||||
if (that.data.syncing) {
|
||||
syncTokens(tokens).catch(err => {
|
||||
console.error('同步删除失败:', err);
|
||||
});
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
wx.showToast({
|
||||
title: '删除失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 直接添加令牌
|
||||
async addTokenDirectly(tokenData) {
|
||||
try {
|
||||
// 获取当前令牌列表
|
||||
const tokens = await wx.getStorageSync('tokens') || [];
|
||||
|
||||
// 生成唯一ID和时间戳
|
||||
const newToken = {
|
||||
...tokenData,
|
||||
id: Date.now().toString(),
|
||||
createdAt: new Date().toISOString(),
|
||||
lastUpdate: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 如果是TOTP类型,先初始化剩余时间
|
||||
if ((newToken.type || 'totp').toLowerCase() === 'totp') {
|
||||
const period = parseInt(newToken.period || '30', 10);
|
||||
const currentRemainingSeconds = this.data.remainingSeconds || {};
|
||||
|
||||
// 先更新剩余时间,避免闪烁
|
||||
this.setData({
|
||||
remainingSeconds: {
|
||||
...currentRemainingSeconds,
|
||||
[newToken.id]: getRemainingSeconds(period)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 生成验证码
|
||||
try {
|
||||
// 确保类型是字符串且为小写
|
||||
const type = (newToken.type || 'totp').toLowerCase();
|
||||
// 确保digits是数字
|
||||
const digits = parseInt(newToken.digits || '6', 10);
|
||||
// 确保period是数字
|
||||
const period = parseInt(newToken.period || '30', 10);
|
||||
// 确保counter是数字(如果存在)
|
||||
const counter = newToken.counter ? parseInt(newToken.counter, 10) : undefined;
|
||||
|
||||
const code = await generateCode({
|
||||
type,
|
||||
secret: newToken.secret,
|
||||
algorithm: newToken.algorithm || 'SHA1',
|
||||
digits,
|
||||
period,
|
||||
counter
|
||||
});
|
||||
newToken.code = code;
|
||||
} catch (error) {
|
||||
console.error('生成验证码失败:', error);
|
||||
wx.showToast({
|
||||
title: '生成验证码失败: ' + error.message,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 添加到列表并保存
|
||||
const updatedTokens = [...tokens, newToken];
|
||||
await wx.setStorageSync('tokens', updatedTokens);
|
||||
|
||||
// 更新UI,使用setData的回调确保UI更新完成
|
||||
this.setData({ tokens: updatedTokens }, () => {
|
||||
// 触发更新事件
|
||||
eventManager.emit('tokensUpdated', updatedTokens);
|
||||
|
||||
// 如果正在同步,更新云端
|
||||
if (this.data.syncing) {
|
||||
syncTokens(updatedTokens).catch(err => {
|
||||
console.error('同步新令牌失败:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('直接添加令牌失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 阻止触摸移动
|
||||
catchTouchMove() {
|
||||
return false;
|
||||
}
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue