Переглянути джерело

Merge branch 'master' of http://git.mydig.net/Sagoo-Cloud/sagoo-admin-ui

vera_min 1 рік тому
батько
коміт
359df2279a

+ 1 - 1
.env

@@ -28,4 +28,4 @@ VITE_MODBUS_API = '/base-api/modbus'
 VITE_ICE104_API = '/base-api/ice104'
 
 # 加密公钥, 用双引号,换行符用 \n
-VITE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBCgKCAQEA2GHtfmjFVCi5pcjsv6ppyeEmESN4FpID8WHLtvXXRm3wexonVmhO\n2GUEl/T8PonnYYuUHARFVBmTEF0SvFUwfTab88HznL8KDW8yPDEz8E8daV59BzAx\nVWP389PUxv0AAwLWNa5dy5cIG4eKvrgRjHtMn/c12Ba5R6OfvWX/lX9YkOIDfeca\n2X1eOhgtfqVr4iG6R4UHAf0NzKleNvPbwQjXUN/wMFIF7bNmYebn5kSBDkFb2DLP\npEb8iQ6CQzQM/Z6HVGM2XokJ2oPhvUFpI7BVQce0gIg7rq00Dexz7IJxCU/khkFY\nR6fjPEkL0hoaae2MXtv22gJPwOhlh7kFhQIDAQAB\n-----END PUBLIC KEY-----"
+VITE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwrJzCAJ0aart82Y2B5qo\nsZRv8p1dGX2oLFr1sArJxevW3a1v7cVA0U4WVFJdifDVFpsuich9nsfhUp7CNOZn\na+rNveglzYlrtMhqynYU+bKUBBAmYaVyDHOpxkp86fhp0q7qoX8YoeSvYRaVaPoF\nHRYeahy0d3L+gL8pRhr0k70RZMraC3zzXbuUcM7GNibiKbFiQllhlGlfbV0bmOH8\nLZcwWwv40Ptdd4x2gihn5vmzGdQ1OAf3D6YmtsXf7iMj0H1g5svyHs17ncSN7h9i\nWTrVKcNDxrl1dm4BRsxDJsWenwrIM1WUHuonlbE6OoIJEO25T3ucymzWDzMSWxe3\nsQIDAQAB\n-----END PUBLIC KEY-----"

+ 3 - 9
.env.development

@@ -1,17 +1,11 @@
 # VITE_SERVER_PROTOCOL = 'http:'
 # VITE_SERVER_HOSTNAME = '127.0.0.1:8200'
+
 VITE_SERVER_PROTOCOL = 'https:'
 VITE_SERVER_HOSTNAME = 'zhgy.sagoo.cn'
 
 # VITE_SERVER_PROTOCOL = 'http:'
-# VITE_SERVER_HOSTNAME = 'iot.server.mydig.net'
-
+# VITE_SERVER_HOSTNAME = 'wg.server.mydig.net'
 
 # VITE_SERVER_PROTOCOL = 'http:'
-# VITE_SERVER_HOSTNAME = 'liangzi.server.mydig.net'
-
-# VITE_SERVER_PROTOCOL = 'http:'
-# VITE_SERVER_HOSTNAME = 'pg.server.mydig.net'
-
-# 加密公钥, 用双引号,换行符用 \n
-VITE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBCgKCAQEA2GHtfmjFVCi5pcjsv6ppyeEmESN4FpID8WHLtvXXRm3wexonVmhO\n2GUEl/T8PonnYYuUHARFVBmTEF0SvFUwfTab88HznL8KDW8yPDEz8E8daV59BzAx\nVWP389PUxv0AAwLWNa5dy5cIG4eKvrgRjHtMn/c12Ba5R6OfvWX/lX9YkOIDfeca\n2X1eOhgtfqVr4iG6R4UHAf0NzKleNvPbwQjXUN/wMFIF7bNmYebn5kSBDkFb2DLP\npEb8iQ6CQzQM/Z6HVGM2XokJ2oPhvUFpI7BVQce0gIg7rq00Dexz7IJxCU/khkFY\nR6fjPEkL0hoaae2MXtv22gJPwOhlh7kFhQIDAQAB\n-----END PUBLIC KEY-----"
+# VITE_SERVER_HOSTNAME = 'pg.server.mydig.net'

+ 1 - 5
.env.open

@@ -1,8 +1,4 @@
 VITE_SERVER_PROTOCOL = ''
 VITE_SERVER_HOSTNAME = ''
 # 开源标识
