diff --git a/src/App.vue b/src/App.vue index 4505a7b..85f382b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,7 +9,7 @@ // import ThemePicker from '@/components/ThemePicker' import autofit from 'autofit.js' - +import { get } from '@/utils/config' export default { name: 'App', // components: { ThemePicker }, @@ -25,7 +25,9 @@ export default { }, } }, - + created() { + get(); + }, mounted() { autofit.init({ /* designHeight: 1080, diff --git a/src/api/config.js b/src/api/config.js new file mode 100644 index 0000000..6c834fb --- /dev/null +++ b/src/api/config.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取系统配置 +export const getConfig = () => { + return request({ + url: '/auth/getConfig', + method: 'get' + }) +} diff --git a/src/assets/img/myImage/point_grey.png b/src/assets/img/myImage/point_grey.png new file mode 100644 index 0000000..e7b0f55 Binary files /dev/null and b/src/assets/img/myImage/point_grey.png differ diff --git a/src/assets/img/myImage/point_red.png b/src/assets/img/myImage/point_red.png index 9d6bc78..6748f4e 100644 Binary files a/src/assets/img/myImage/point_red.png and b/src/assets/img/myImage/point_red.png differ diff --git a/src/components/home/countryMap.vue b/src/components/home/countryMap.vue index e5c4310..33d2594 100644 --- a/src/components/home/countryMap.vue +++ b/src/components/home/countryMap.vue @@ -558,16 +558,19 @@ export default { roam: false, coordinateSystem: 'geo', //使用坐标系 zlevel: 1, - symbolSize: [80, 90], + symbolSize: [60, 70], symbol: (value, params) => { if (params.data.errorNum>0) { return ( 'image://' + require('@/assets/img/myImage/point_red.png') ) } else { - return ( - 'image://' + require('@/assets/img/myImage/point.png') - ) + if(params.data.devStatus==0){ + return ('image://' + require('@/assets/img/myImage/point_grey.png')) + }else{ + return ('image://' + require('@/assets/img/myImage/point.png')) + } + } /*if (params.name == '安徽省') { return 'image://' + require('@/assets/img/myImage/markPoints.png') diff --git a/src/main.js b/src/main.js index a378822..7f444f9 100644 --- a/src/main.js +++ b/src/main.js @@ -47,7 +47,7 @@ import TableModel from '@/components/TableModel' import JsonExcel from 'vue-json-excel' Vue.prototype.$eventBus = new Vue() -import global_ from '@/utils/globalUrl' +// import global_ from '@/utils/globalUrl' // 全局方法挂载 Vue.prototype.getDicts = getDicts @@ -60,7 +60,7 @@ Vue.prototype.selectDictLabels = selectDictLabels Vue.prototype.download = download Vue.prototype.downloadJson = downloadJson Vue.prototype.handleTree = handleTree -Vue.prototype.globalUrl = global_ +// Vue.prototype.globalUrl = global_ Vue.prototype.indexContinuation = indexContinuation // 全局组件挂载 diff --git a/src/utils/bonus.js b/src/utils/bonus.js index 36e2b1a..ef2cbd4 100644 --- a/src/utils/bonus.js +++ b/src/utils/bonus.js @@ -232,3 +232,10 @@ export function tansParams(params) { export function blobValidate(data) { return data.type !== 'application/json' } + +// 处理表格索引延续问题 +export function indexContinuation(num, size) { + return (num - 1) * size + 1 +} + + diff --git a/src/utils/chapter.js b/src/utils/chapter.js deleted file mode 100644 index 69871a3..0000000 --- a/src/utils/chapter.js +++ /dev/null @@ -1,76 +0,0 @@ -let chapter = (text, companyName) => { - let canvas = document.getElementById("canvas"); - let context = canvas.getContext("2d"); - - //let text = "XXX专用章"; - //let companyName = "XXX科技股份有限公司"; - - // 绘制印章边框 - let width = canvas.width / 2; - let height = canvas.height / 2; - context.lineWidth = 3; - context.strokeStyle = "#f00"; - context.beginPath(); - context.arc(width, height, 80, 0, Math.PI * 2); //宽、高、半径 - context.stroke(); - - //画五角星 - create5star(context, width, height, 20, "#f00", 0); - - // 绘制印章名称 - context.font = "14px 宋体"; - context.textBaseline = "middle"; //设置文本的垂直对齐方式 - context.textAlign = "center"; //设置文本的水平对对齐方式 - context.lineWidth = 1; - context.strokeStyle = "#f00"; - context.strokeText(text, width, height + 50); - - // 绘制印章单位 - context.translate(width, height); // 平移到此位置, - context.font = "14px 宋体"; - let count = companyName.length; // 字数 - let angle = (4 * Math.PI) / (3 * (count - 1)); // 字间角度 - let chars = companyName.split(""); - let c; - for (let i = 0; i < count; i++) { - c = chars[i]; // 需要绘制的字符 - if (i == 0) { - context.rotate((5 * Math.PI) / 6); - } else { - context.rotate(angle); - } - - context.save(); - context.translate(65, 0); // 平移到此位置,此时字和x轴垂直,公司名称和最外圈的距离 - context.rotate(Math.PI / 2); // 旋转90度,让字平行于x轴 - context.strokeText(c, 0, 0); // 此点为字的中心点 - context.restore(); - } - - //绘制五角星 - function create5star(context, sx, sy, radius, color, rotato) { - context.save(); - context.fillStyle = color; - context.translate(sx, sy); //移动坐标原点 - context.rotate(Math.PI + rotato); //旋转 - context.beginPath(); //创建路径 - // let x = Math.sin(0); - // let y = Math.cos(0); - let dig = (Math.PI / 5) * 4; - for (let i = 0; i < 5; i++) { - //画五角星的五条边 - let x = Math.sin(i * dig); - let y = Math.cos(i * dig); - context.lineTo(x * radius, y * radius); - } - context.closePath(); - context.stroke(); - context.fill(); - context.restore(); - } - }; - - export default chapter; - - - \ No newline at end of file diff --git a/src/utils/config.js b/src/utils/config.js new file mode 100644 index 0000000..9c1f849 --- /dev/null +++ b/src/utils/config.js @@ -0,0 +1,11 @@ +import { getConfig } from '@/api/config'; +export function get() { + getConfig() + .then(response => { + console.log(response) + localStorage.setItem('systemConfig', JSON.stringify(response.data)); + }) + .catch(error => { + console.error('Failed to fetch config:', error); + }); +} diff --git a/src/utils/configure.js b/src/utils/configure.js index b0396c2..e6274c3 100644 --- a/src/utils/configure.js +++ b/src/utils/configure.js @@ -1,80 +1,23 @@ -// 密码强度级别常量 -const STRENGTH_LEVELS = { - WEAK: 'weak', // 弱:一类字符 - MEDIUM: 'medium', // 中:两类字符 - STRONG: 'strong', // 强:三类字符 - VERY_STRONG: 'very-strong' // 非常强:四类字符 -} - -// 数据设置常量 -const DATA_SETTINGS = { - OPEN: true, // 开启 - CLOSE: false // 关闭 -} - // SM 配置 -const SM_CONFIG = { - SALT: '2cc0c5f9f1749f1632efa9f63e902323' // SM3 盐值(16 字节) +export const SM_CONFIG = { + SALT: '2cc0c5f9f1749f1632efa9f63e902323', // SM3 盐值(16 字节) + SM4_KEY: "78d1295afa99449b99d6f83820e6965c", // SM4 对称加密密钥 + SM4_SALT: "f555adf6c01d0ab0761e626a2dae34a2", + SM2_PUBLIC_KEY: 'your-public-key', // SM2 公钥 + SM2_PRIVATE_KEY: 'your-private-key' // SM2 私钥 } - // AES 配置 -const AES_CONFIG = { +export const AES_CONFIG = { AES_KEY: 'zhgd@bonus@zhgd@bonus@1234567890', // AES key值 AES_IV: '1234567812345678' // AES 偏移量 } -// 登录配置 -const LOGIN_CONFIG = { - CODE_PHONE_LOGIN: DATA_SETTINGS.CLOSE, // 手机号验证码登录(true:开启,false:关闭) - CODE_EMAIL_LOGIN: DATA_SETTINGS.OPEN,// 邮箱验证码登录(true:开启,false:关闭) - PHONE_LOGIN: DATA_SETTINGS.OPEN, // 手机号密码登录(true:开启,false:关闭) - EMAIL_LOGIN: DATA_SETTINGS.CLOSE // 邮箱密码登录(true:开启,false:关闭) -} -//注册配置 -const REGISTER_CONFIG = { - PHONE_REGISTER: DATA_SETTINGS.OPEN, // 手机号注册(true:开启,false:关闭) - EMAIL_REGISTER: DATA_SETTINGS.CLOSE // 邮箱注册(true:开启,false:关闭) +export function generateUUID() { + // 使用当前时间戳和随机数生成一个 UUID + return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = Math.random() * 16 | 0; // 生成随机数 + const v = c === 'x' ? r : (r & 0x3 | 0x8); // 根据 UUID 规范生成相应的值 + return v.toString(16); // 转换为十六进制 + }); } -// 配置设置 -const CONFIG = { - STRENGTH: STRENGTH_LEVELS.STRONG,//密码强度配置 - IS_OPEN_REGISTER: REGISTER_CONFIG.PHONE_REGISTER || REGISTER_CONFIG.EMAIL_REGISTER, // 是否开启注册 - IS_CODE_LOGIN: LOGIN_CONFIG.CODE_EMAIL_LOGIN || LOGIN_CONFIG.CODE_PHONE_LOGIN, // 是否开启短信登录 - // 数据设置 - dataSettings: { - integrityCheck: DATA_SETTINGS.OPEN, // 数据完整性校验(true:开启,false:关闭) - encryptRequest: DATA_SETTINGS.OPEN, // 数据传输加密(true:开启,false:关闭) - encryptResponse: DATA_SETTINGS.OPEN // 数据返回解密(true:开启,false:关闭) - } -} - -// 获取占位符文本的函数 -// 获取占位符文本的函数 -function getPlaceholderText() { - const loginOptions = [] - if (LOGIN_CONFIG.PHONE_LOGIN) loginOptions.push('手机号') - if (LOGIN_CONFIG.EMAIL_LOGIN) loginOptions.push('邮箱') - return `用户名${loginOptions.length ? '/' + loginOptions.join('/') : ''}` -} - -// 获取占位符文本的函数 -// 获取占位符文本的函数 -function getCodePlaceholderText() { - const loginOptions = [] - if (LOGIN_CONFIG.CODE_PHONE_LOGIN) loginOptions.push('手机号') - if (LOGIN_CONFIG.CODE_EMAIL_LOGIN) loginOptions.push('邮箱') - return loginOptions.length ? loginOptions.join('/') : '' -} - -module.exports = { - STRENGTH_LEVELS, - DATA_SETTINGS, - CONFIG, - SM_CONFIG, - AES_CONFIG, - LOGIN_CONFIG, - REGISTER_CONFIG, - getPlaceholderText, - getCodePlaceholderText -} diff --git a/src/utils/dict/Dict.js b/src/utils/dict/Dict.js index 8fb6ca0..9d5069f 100644 --- a/src/utils/dict/Dict.js +++ b/src/utils/dict/Dict.js @@ -1,5 +1,5 @@ import Vue from 'vue' -import { mergeRecursive } from "@/utils/ruoyi"; +import { mergeRecursive } from "@/utils/bonus"; import DictMeta from './DictMeta' import DictData from './DictData' @@ -77,7 +77,6 @@ function loadDict(dict, dictMeta) { dicts.forEach(d => { Vue.set(dict.label[type], d.value, d.label) }) - // console.log(dict) return dicts }) } diff --git a/src/utils/dict/DictMeta.js b/src/utils/dict/DictMeta.js index 9779daa..355483d 100644 --- a/src/utils/dict/DictMeta.js +++ b/src/utils/dict/DictMeta.js @@ -1,4 +1,4 @@ -import { mergeRecursive } from "@/utils/ruoyi"; +import { mergeRecursive } from "@/utils/bonus"; import DictOptions from './DictOptions' /** diff --git a/src/utils/dict/DictOptions.js b/src/utils/dict/DictOptions.js index 338a94e..76f6d93 100644 --- a/src/utils/dict/DictOptions.js +++ b/src/utils/dict/DictOptions.js @@ -1,4 +1,4 @@ -import { mergeRecursive } from "@/utils/ruoyi"; +import { mergeRecursive } from "@/utils/bonus"; import dictConverter from './DictConverter' export const options = { @@ -8,7 +8,6 @@ export const options = { * 字典请求,方法签名为function(dictMeta: DictMeta): Promise */ request: (dictMeta) => { - console.log(`load dict ${dictMeta.type}`) return Promise.resolve([]) }, /** diff --git a/src/utils/download.js b/src/utils/download.js new file mode 100644 index 0000000..aba2d53 --- /dev/null +++ b/src/utils/download.js @@ -0,0 +1,26 @@ +// 下载blob文件 +export const downloadFile = ({ fileData, fileType, fileName }) => { + const blob = new Blob([fileData], { + type: fileType + }) + const link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.download = fileName + link.style.display = 'none' + document.body.appendChild(link) + link.click() + URL.revokeObjectURL(link.href) + document.body.removeChild(link) +} + +// 通用a链接下载 +export const downloadFileByUrl = (url) => { + const link = document.createElement('a') + link.href = url + link.setAttribute('download', '') + link.style.display = 'none' + document.body.appendChild(link) + link.click() + URL.revokeObjectURL(link.href) + document.body.removeChild(link) +} diff --git a/src/utils/globalUrl.js b/src/utils/globalUrl.js deleted file mode 100644 index d82b573..0000000 --- a/src/utils/globalUrl.js +++ /dev/null @@ -1,26 +0,0 @@ - -// const qrUrl = 'http://192.168.0.14:21624/qrCode/qrCodePage?qrCode='; //测试 -// const qrUrl = 'http://112.29.103.165:21626/qrCode/qrCodePage?qrCode='; //重庆 -// const qrUrl = 'http://112.29.103.165:21624/qrCode/qrCodePage?qrCode='; //宁夏 -// const qrUrl = 'https://z.csgmall.com.cn/gl/qrCode/qrCodePage?qrCode='; //南网 - -// const qrUrl = process.env.NODE_ENV === 'production' ? 'http://192.168.0.14:18866/qrCode/qrCodePage?qrCode=' : 'http://192.168.0.14:21624/qrCode/qrCodePage?qrCode=' -let qrUrl = '' -const origin = window.location.origin -if (origin == 'http://112.29.103.165:21626') { - qrUrl = 'http://112.29.103.165:21626/qrCode/qrCodePage?qrCode=' -} else { - qrUrl = 'http://192.168.0.14:18866/qrCode/qrCodePage?qrCode=' -} - -export default { - qrUrl, -} - - - - - - - - diff --git a/src/utils/index.js b/src/utils/index.js index 4e65504..64e371e 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,16 +1,16 @@ -import { parseTime } from './ruoyi' +import { parseTime } from './bonus' /** * 表格时间格式化 */ export function formatDate(cellValue) { if (cellValue == null || cellValue == "") return ""; - var date = new Date(cellValue) + var date = new Date(cellValue) var year = date.getFullYear() var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 - var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() - var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() - var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds } @@ -330,7 +330,7 @@ export function makeMap(str, expectsLowerCase) { ? val => map[val.toLowerCase()] : val => map[val] } - + export const exportDefault = 'export default ' export const beautifierConf = { @@ -387,4 +387,4 @@ export function camelCase(str) { export function isNumberStr(str) { return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) } - + diff --git a/src/utils/jsencrypt copy.js b/src/utils/jsencrypt copy.js deleted file mode 100644 index 78d9523..0000000 --- a/src/utils/jsencrypt copy.js +++ /dev/null @@ -1,30 +0,0 @@ -import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' - -// 密钥对生成 http://web.chacuo.net/netrsakeypair - -const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + - 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' - -const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + - '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + - 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + - 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + - 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + - 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + - 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + - 'UP8iWi1Qw0Y=' - -// 加密 -export function encrypt(txt) { - const encryptor = new JSEncrypt() - encryptor.setPublicKey(publicKey) // 设置公钥 - return encryptor.encrypt(txt) // 对数据进行加密 -} - -// 解密 -export function decrypt(txt) { - const encryptor = new JSEncrypt() - encryptor.setPrivateKey(privateKey) // 设置私钥 - return encryptor.decrypt(txt) // 对数据进行解密 -} - diff --git a/src/utils/request.js b/src/utils/request.js index 71618ac..7d2be68 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -6,11 +6,10 @@ import errorCode from '@/utils/errorCode' import { tansParams, blobValidate } from '@/utils/bonus' import cache from '@/plugins/cache' import { saveAs } from 'file-saver' -import { encryptCBC, decryptCBC } from '@/utils/aescbc' -import { CONFIG } from '@/utils/configure' -import { hashWithSM3AndSalt } from '@/utils/sm' - -//let token = localStorage.getItem("tokens"); +import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm' +const systemConfig = JSON.parse(localStorage.getItem('systemConfig')) || { + requestConfig: { encryptRequest: false, checkIntegrity: false, encryptResponse: false } +}; let downloadLoadingInstance // 是否显示重新登录 @@ -40,11 +39,11 @@ service.interceptors.request.use(config => { // 设置请求头 //入参加密 - config.headers['encryptRequest'] = CONFIG.dataSettings.encryptRequest && encryptRequest ? 'true' : 'false' + config.headers['encryptRequest'] = systemConfig.requestConfig.encryptRequest && encryptRequest ? 'true' : 'false' // 数据完整性校验 - config.headers['checkIntegrity'] = CONFIG.dataSettings.integrityCheck && checkIntegrity ? 'true' : 'false' + config.headers['checkIntegrity'] = systemConfig.requestConfig.checkIntegrity && checkIntegrity ? 'true' : 'false' //回参是否加密 - config.headers['encryptResponse'] = CONFIG.dataSettings.encryptResponse && encryptResponse ? 'true' : 'false' + config.headers['encryptResponse'] = systemConfig.requestConfig.encryptResponse && encryptResponse ? 'true' : 'false' const isRepeatSubmit = repeatSubmit // 处理 Token @@ -52,21 +51,6 @@ service.interceptors.request.use(config => { config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义 token } - // // 处理 GET 请求 - // if (config.method === 'get' && config.params) { - // let params = tansParams(config.params).slice(0, -1) - // // 数据完整性校验 - // if (CONFIG.dataSettings.integrityCheck && checkIntegrity) { - // config.headers['Params-Hash'] = hashWithSM3AndSalt(params) - // } - // // 加密参数 - // if (CONFIG.dataSettings.encryptRequest && encryptRequest) { - // params = encryptCBC(params) - // } - // config.url = `${config.url}?${params}` - // config.params = {} - // } - // get请求映射params参数 if (config.method === 'get' && config.params) { let url = config.url + '?' + tansParams(config.params); @@ -78,15 +62,12 @@ service.interceptors.request.use(config => { if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { let data = typeof config.data === 'object' ? JSON.stringify(config.data) : config.data let contentType = config.headers['Content-Type'] - if (contentType.includes('application/json')) { - // 数据完整性校验 - if (CONFIG.dataSettings.integrityCheck && checkIntegrity) { - config.headers['Params-Hash'] = hashWithSM3AndSalt(data) - config.data = data - } + if (contentType.includes('application/json') && typeof data !== 'undefined') { // 加密数据 - if (CONFIG.dataSettings.encryptRequest && encryptRequest) { - config.data = encryptCBC(data) + if (systemConfig.requestConfig.encryptRequest && encryptRequest) { + console.log(data); + console.log(hashWithSM3AndSalt(data)); + config.data = encryptWithSM4(data+"|"+hashWithSM3AndSalt(data)) } } // 检查请求数据大小 @@ -115,8 +96,8 @@ service.interceptors.request.use(config => { // 响应拦截器 service.interceptors.response.use(res => { - if (res.headers.encryptresponse) { - res.data = JSON.parse(decryptCBC(res.data)) + if (res.headers.encryptresponse && !res.data.hasOwnProperty('code')) { + res.data = JSON.parse(decryptWithSM4(res.data)) } // 未设置状态码则默认成功状态 const code = res.data.code || 200 @@ -157,7 +138,6 @@ service.interceptors.response.use(res => { } }, error => { - console.log('err' + error) let { message } = error if (message == 'Network Error') { message = '后端接口连接异常' diff --git a/src/utils/sm.js b/src/utils/sm.js index 2bdc324..8ed7008 100644 --- a/src/utils/sm.js +++ b/src/utils/sm.js @@ -1,6 +1,9 @@ // src/utils/encryption.js -import sm3 from 'sm-crypto/src/sm3' +import { sm2, sm3, sm4 } from 'sm-crypto' +// 配置项,例如盐值、SM2 公私钥、SM4 密钥 import { SM_CONFIG } from './configure' +import SM4 from 'sm-crypto/src/sm4' +import { hexToArray } from 'sm-crypto/src/sm2/utils' // SM3 哈希 export function hashSM3(text) { @@ -15,3 +18,33 @@ export function hashWithSM3AndSalt(text) { // 使用 SM3 进行哈希 return hashSM3(textWithSalt) } + +// SM2 加密 +export function encryptWithSM2(text) { + // SM2 公钥加密 + return sm2.doEncrypt(text, SM_CONFIG.SM2_PUBLIC_KEY) +} + +// SM2 解密 +export function decryptWithSM2(encryptedText) { + // SM2 私钥解密 + return sm2.doDecrypt(encryptedText, SM_CONFIG.SM2_PRIVATE_KEY) +} +/** + * 加密函数 + * @param {string} plainText + * @returns {string} 加密后的密文(Hex 编码格式) + */ +export function encryptWithSM4(plainText) { + return sm4.encrypt(plainText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5',iv:SM_CONFIG.SM4_SALT}); +} + +/** + * 解密函数 + * @param {string} cipherText + * @returns {string} 解密后的明文 + */ +export function decryptWithSM4(cipherText){ + return SM4.decrypt(cipherText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5' ,iv:SM_CONFIG.SM4_SALT}); +} + diff --git a/src/utils/validate.js b/src/utils/validate.js index e5a1abc..1dca102 100644 --- a/src/utils/validate.js +++ b/src/utils/validate.js @@ -1,6 +1,4 @@ -import { CONFIG } from '@/utils/configure' -import passwordConfig from '@/utils/passwordConfig' - +const systemConfig = JSON.parse(localStorage.getItem('systemConfig')); /** * @param {string} path * @returns {Boolean} @@ -92,71 +90,12 @@ export function validPwd(value) { return reg.test(value) } -/** - * 弱:长度至少为8个字符。 - * 一般:长度至少为8个字符,并包含至少一种字符类型。 - * 强:长度至少为8个字符,并包含至少两种字符类型。 - * 非常强:长度至少为8个字符,并包含所有四种字符类型。 - * @param rule - * @param value - * @param callback - * @returns {*} - */ - -export function validatePassword(rule, value, callback) { - if (!value) { - return callback(new Error('请输入密码')) - } - - const lengthRegex = /^.{8,20}$/ - const uppercaseRegex = /[A-Z]/ - const lowercaseRegex = /[a-z]/ - const digitRegex = /\d/ - const specialCharRegex = /[!@#$%^&*(),.?":{}|<>]/ - - if (!lengthRegex.test(value)) { - return callback(new Error('密码长度必须为8到20位')) - } - - const checks = [ - { regex: uppercaseRegex, message: '必须包含至少一个大写字母' }, - { regex: lowercaseRegex, message: '必须包含至少一个小写字母' }, - { regex: digitRegex, message: '必须包含至少一个数字' }, - { regex: specialCharRegex, message: '必须包含至少一个特殊字符' } - ] - - let passedChecks = checks.filter(check => check.regex.test(value)).length - - let requiredChecks - switch (CONFIG.STRENGTH) { - case 'weak': - requiredChecks = 1 - break - case 'medium': - requiredChecks = 2 - break - case 'strong': - requiredChecks = 3 - break - case 'very-strong': - requiredChecks = 4 - break - default: - return callback(new Error('请选择有效的密码强度')) - } - - if (passedChecks < requiredChecks) { - return callback(new Error(`密码至少包含 ${requiredChecks} 类字符(大写字母,小写字母,数字,特殊字符)`)) - } - callback() -} - export function validateNewPassword(rule, value, callback) { // 使用配置文件中的策略进行验证 // 1. 检查密码长度 - if (value.length < passwordConfig.minLength || value.length > passwordConfig.maxLength) { - callback(new Error('密码长度应为' + passwordConfig.minLength + '至' + passwordConfig.maxLength + '位!')) + if (value.length < systemConfig.passwordConfig.minLength || value.length > systemConfig.passwordConfig.maxLength) { + callback(new Error('密码长度应为' + systemConfig.passwordConfig.minLength + '至' + systemConfig.passwordConfig.maxLength + '位!')) return } @@ -166,46 +105,113 @@ export function validateNewPassword(rule, value, callback) { const hasDigit = /\d/.test(value) const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value) - if (passwordConfig.requireUpperCase && !hasUpperCase) { + if (systemConfig.passwordConfig.requireUpperCase && !hasUpperCase) { callback(new Error('密码必须包含大写字母!')) return } - if (passwordConfig.requireLowerCase && !hasLowerCase) { + if (systemConfig.passwordConfig.requireLowerCase && !hasLowerCase) { callback(new Error('密码必须包含小写字母!')) return } - if (passwordConfig.requireDigit && !hasDigit) { + if (systemConfig.passwordConfig.requireDigit && !hasDigit) { callback(new Error('密码必须包含数字!')) return } - if (passwordConfig.requireSpecialChar && !hasSpecialChar) { + if (systemConfig.passwordConfig.requireSpecialChar && !hasSpecialChar) { callback(new Error('密码必须包含特殊字符!')) return } // 3. 检查是否包含弱密码 - for (const weakPwd of passwordConfig.weakPasswords) { - if (value.includes(weakPwd)) { + for (const weakPwd of systemConfig.passwordConfig.weakPasswords) { + // 将密码和弱密码都转换为小写进行比较 + if (value.toLowerCase().includes(weakPwd.toLowerCase())) { callback(new Error(`密码包含常见的弱密码片段: ${weakPwd}`)) return } } // 4. 检查是否包含超过规定数量的连续字符 - if (passwordConfig.restrictConsecutiveChars && containsConsecutiveCharacters(value, passwordConfig.maxConsecutiveChars)) { - callback(new Error(`密码不能包含超过${passwordConfig.maxConsecutiveChars}位连续字符!`)) + if (systemConfig.passwordConfig.restrictConsecutiveChars && containsConsecutiveCharacters(value, systemConfig.passwordConfig.maxConsecutiveChars)) { + callback(new Error(`密码不能包含超过${systemConfig.passwordConfig.maxConsecutiveChars}位连续字符!`)) return } callback() // 验证成功 } -function containsConsecutiveCharacters(password, maxConsecutive) { - let count = 1 - for (let i = 1; i < password.length; i++) { - if (password[i] === password[i - 1]) { - count++ - if (count > maxConsecutive) return true - } else { - count = 1 +/** + * 检查密码中是否包含超过 n 个连续相同字符、连续递增/递减的数字或字母(不区分大小写) + */ +function containsConsecutiveCharacters(password, n) { + // 检查连续相同字符 + n = n + 1; // 允许最多 n 个连续字符 + for (let i = 0; i <= password.length - n; i++) { + let consecutiveSameChar = true; + for (let j = 1; j < n; j++) { + if (password[i + j] !== password[i]) { + consecutiveSameChar = false; + break; + } + } + if (consecutiveSameChar) { + return true; // 包含超过 n 个连续相同字符 } } - return false + + // 检查连续递增或递减的数字 + for (let i = 0; i <= password.length - n; i++) { + let consecutiveIncreasing = true; + let consecutiveDecreasing = true; + for (let j = 1; j < n; j++) { + const currentChar = password[i]; + const nextChar = password[i + j]; + + // 检查数字递增或递减 + if (/\d/.test(currentChar) && /\d/.test(nextChar)) { + if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) + j) { + consecutiveIncreasing = false; + } + if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) - j) { + consecutiveDecreasing = false; + } + } else { + consecutiveIncreasing = false; + consecutiveDecreasing = false; + break; + } + } + if (consecutiveIncreasing || consecutiveDecreasing) { + return true; // 包含超过 n 个递增或递减的连续数字 + } + } + + // 检查连续递增或递减的字母(不区分大小写) + for (let i = 0; i <= password.length - n; i++) { + let consecutiveIncreasing = true; + let consecutiveDecreasing = true; + for (let j = 1; j < n; j++) { + const currentChar = password[i].toLowerCase(); // 转为小写 + const nextChar = password[i + j].toLowerCase(); // 转为小写 + + // 检查字母递增或递减 + if (/[a-zA-Z]/.test(currentChar) && /[a-zA-Z]/.test(nextChar)) { + if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) + j) { + consecutiveIncreasing = false; + } + if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) - j) { + consecutiveDecreasing = false; + } + } else { + consecutiveIncreasing = false; + consecutiveDecreasing = false; + break; + } + } + if (consecutiveIncreasing || consecutiveDecreasing) { + return true; // 包含超过 n 个递增或递减的连续字母 + } + } + + // 不包含连续相同字符、数字或字母序列 + return false; } + + diff --git a/src/views/Lines/index.vue b/src/views/Lines/index.vue index 7af4be2..d470981 100644 --- a/src/views/Lines/index.vue +++ b/src/views/Lines/index.vue @@ -272,7 +272,9 @@ export default { handelCloseSub(){ this.showProjSec = true this.showLefSec = false - this.renderAllProj() + setTimeout(()=>{ + this.getProjData() + },500) }, validForbid(value) { value = value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g, '').replace(/\s/g, ""); return value; } diff --git a/src/views/Substation/index.vue b/src/views/Substation/index.vue index 0303199..f1a3716 100644 --- a/src/views/Substation/index.vue +++ b/src/views/Substation/index.vue @@ -188,6 +188,7 @@ export default { devNum: item.devNum, successNum: item.successNum, errorNum: item.errorNum, + devStatus: item.devStatus, itemStyle: { normal: { areaColor: '#7DDEFF' diff --git a/src/views/homePage/index.vue b/src/views/homePage/index.vue index 59f2268..52d21d0 100644 --- a/src/views/homePage/index.vue +++ b/src/views/homePage/index.vue @@ -222,6 +222,7 @@ export default { devNum: item.devNum, successNum: item.successNum, errorNum: item.errorNum, + devStatus: item.devStatus, itemStyle: { normal: { areaColor: '#7DDEFF',