Browse Source

加密

q_v1
herenshan112 12 hours ago
parent
commit
7f253b1c86
  1. 5
      .env
  2. 46
      package-lock.json
  3. 3
      package.json
  4. 28
      src/utils/axios/index.ts
  5. 102
      src/utils/axiosRequest/request.ts
  6. 63
      src/utils/encryptionAndDecryption/randNumber.ts
  7. 7
      src/utils/encryptionAndDecryption/shiliu.ts
  8. 74
      src/utils/encryptionAndDecryption/sm4Utils.ts
  9. 347
      src/utils/encryptionAndDecryption/tongyong.ts
  10. 14
      src/utils/loadSvgFile.ts
  11. 32
      src/utils/validate.ts

5
.env

@ -0,0 +1,5 @@
//统一配置
VITE_APP_PORT = 9998
VITE_APP_TITLE = '数通智联化工云平台'
VITE_APP_SYSTEM_KEY = 'hengxingaokeApp1'
VITE_APP_CSS_URL = '/public/'

46
package-lock.json

@ -20,9 +20,11 @@
"@zxing/library": "^0.21.3", "@zxing/library": "^0.21.3",
"appformlowcode": "file:", "appformlowcode": "file:",
"axios": "^1.7.7", "axios": "^1.7.7",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"element-plus": "^2.8.6", "element-plus": "^2.8.6",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"gm-crypto": "^0.1.12",
"html5-qrcode": "^2.3.8", "html5-qrcode": "^2.3.8",
"js-beautify": "^1.15.1", "js-beautify": "^1.15.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -48,6 +50,7 @@
"weixin-js-sdk": "^1.6.5" "weixin-js-sdk": "^1.6.5"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "^4.2.2",
"@types/js-beautify": "^1.14.3", "@types/js-beautify": "^1.14.3",
"@types/node": "^22.7.8", "@types/node": "^22.7.8",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
@ -1586,6 +1589,13 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/@types/crypto-js": {
"version": "4.2.2",
"resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.2.tgz",
"integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/debug": { "node_modules/@types/debug": {
"version": "4.1.12", "version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@ -2509,8 +2519,7 @@
"type": "consulting", "type": "consulting",
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
], ]
"optional": true
}, },
"node_modules/big.js": { "node_modules/big.js": {
"version": "5.2.2", "version": "5.2.2",
@ -2629,7 +2638,6 @@
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
], ],
"optional": true,
"dependencies": { "dependencies": {
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"ieee754": "^1.1.13" "ieee754": "^1.1.13"
@ -3075,6 +3083,12 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/css-select": { "node_modules/css-select": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz",
@ -4455,6 +4469,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/gm-crypto": {
"version": "0.1.12",
"resolved": "https://registry.npmmirror.com/gm-crypto/-/gm-crypto-0.1.12.tgz",
"integrity": "sha512-ercd9ionBqxR+/FCXICr0eo+jzC8BvSK0j9L7/eB0uwbyjgeMPTdBNrcQTqIuRXOtOAKSGsTNvtLYFnIxNEoFg==",
"license": "MIT",
"dependencies": {
"buffer": "^5.7.0",
"jsbn": "^1.1.0",
"to-arraybuffer": "^1.0.1"
}
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
@ -4857,8 +4882,7 @@
"type": "consulting", "type": "consulting",
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
], ]
"optional": true
}, },
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
@ -5374,6 +5398,12 @@
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==" "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="
}, },
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
"license": "MIT"
},
"node_modules/jsesc": { "node_modules/jsesc": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.0.2.tgz", "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.0.2.tgz",
@ -8983,6 +9013,12 @@
"resolved": "https://registry.npmmirror.com/tinymce/-/tinymce-7.7.1.tgz", "resolved": "https://registry.npmmirror.com/tinymce/-/tinymce-7.7.1.tgz",
"integrity": "sha512-rMetqSgZtYbj4YPOX+gYgmlhy/sIjVlI/qlrSOul/Mpn9e0aIIG/fR0qvQSVYvxFv6OzRTge++NQyTbzLJK1NA==" "integrity": "sha512-rMetqSgZtYbj4YPOX+gYgmlhy/sIjVlI/qlrSOul/Mpn9e0aIIG/fR0qvQSVYvxFv6OzRTge++NQyTbzLJK1NA=="
}, },
"node_modules/to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
"integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==",
"license": "MIT"
},
"node_modules/to-fast-properties": { "node_modules/to-fast-properties": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",

3
package.json

@ -53,9 +53,11 @@
"@zxing/library": "^0.21.3", "@zxing/library": "^0.21.3",
"appformlowcode": "file:", "appformlowcode": "file:",
"axios": "^1.7.7", "axios": "^1.7.7",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"element-plus": "^2.8.6", "element-plus": "^2.8.6",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"gm-crypto": "^0.1.12",
"html5-qrcode": "^2.3.8", "html5-qrcode": "^2.3.8",
"js-beautify": "^1.15.1", "js-beautify": "^1.15.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -81,6 +83,7 @@
"weixin-js-sdk": "^1.6.5" "weixin-js-sdk": "^1.6.5"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "^4.2.2",
"@types/js-beautify": "^1.14.3", "@types/js-beautify": "^1.14.3",
"@types/node": "^22.7.8", "@types/node": "^22.7.8",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",

28
src/utils/axios/index.ts

@ -1,5 +1,7 @@
import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios"; import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { userStror } from "@/utils/pinia/stores/modules/userOrders"; import { userStror } from "@/utils/pinia/stores/modules/userOrders";
import { sm4DecryptMethod, sm4EncryptMethod } from "../encryptionAndDecryption/sm4Utils";
import { generateRandomString } from "../encryptionAndDecryption/randNumber";
// 创建 axios 实例 // 创建 axios 实例
@ -24,6 +26,22 @@ service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
if (userPinia.userToken) { if (userPinia.userToken) {
config.headers["user-token"] = userPinia.userToken; config.headers["user-token"] = userPinia.userToken;
} }
console.error('<---------------请求拦截---------->')
console.error('请求拦截----config------>', config.url)
console.error('请求拦截----data------>', config.data)
console.error('<---------------请求拦截---------->')
// if (config.headers['content-type'] === 'application/json') {
let { data, headers } = config
//获取16位随机数
let randomString = generateRandomString(16)
config.headers['Auth-key'] = randomString
if (data) {
// 加密请求数据
config.data = {
data: sm4EncryptMethod(JSON.stringify(data), randomString)
}
}
return config; return config;
}, (error: any) => { }, (error: any) => {
// 对请求错误做些什么 // 对请求错误做些什么
@ -35,6 +53,16 @@ service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
// 添加响应拦截器 // 添加响应拦截器
service.interceptors.response.use((response: AxiosResponse) => { service.interceptors.response.use((response: AxiosResponse) => {
// 2xx 范围内的状态码都会触发该函数。 // 2xx 范围内的状态码都会触发该函数。
let { data, headers } = response
let authKey = headers['auth-key']
// console.log('行营结果----authKey------>', authKey)
// 解密响应数据
if (authKey) {
let jsonData = sm4DecryptMethod(data.data, authKey)
response.data.data = JSON.parse(jsonData)
}
console.error('行营结果----解密结构------>', response.config.url)
console.error('行营结果----解密结构------>', response.data)
// 对响应数据做点什么 // 对响应数据做点什么
const { code, msg } = response.data; const { code, msg } = response.data;
//如果是以下状态直接放行 //如果是以下状态直接放行

102
src/utils/axiosRequest/request.ts

@ -0,0 +1,102 @@
/**
@ 作者: 秦东
@ 时间: 2025-12-09 10:57:37
@ 功能: 封装axios请求
*/
import axios, { AxiosError, type InternalAxiosRequestConfig } from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'
import { generateRandomString } from '@/utils/encryptionAndDecryption/randNumber'
import { sm4DecryptMethod, sm4EncryptMethod } from '@/utils/encryptionAndDecryption/sm4Utils'
import { useUserStore } from '@/stores/user'
/**
@ 作者: 秦东
@ 时间: 2025-12-09 10:58:34
@ 功能: 创建axios实例
*/
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000
})
/**
@ 作者: 秦东
@ 时间: 2025-12-09 11:00:05
@ 功能: 请求拦截
*/
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const userStore = useUserStore()
// if (config.headers['content-type'] === 'application/json') {
let { data, headers } = config
console.log('请求拦截----data---1--->', data)
if (userStore.authToken) {
config.headers['Auth-token'] = userStore.authToken
}
//获取16位随机数
let randomString = generateRandomString(16)
config.headers['Auth-key'] = randomString
if (data) {
// 加密请求数据
config.data = {
encryptedFile: sm4EncryptMethod(JSON.stringify(data), randomString)
}
}
console.log('请求拦截---------->', randomString)
console.log('请求拦截----headers------>', headers)
console.log('请求拦截----data------>', config.data)
console.log('请求拦截----config------>', config)
// }
// console.log('请求拦截----content-type------>', config.headers['Content-Type'])
// console.log('请求拦截----config------>', config.headers)
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
/**
@ 作者: 秦东
@ 时间: 2025-12-09 21:00:20
@ 备注: 响应拦截
*/
service.interceptors.response.use(
response => {
console.log('行营结果---------->', response)
let { data, headers } = response
// console.log('行营结果----data------>', data.data)
let authKey = headers['auth-key']
console.log('行营结果----authKey------>', authKey)
// 解密响应数据
if (authKey) {
let jsonData = sm4DecryptMethod(data.data, authKey)
response.data.data = JSON.parse(jsonData)
}
console.log('行营结果----解密结构------>', headers['auth-key'], response)
return response.data
},
(error: AxiosError) => {
if (error.response) {
const status = error.response.status
switch (status) {
case 401:
// 处理401错误,例如跳转到登录页
break
case 403:
// 处理403错误,例如提示无权限
break
case 404:
// 处理404错误,例如提示资源不存在
break
case 500:
// 处理500错误,例如提示服务器错误
break
default:
// 处理其他错误
break
}
}
return Promise.reject(error)
}
)
export default service

63
src/utils/encryptionAndDecryption/randNumber.ts

@ -0,0 +1,63 @@
/**
@ 作者: 秦东
@ 时间: 2025-12-07 11:07:32
@ 备注: 创建任意位数的随机字符串
*/
const generateRandomString = (length: number = 16): string => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const array = new Uint8Array(length)
crypto.getRandomValues(array)
return Array.from(array, byte => characters[byte % characters.length]).join('')
// let randomString = '';
// for (let i = 0; i < length; i++) {
// const randomIndex = Math.floor(Math.random() * characters.length);
// randomString += characters.charAt(randomIndex);
// }
// return randomString;
}
/**
@ 作者: 秦东
@ 时间: 2025-12-08 16:15:12
@ 功能: 获取UUID
*/
const uuid = () => {
const s: string[] = []
const hexDigits = '123456789abcdef'
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((parseInt(s[19]!, 16) & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = '-'
const uuid = s.join('')
return uuid
}
/**
@ 作者: 秦东
@ 时间: 2025-12-08 16:15:48
@ 功能: 获取简单随机数
*/
const randomString = (length: number) => {
var str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
var result = ''
for (var i = length; i > 0; --i) {
result += str[Math.floor(Math.random() * str.length)]
}
return result
}
/**
@ 作者: 秦东
@ 时间: 2025-12-08 16:16:12
@ 功能: 中间画线驼峰
*/
const toHump = (str: string) => {
if (!str) return str
return str
.replace(/\-(\w)/g, function (all: string, letter: string) {
return letter.toUpperCase()
})
.replace(/(\s|^)[a-z]/g, function (char: string) {
return char.toUpperCase()
})
}
export { generateRandomString, uuid, randomString, toHump }

7
src/utils/encryptionAndDecryption/shiliu.ts

@ -0,0 +1,7 @@
import * as CryptoJS from 'crypto-js'
const strToHexSub = (str: string): string => {
return CryptoJS.enc.Utf8.parse(str).toString(CryptoJS.enc.Hex)
}
export { strToHexSub }

74
src/utils/encryptionAndDecryption/sm4Utils.ts

@ -0,0 +1,74 @@
import { SM4 as sm4 } from 'gm-crypto'
import * as CryptoJS from 'crypto-js'
/**
@ 作者: 秦东
@ 时间: 2025-12-07 10:26:51
@ 备注: 将字符串转换成16进制
*/
const strToHexMethod = (str: string): string => {
return CryptoJS.enc.Utf8.parse(str).toString(CryptoJS.enc.Hex)
}
/**
@ 作者: 秦东
@ 时间: 2025-12-07 10:29:48
@ 备注: 判断是不是12位16进制参数
*/
const isValidHex32 = (str: string): boolean => {
return /^[0-9a-fA-F]{32}$/.test(str)
}
/**
@ 作者: 秦东
@ 时间: 2025-12-07 10:28:33
@ 备注: 初始化SM4密钥参数
*/
const appSystemKey = strToHexMethod(import.meta.env.VITE_APP_SYSTEM_KEY)
const sm4TokenKey = strToHexMethod(import.meta.env.VITE_APP_SM4_APP_KEY)
/**
@ 作者: 秦东
@ 时间: 2025-12-07 10:30:40
@ 备注: SM4加密方法
*/
const sm4EncryptMethod = (data: string, customKey: string): string => {
let ivSetup = sm4TokenKey
if (customKey) {
ivSetup = strToHexMethod(customKey)
if (!isValidHex32(ivSetup)) {
ivSetup = sm4TokenKey
}
}
return sm4.encrypt(data, appSystemKey, {
iv: ivSetup,
mode: sm4.constants.CBC,
inputEncoding: 'utf8',
outputEncoding: 'hex',
padding: 1
} as never)
}
/**
@ 作者: 秦东
@ 时间: 2025-12-07 10:31:22
@ 备注: SM4解密方法
*/
const sm4DecryptMethod = (data: string, customKey: string): string => {
let ivSetup = sm4TokenKey
if (customKey) {
ivSetup = strToHexMethod(customKey)
if (!isValidHex32(ivSetup)) {
ivSetup = sm4TokenKey
}
}
// console.log('SM4解密方法----解密结构---data--->', data)
console.log('SM4解密方法----解密结构----customKey-->', customKey)
console.log('SM4解密方法----解密结构--ivSetup---->', ivSetup)
console.log('SM4解密方法----解密结构---sm4TokenKey--->', sm4TokenKey)
console.log('SM4解密方法----解密结构---appSystemKey--->', appSystemKey)
return sm4.decrypt(data, appSystemKey, {
iv: ivSetup,
mode: sm4.constants.CBC,
inputEncoding: 'hex',
outputEncoding: 'utf8',
padding: 1
} as never)
}
export { sm4EncryptMethod, sm4DecryptMethod }

347
src/utils/encryptionAndDecryption/tongyong.ts

@ -0,0 +1,347 @@
/**
*
*/
const REG_NUMBER: string = '.*\\d+.*'
/**
*
*/
const REG_UPPERCASE: string = '.*[A-Z]+.*'
/**
*
*/
const REG_LOWERCASE: string = '.*[a-z]+.*'
/**
* (~!@#$%^&*()_+|<>,.?/:;'[]{}\)
*/
const REG_SYMBOL: string = '.*[~!@#$%^&*()_+|<>,.?/:;\'\\[\\]{}]+".*'
/**
* ()
* shift键盘字符表
*/
const CHAR_TABLE1: string[][] = [
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\0'],
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '\0', '\0'],
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0']
]
/**
* shift键盘的字符表
*/
const CHAR_TABLE2: string[][] = [
['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\0'],
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"', '\0', '\0'],
['z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?', '\0', '\0', '\0']
]
/**
*
* @param password
* @param username
*/
export const checkPasswordRule = (password: string, username: string) => {
if (password === '' || password.length < 8 || password.length > 32) {
// console.log("长度小于8,或大于32");
return '密码长度应大于8小于32'
}
if (password.indexOf(username) !== -1) {
// console.log("包含用户名");
return '请勿包含用户名'
}
if (isContinuousChar(password)) {
// console.log("包含3个及以上相同或字典连续字符");
return '请勿包含3个及以上相同或连续的字符'
}
if (isKeyBoardContinuousChar(password)) {
// console.log("包含3个及以上键盘连续字符");
return '请勿包含3个及以上键盘连续字符'
}
let i: number = 0
if (password.match(REG_NUMBER)) i++
if (password.match(REG_LOWERCASE)) i++
if (password.match(REG_UPPERCASE)) i++
if (password.match(REG_SYMBOL)) i++
if (i < 2) {
// console.log(("数字、小写字母、大写字母、特殊字符,至少包含两种"));
return '数字、小写字母、大写字母、特殊字符,至少包含两种'
}
// console.log(i);
return '校验通过'
}
/**
* 3
*/
const isContinuousChar = (password: string) => {
const chars: string[] = password.split('')
// 遍历所有可能的三个连续字符组合
for (let i = 0; i < chars.length - 2; i++) {
// 添加类型断言,确保字符不是undefined
const char1 = chars[i] as string
const char2 = chars[i + 1] as string
const char3 = chars[i + 2] as string
// 获取字符的ASCII码
const n1 = char1.charCodeAt(0)
const n2 = char2.charCodeAt(0)
const n3 = char3.charCodeAt(0)
// 判断重复字符
if (n1 === n2 && n1 === n3) {
return true
}
// 判断连续字符: 正序(如abc)和倒序(如cba)
if ((n1 + 1 === n2 && n2 + 1 === n3) || (n1 - 1 === n2 && n2 - 1 === n3)) {
return true
}
}
return false
}
/**
* 3
* @param password
*/
const isKeyBoardContinuousChar = (password: string) => {
if (password === '') {
return false
}
//考虑大小写,都转换成小写字母
const lpStrChars: string[] = password.toLowerCase().split('')
// 获取字符串长度
const nStrLen: number = lpStrChars.length
// 定义位置数组:row - 行,col - column 列,初始化为-1
const pRowCharPos: number[] = new Array(nStrLen).fill(-1)
const pColCharPos: number[] = new Array(nStrLen).fill(-1)
for (let i = 0; i < nStrLen; i++) {
// 添加类型断言,确保字符不是undefined
const chLower: string = lpStrChars[i] as string
// 检索在表1中的位置,构建位置数组
for (let nRowTable1Idx = 0; nRowTable1Idx < 4; nRowTable1Idx++) {
for (let nColTable1Idx = 0; nColTable1Idx < 13; nColTable1Idx++) {
// 获取当前字符,跳过\0字符,使用可选链和空值合并确保类型安全
const tableChar = CHAR_TABLE1[nRowTable1Idx]?.[nColTable1Idx] ?? ''
if (tableChar && tableChar !== '\0' && chLower === tableChar) {
pRowCharPos[i] = nRowTable1Idx
pColCharPos[i] = nColTable1Idx
break
}
}
}
// 在表1中没找到,到表二中去找,找到则continue
// 添加非空检查
const currentColPos = pColCharPos[i]
if (currentColPos !== undefined && currentColPos >= 0) {
continue
}
// 检索在表2中的位置,构建位置数组
for (let nRowTable2Idx = 0; nRowTable2Idx < 4; nRowTable2Idx++) {
for (let nColTable2Idx = 0; nColTable2Idx < 13; nColTable2Idx++) {
// 获取当前字符,跳过\0字符,使用可选链和空值合并确保类型安全
const tableChar = CHAR_TABLE2[nRowTable2Idx]?.[nColTable2Idx] ?? ''
if (tableChar && tableChar !== '\0' && chLower === tableChar) {
pRowCharPos[i] = nRowTable2Idx
pColCharPos[i] = nColTable2Idx
break
}
}
}
}
// 匹配坐标连线
for (let j = 1; j <= nStrLen - 2; j++) {
// 获取当前三个字符的位置,添加类型断言
const row1 = pRowCharPos[j - 1] as number
const row2 = pRowCharPos[j] as number
const row3 = pRowCharPos[j + 1] as number
const col1 = pColCharPos[j - 1] as number
const col2 = pColCharPos[j] as number
const col3 = pColCharPos[j + 1] as number
//同一行
if (row1 === row2 && row2 === row3) {
// 键盘行正向连续(asd)或者键盘行反向连续(dsa)
if (
(col1 + 1 === col2 && col2 + 1 === col3) ||
(col3 + 1 === col2 && col2 + 1 === col1)
) {
return true
}
}
//同一列
if (col1 === col2 && col2 === col3) {
//键盘列连续(qaz)或者键盘列反向连续(zaq)
if (
(row1 + 1 === row2 && row2 + 1 === row3) ||
(row1 - 1 === row2 && row2 - 1 === row3)
) {
return true
}
}
}
return false
}
/**
*
*/
/**
*
* @param str
*/
const length = (str: string) => {
if (str.length < 5) {
return 5
} else if (str.length < 8) {
return 10
} else {
return 25
}
}
/**
*
* @param str
*/
const letters = (str: string) => {
let count1 = 0,
count2 = 0
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) >= 'a' && str.charAt(i) <= 'z') {
count1++
}
if (str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') {
count2++
}
}
if (count1 === 0 && count2 === 0) {
return 0
}
if (count1 !== 0 && count2 !== 0) {
return 20
}
return 10
}
/**
*
* @param str
*/
const numbers = (str: string) => {
let count = 0
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
count++
}
}
if (count === 0) {
return 0
}
if (count === 1) {
return 10
}
return 20
}
/**
*
* @param str
*/
const symbols = (str: string) => {
let count = 0
for (let i = 0; i < str.length; i++) {
if (
(str.charCodeAt(i) >= 0x21 && str.charCodeAt(i) <= 0x2f) ||
(str.charCodeAt(i) >= 0x3a && str.charCodeAt(i) <= 0x40) ||
(str.charCodeAt(i) >= 0x5b && str.charCodeAt(i) <= 0x60) ||
(str.charCodeAt(i) >= 0x7b && str.charCodeAt(i) <= 0x7e)
) {
count++
}
}
if (count === 0) {
return 0
}
if (count === 1) {
return 10
}
return 25
}
/**
*
* @param str
*/
const rewards = (str: string) => {
let letter = letters(str) //字母
let number = numbers(str) //数字
let symbol = symbols(str) //符号
if (letter > 0 && number > 0 && symbol === 0) {
//字母和数字
return 2
}
if (letter === 10 && number > 0 && symbol > 0) {
//字母、数字和符号
return 3
}
if (letter === 20 && number > 0 && symbol > 0) {
//大小写字母、数字和符号
return 5
}
return 0
}
/**
*
* @param str
*/
export const level = (str: string) => {
let lengths = length(str) //长度
let letter = letters(str) //字母
let number = numbers(str) //数字
let symbol = symbols(str) //符号
let reward = rewards(str) //奖励
let sum = lengths + letter + number + symbol + reward
console.log(sum)
if (sum >= 80) {
return '非常强' //非常安全
} else if (sum >= 60) {
return '强' //非常强
} else if (sum >= 40) {
return '一般' //一般
} else if (sum >= 25) {
return '弱' //弱
} else {
return '非常弱' //非常弱
}
}
export const checkPwdRule = (password: string) => {
if (password === '' || password.length < 8 || password.length > 32) {
// console.log("长度小于8,或大于32");
return '密码长度应大于8小于32'
}
if (isContinuousChar(password)) {
// console.log("包含3个及以上相同或字典连续字符");
return '请勿包含3个及以上相同或连续的字符'
}
if (isKeyBoardContinuousChar(password)) {
// console.log("包含3个及以上键盘连续字符");
return '请勿包含3个及以上键盘连续字符'
}
let i: number = 0
if (password.match(REG_NUMBER)) i++
if (password.match(REG_LOWERCASE)) i++
if (password.match(REG_UPPERCASE)) i++
if (password.match(REG_SYMBOL)) i++
if (i < 2) {
// console.log(("数字、小写字母、大写字母、特殊字符,至少包含两种"));
return '数字、小写字母、大写字母、特殊字符,至少包含两种'
}
// console.log(i);
return '校验通过'
}