-VITE_ISOPEN = true
-
-# 加密公钥, 用双引号,换行符用 \n
-
-VITE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBCgKCAQEA2GHtfmjFVCi5pcjsv6ppyeEmESN4FpID8WHLtvXXRm3wexonVmhO\n2GUEl/T8PonnYYuUHARFVBmTEF0SvFUwfTab88HznL8KDW8yPDEz8E8daV59BzAx\nVWP389PUxv0AAwLWNa5dy5cIG4eKvrgRjHtMn/c12Ba5R6OfvWX/lX9YkOIDfeca\n2X1eOhgtfqVr4iG6R4UHAf0NzKleNvPbwQjXUN/wMFIF7bNmYebn5kSBDkFb2DLP\npEb8iQ6CQzQM/Z6HVGM2XokJ2oPhvUFpI7BVQce0gIg7rq00Dexz7IJxCU/khkFY\nR6fjPEkL0hoaae2MXtv22gJPwOhlh7kFhQIDAQAB\n-----END PUBLIC KEY-----"
+VITE_ISOPEN = true

+ 12 - 4
src/utils/download.ts

@@ -6,19 +6,27 @@ const downloadFile = (res: any, fileName: string = '导出日志.xlsx') => {
   // 判断是不是返回的是json,是json说明报错了,否则返回的是文本流
   const decoder = new TextDecoder('utf-8');
   const text = decoder.decode(res.data);
+  console.log(text)
 
   try {
     const errJson = JSON.parse(text)
     if (errJson.message) {
       ElMessage.closeAll()
       ElMessage.error(errJson.message)
+    } else {
+      // 可能是导出  json文件
+      beginDownload(res, fileName)
     }
   } catch {
-    // 用split是避免多次取值重复都好分割的情况,比如 
-    // attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx", attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx"
-    const lastFileName = res.headers['content-disposition'] ? res.headers['content-disposition'].split(',')[0].replaceAll('attachment; filename="', '').replaceAll('"', '') : fileName;
-    download(res.data, lastFileName, res.headers['content-type']);
+    beginDownload(res, fileName)
   }
 }
 
+function beginDownload(res: any, fileName: string) {
+  // 用split是避免多次取值重复都好分割的情况,比如 
+  // attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx", attachment; filename="2022-12-06 21:34:35-SysLoginLog.xlsx"
+  const lastFileName = res.headers['content-disposition'] ? res.headers['content-disposition'].split(',')[0].replaceAll('attachment; filename="', '').replaceAll('"', '') : fileName;
+  download(res.data, lastFileName, res.headers['content-type']);
+}
+
 export default downloadFile

+ 150 - 10
src/utils/rsa.ts

@@ -1,15 +1,155 @@
-import JSEncrypt from 'jsencrypt'
+const PUBLIC_KEY = import.meta.env.VITE_PUBLIC_KEY
+const PRIVATE_KEY = import.meta.env.VITE_PRIVATE_KEY
 
