- 新增响应式工具函数与全局移动端样式,统一适配≤768px屏幕 - 添加移动端底部导航组件,替代桌面端侧边栏菜单 - 为经销商、客户、报备列表页新增移动端卡片视图替换表格布局 - 调整所有页面的表单、弹窗与布局适配移动端尺寸 - 优化移动端viewport配置,适配全面屏安全区域 - 修复密码哈希工具,在非安全上下文下回退使用js-sha256库 - 更新依赖包,新增@vueuse/core与js-sha256
63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
/**
|
||
* 密码加密工具(SHA-256 → 小写 hex)
|
||
*
|
||
* 安全上下文(HTTPS 或 localhost)下使用原生 Web Crypto API;
|
||
* 非安全上下文(HTTP + 局域网/IP 访问,此时 crypto.subtle 为 undefined)
|
||
* 下回退到纯 JS 实现 js-sha256,保证产物一致,登录不再卡死。
|
||
* 两条路径对同一字符串产出的 SHA-256 小写 hex 完全相同。
|
||
*/
|
||
import { sha256 } from 'js-sha256'
|
||
|
||
/** 当前环境是否可用原生 Web Crypto(即处于安全上下文) */
|
||
function hasSubtleCrypto(): boolean {
|
||
return typeof crypto !== 'undefined' && typeof crypto.subtle?.digest === 'function'
|
||
}
|
||
|
||
/** 原生 Web Crypto SHA-256 → 小写 hex */
|
||
async function subtleSha256Hex(input: string): Promise<string> {
|
||
const data = new TextEncoder().encode(input)
|
||
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
|
||
return Array.from(new Uint8Array(hashBuffer))
|
||
.map((b) => b.toString(16).padStart(2, '0'))
|
||
.join('')
|
||
}
|
||
|
||
/** 统一入口:优先原生,回退纯 JS */
|
||
async function sha256Hex(input: string): Promise<string> {
|
||
if (hasSubtleCrypto()) {
|
||
return subtleSha256Hex(input)
|
||
}
|
||
return sha256(input)
|
||
}
|
||
|
||
/**
|
||
* 使用 SHA-256 算法哈希密码
|
||
* @param password 明文密码
|
||
* @returns 哈希后的十六进制字符串(小写)
|
||
*/
|
||
export async function hashPassword(password: string): Promise<string> {
|
||
if (!password) return ''
|
||
try {
|
||
return await sha256Hex(password)
|
||
} catch (error) {
|
||
console.error('密码哈希失败:', error)
|
||
throw new Error('密码加密失败')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 简单的加盐哈希(可选,用于增强安全性)
|
||
* @param password 明文密码
|
||
* @param salt 盐值
|
||
* @returns 哈希后的十六进制字符串(小写)
|
||
*/
|
||
export async function hashPasswordWithSalt(password: string, salt: string): Promise<string> {
|
||
if (!password) return ''
|
||
try {
|
||
return await sha256Hex(password + salt)
|
||
} catch (error) {
|
||
console.error('密码哈希失败:', error)
|
||
throw new Error('密码加密失败')
|
||
}
|
||
}
|