14
src/utils/loadSvgFile.ts

@ -0,0 +1,14 @@
/**
@ 作者: 秦东
@ 时间: 2026-01-09 15:57:48
@ 功能: 加载svg文件
*/
export const loadSvgFiles = (): string[] => {
const icons = import.meta.glob('../assets/icons/*.svg')
const svgIcons: string[] = []
for (const icon in icons) {
const iconName = icon.split('assets/icons/')[1].split('.svg')[0]
svgIcons.push(iconName)
}
return svgIcons
}

32
src/utils/validate.ts

@ -0,0 +1,32 @@
/**
@ 作者: 秦东
@ 时间: 2025-12-29 16:00:55
@ 功能: 路径匹配器
* @param {string} pattern
* @param {string} path
* @returns {Boolean}
*/
export const isPathMatch = (pattern: string, path: string): boolean => {
const regexPattern = pattern
.replace(/\//g, '\\/')
.replace(/\*\*/g, '.*')
.replace(/\*/g, '[^\\/]*')
const regex = new RegExp(`^${regexPattern}$`)
return regex.test(path)
}
/**
@ 作者: 秦东
@ 时间: 2025-12-29 16:02:21
@ 功能: 判断字符串是否为空
*/
export const isEmptyString = (str: string): boolean => {
return str.trim().length === 0
}
/**
@ 作者: 秦东
@ 时间: 2025-12-29 16:03:14
@ 功能: 判断url是否是http或https
*/
export const isHttpOrHttpsUrl = (url: string): boolean => {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
}
Loading…
Cancel
Save