-export function encrypt(value: string) {
-  const crypt = new JSEncrypt();
-  crypt.setPublicKey(import.meta.env.VITE_PUBLIC_KEY);
-  return crypt.encrypt(value)
+let publicKey: CryptoKey
+let privateKey: CryptoKey
+
+async function setPublicKey() {
+  const publicKeyBuffer = pemToBuffer(PUBLIC_KEY);
+  publicKey = await window.crypto.subtle.importKey(
+    "spki", // 这边如果私钥则是pkcs8
+    publicKeyBuffer,
+    {
+      name: "RSA-OAEP",
+      hash: { name: 'SHA-256' },
+    },
+    true,
+    ["encrypt"] // 用于加密所以是公钥,私钥就是 decrypt
+  )
+  // console.log(publicKey)
+}
+
+async function setPrivateKey() {
+  const privateBuffer = pemToBuffer(PRIVATE_KEY);
+  privateKey = await window.crypto.subtle.importKey(
+    "pkcs8", // 这边如果私钥则是pkcs8
+    privateBuffer,
+    {
+      name: "RSA-OAEP",
+      hash: { name: 'SHA-256' },
+    },
+    true,
+    ["decrypt"] // 用于加密所以是公钥,私钥就是 decrypt
+  )
+  // console.log(privateKey)
+}
+
+setPublicKey()
+// setPrivateKey()
+
+// setTimeout(() => {
+//   // encrypt('asdfasdf').then(res => {
+//   //   console.log(res)
+//   // })
+//   decrypt('uts03JfyTGh/l8ivwQX/n9a6UpaVpfGgLaelrPfyMMdbslJmj79UNnv30NqktM7E6LhOyp4Ebt+EoJ8ss1FPyMlKpxwPHCFIanaClyR/aTCzeQGxVH2kZKJuZu02vrX1m2qXjn0Z1aV43GV5bNB40qPHzUZsMzMBHOqCAvqYO2zZwGpmFRFRu/93CPgpxgbhsbH7dCwOqJt9I7DMx9KaiiJmnPlbDSI4HG/wP/J2L1GHnZPK9zq+ccHm7WUYeVhf2xVYvma+hSzLWSiuwT1n5vRMIUKwjlT66usKJkEVhIiLTPmR6KLqEmWN21a/p6Dus/MCkFlA5hAURBalDqlnMQ==').then(res => {
+//     console.log(res)
+//   })
+// }, 300)
+
+export function encrypt(str: string) {
+  const enc = new TextEncoder()
+  const data = enc.encode(str) // 这边将要加密的字符串转为utf-8的Uint8Array
+  return new Promise(async (resolve) => {
+    const buffer = await window.crypto.subtle.encrypt(
+      {
+        name: "RSA-OAEP"
+      },
+      publicKey, // 生成或者导入的CryptoKey对象
+      data
+    )
+    const resStr = window.btoa(String.fromCharCode(...new Uint8Array(buffer)))
+    resolve(resStr)
+  })
+}
+
+export function decrypt(base64String: string) {
+  return new Promise(async (resolve) => {
+    const buffer = await window.crypto.subtle.decrypt(
+      {
+        name: "RSA-OAEP"
+      },
+      privateKey,
+      base64ToUint8Array(base64String)
+    )
+    const resStr = new TextDecoder().decode(buffer);
+    resolve(resStr)
+  })
+}
+
+function pemToBuffer(pem: string) {
+  const lines = pem.split('\n');
+  const encoded = lines.slice(1, -1).join('');
+  const decoded = window.atob(encoded);
+  const buffer = new Uint8Array(decoded.length);
+  for (let i = 0; i < decoded.length; ++i) {
+    buffer[i] = decoded.charCodeAt(i);
+  }
+  return buffer.buffer
+}
+
+function base64ToUint8Array(base64String: string) {
+  const padding = '='.repeat((4 - base64String.length % 4) % 4)
+  const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/')
+
+  const rawData = window.atob(base64)
+  const outputArray = new Uint8Array(rawData.length)
+
+  for (let i = 0; i < rawData.length; ++i) {
+    outputArray[i] = rawData.charCodeAt(i)
+  }
+  return outputArray
+}
+
+//#region 生成公钥私钥
+
+// generateKey()
+
+function generateKey() {
+  window.crypto.subtle.generateKey(
+    {
+      name: "RSA-OAEP",
+      modulusLength: 2048,
+      publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+      hash: {
+        name: "SHA-256" // 这边如果后端使用公钥加密,要注意与前端一致
+      },
+    },
+    true,
+    ["encrypt", "decrypt"] // must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
+  ).then(key => {
+    getPemText('spki', key.publicKey)
+    getPemText('pkcs8', key.privateKey)
+  })
+}
+
+
+function getPemText(type: "pkcs8" | "spki", key: CryptoKey) {
+  // 导出私钥
+  window.crypto.subtle.exportKey(type, key).then(function (res) {
+    const key = RSA2text(res, type) // 私钥pem
+    console.log(key)
+    console.log(key.length)
+  }).catch(function (err) {
+    console.error(err)
+  })
 }
 
-export function decrypt(value: string) {
-  const crypt = new JSEncrypt();
-  crypt.setPrivateKey(import.meta.env.VITE_PRIVATE_KEY);
-  return crypt.decrypt(value)
+// pem格式文本
+function RSA2text(buffer: ArrayBuffer, type: "pkcs8" | "spki") {
+  const isPrivate = type === 'pkcs8'
+  let binary = ''
+  const bytes = new Uint8Array(buffer)
+  const len = bytes.byteLength
+  for (let i = 0; i < len; i++) {
+    binary += String.fromCharCode(bytes[i])
+  }
+  const base64 = window.btoa(binary)
+  console.log(base64)
+  console.log('\n')
+  let text = "-----BEGIN " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----\n" // 这里-----BEGIN和-----是固定的
+  text += base64.replace(/[^\x00-\xff]/g, "$&\x01").replace(/.{64}\x01?/g, "$&\n") // 中间base64编码
+  text += "\n-----END " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----" // 这里-----END和-----是固定的
+  return text
 }
 
-// console.log(decrypt('GyFOsw70nGTQD/tnI5o2pOnJxQKH2MEsG8jL2ZxFP+nLkEpB8puahImT7cs2yjGK8dc78WPKev2N2zwypbVDG3A/31GctX38tYF2ivBO4c+XZyGuYwQSLEidDv3XqV3MrXwNLksYeIKKEwPBHD52nrWwZKvyK0LeLeaBCrRQmLV7kfhLh6bGKhe9n5Yjiwu+1GsgWpUa4ht4WwFf5og+tNCaI99IQoxpNdhHA2UtrtbgVcNZ8o2/d5aSE7g9ScvGaH0rMNfLc25UezltxTzEMSWv/8DGg7yL844U97qXwRWBGS5oi0Da9UtvGMegEjBLqBGSztGBB/LeQDzu9KyAQQ=='))
+//#endregion

+ 41 - 0
src/views/iot/device/instance/component/map.vue

@@ -2,12 +2,23 @@
     <div>
       <el-dialog title="地图选点" v-model="isShowDialog" width="900px" append-to-body>
         <div class="map-container">
+
+
+
           <div class="coordinate-search">
+
+
+            <el-input v-model="searchKeyword" placeholder="搜索地名" />
+
+
             <el-input v-model="lng" placeholder="经度" />
             <div>-</div>
             <el-input v-model="lat" placeholder="纬度" />
             <el-button @click="searchByCoordinate" type="primary">搜索</el-button>
           </div>
+
+
+
           <div class="map" ref="mapContainer"></div>
           <!-- 地址解析结果 -->
           <div class="address-result" v-if="address">
@@ -26,6 +37,8 @@
   const address = ref('');
   const lng = ref('');
   const lat = ref('');
+  const searchKeyword = ref(''); // 搜索输入框的值
+
   const isShowDialog = ref(false);
   const marker = ref<BMapGL.Marker | null>(null);
   let map: BMapGL.Map | null = null;
@@ -47,6 +60,14 @@
         setAddressByCoordinate(lng.value, lat.value);
       });
     });
+
+
+    lng.value="";
+    lat.value="";
+    searchKeyword.value="";
+
+
+
   };
   
   const confirmAddress = () => {
@@ -82,8 +103,28 @@
     if (lng.value && lat.value) {
       setMarker(lng.value, lat.value);
       setAddressByCoordinate(lng.value, lat.value);
+    }else if(searchKeyword.value){
+      searchByKeyword(searchKeyword.value);
     }
   };
+
+  const searchByKeyword = () => { // 搜索方法
+  const local = new BMapGL.LocalSearch(map, {
+    onSearchComplete(results: any) {
+      if (local.getStatus() === BMAP_STATUS_SUCCESS) {
+        if (results && results.getPoi(0)) {
+          const point = results.getPoi(0).point;
+          lng.value = point.lng.toFixed(5);
+          lat.value = point.lat.toFixed(5);
+          setMarker(lng.value, lat.value);
+          setAddressByCoordinate(lng.value, lat.value);
+        }
+      }
+    }
+  });
+
+  local.search(searchKeyword.value);
+};
   
   const emit = defineEmits(['updateMap']);
   

+ 3 - 2
src/views/login/component/account.vue

@@ -114,13 +114,14 @@ export default defineComponent({
 		const onSignIn = () => {
 			// 验证表单
 			proxy.$refs.loginForm
-				.validate((valid: boolean) => {
+				.validate(async (valid: boolean) => {
 					if (valid) {
 						state.loading.signIn = true;
+						const password = await encrypt(state.ruleForm.password)
 						api.login
 							.login({
 								...state.ruleForm,
-								password: encrypt(state.ruleForm.password)
+								password
 							})
 							.then(async (res: any) => {
 								// 检查是否需要更换密码

+ 5 - 2
src/views/login/component/changePwd.vue

@@ -52,10 +52,13 @@ const formRules = computed(() => ({
 async function onSubmit() {
 	await formRef.value.validate()
 
+	const oldUserPassword = await encrypt(ruleForm.oldUserPassword)
+	const userPassword = await encrypt(ruleForm.userPassword)
+
 	api.login.editPassword({
 		userName: ruleForm.userName,
-		oldUserPassword: encrypt(ruleForm.oldUserPassword),
-		userPassword: encrypt(ruleForm.userPassword)
+		oldUserPassword,
+		userPassword,
 	}).then(() => {
 		show.value = false
 		ElMessage.success('修改成功')