diff --git a/src/assets/images/chat/chat-avator.png b/src/assets/images/chat/chat-avator.png new file mode 100644 index 0000000..aa1180e Binary files /dev/null and b/src/assets/images/chat/chat-avator.png differ diff --git a/src/components/abstract.vue b/src/components/abstract.vue index c45225f..72b4175 100644 --- a/src/components/abstract.vue +++ b/src/components/abstract.vue @@ -5,10 +5,11 @@ import { messageSend } from '@/utils/api' import { getHiddenProp, getVisibilityState, decryptByVersionNo1, isSit, Encrypt, isDev, isProd } from "@/utils"; const store = inject('appStore', null) const businessHandleInfo = ref({}) +const orderCount = ref(0) const abstractList = computed(() => [ { title: '保障状态', - num: 3, + num: orderCount.value, text: '保单保障中', unit: '张', name: '查保单', @@ -61,7 +62,7 @@ const getIdentityCardno = () => { if (!versionNo || !pidCipher) return '' const identityCardno = decryptByVersionNo1(versionNo, pidCipher) - console.log('decrypt pid =>', JSON.stringify(identityCardno)) + console.log(decryptByVersionNo1(versionNo, '0FLzZwhD08nkUE4BjRGOSEri4KWO'),id); return isIdentityNo(identityCardno) ? identityCardno.trim() : '' } @@ -79,6 +80,8 @@ const getOrderCount = () => { Post('policy/queryCount', { dentityCardno: identityCardno, userId: store.userId + }).then(res => { + orderCount.value = res?.data?.policyCount || 0 }) } catch (error) { console.log(error) @@ -86,7 +89,7 @@ const getOrderCount = () => { } const toDetail = (record) => { const text = record?.name - console.log('text',text); + console.log('text', text); if (!text) return messageSend(text).catch((err) => { console.log(err) @@ -125,12 +128,14 @@ onMounted(async () => { \ No newline at end of file diff --git a/src/store/app.js b/src/store/app.js index 8266ae7..0003d0d 100644 --- a/src/store/app.js +++ b/src/store/app.js @@ -91,7 +91,7 @@ export const useAppStore = defineStore('app', () => { const navigationCorpus = ref(initialNavigationCorpus) const routeAction = ref('push') const direction = ref('forward') - const mescrollContent = ref('') + const mescrollContent = ref(null) const isLeaveMessage = ref(false) const isLeaveMessageSuccess = ref(initialIsLeaveMessageSuccess) const manualService = ref(1) @@ -725,6 +725,8 @@ export const useAppStore = defineStore('app', () => { console.log('发送消息==', sendParmas.value) timestamp.value = sendParmas.value.timestamp + sendLoading.value = true + sendParmas.value.sendLoading = true // 存储正在发送的那条数据 setCurrentItemId(sendParmas.value.timestamp) @@ -774,14 +776,18 @@ export const useAppStore = defineStore('app', () => { } function setFindActiveItem(findActiveItem) { - if (chatList.value && chatList.value.length > 0) { - chatList.value.forEach((item) => { + const syncList = (list) => { + if (!list || list.length === 0) return + list.forEach((item) => { if (currentItemId.value == item.timestamp) { item.isSendSuccess = findActiveItem item.sendLoading = false } }) } + syncList(chatList.value) + syncList(chatArr.value) + sendLoading.value = false } function setActiveItemExtra(data) { @@ -813,7 +819,9 @@ export const useAppStore = defineStore('app', () => { function scrollBottom() { setTimeout(() => { try { - mescrollContent.value.scrollTop = mescrollContent.value.scrollHeight || 0 + const el = mescrollContent.value + if (!el || typeof el !== 'object') return + el.scrollTop = el.scrollHeight || 0 } catch (error) { console.log(error.message) } @@ -1078,6 +1086,7 @@ export const useAppStore = defineStore('app', () => { return await sendWebsocketMessage(sendparam) } catch (error) { issubmit.value = true + sendLoading.value = false throw error } } diff --git a/src/utils/index.js b/src/utils/index.js index ba3bac2..91e10d3 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -371,6 +371,26 @@ export function getHost() { return hostName; } +function safeUtf8(words) { + try { + return CryptoJS.enc.Utf8.stringify(words) || '' + } catch (e) { + return '' + } +} + +function normalizeCipherText(value) { + let normalized = String(value ?? '').replace(/\s+/g, '').replace(/\r|\n/g, '') + try { + if (/%[0-9A-Fa-f]{2}/.test(normalized)) { + normalized = decodeURIComponent(normalized) + } + } catch (e) {} + // URL-safe Base64 + normalized = normalized.replace(/-/g, '+').replace(/_/g, '/') + return normalized +} + export function Encrypt(Okey, Oiv, payload) { const key = CryptoJS.enc.Utf8.parse(Okey); const iv = CryptoJS.enc.Utf8.parse(Oiv); @@ -386,7 +406,7 @@ export function Decryption(Okey, Oiv, value) { if (typeof value !== 'string' && typeof value !== 'number') return '' const key = CryptoJS.enc.Utf8.parse(Okey); const iv = CryptoJS.enc.Utf8.parse(Oiv); - const normalized = String(value).replace(/\s+/g, '') + const normalized = normalizeCipherText(value) const options = { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } // 1) 历史 Hex 密文 @@ -396,38 +416,80 @@ export function Decryption(Okey, Oiv, value) { key, options, ) - const hexText = CryptoJS.enc.Utf8.stringify(hexDecrypt) + const hexText = safeUtf8(hexDecrypt) if (hexText) return hexText } // 2) 兼容原 OpenSSL/Base64 字符串解密(ASR 等存量逻辑) const opensslDecrypt = CryptoJS.AES.decrypt(normalized, key, options) - const opensslText = CryptoJS.enc.Utf8.stringify(opensslDecrypt) + const opensslText = safeUtf8(opensslDecrypt) if (opensslText) return opensslText // 3) 显式按裸 Base64 ciphertext 再试(与 Encrypt 输出对齐) - const rawDecrypt = CryptoJS.AES.decrypt( - { ciphertext: CryptoJS.enc.Base64.parse(normalized) }, - key, - options, - ) - return CryptoJS.enc.Utf8.stringify(rawDecrypt) + try { + const rawDecrypt = CryptoJS.AES.decrypt( + { ciphertext: CryptoJS.enc.Base64.parse(normalized) }, + key, + options, + ) + return safeUtf8(rawDecrypt) + } catch (e) { + return '' + } } export function decryptByVersionNo1(versionNo1Cipher, cipherText) { if (!versionNo1Cipher || !cipherText) return '' + const normalizedCipher = normalizeCipherText(cipherText) const year = new Date().getFullYear() - const versionNo1 = DecryptionECB(`${year}${year}${year}${year}`, versionNo1Cipher) - const key = versionNo1.substring(0, 16) - const iv = versionNo1.substring(16, 32) - if (key.length !== 16 || iv.length !== 16) return '' - return Decryption(key, iv, cipherText) + // 兼容跨年:versionNo1 可能按加密当年的 year 生成 + const yearCandidates = [year, year - 1, year - 2, year + 1] + + for (const y of yearCandidates) { + const versionNo1 = DecryptionECB(`${y}${y}${y}${y}`, versionNo1Cipher) + if (!versionNo1 || versionNo1.length < 32) continue + const key = versionNo1.substring(0, 16) + const iv = versionNo1.substring(16, 32) + if (key.length !== 16 || iv.length !== 16) continue + const plain = Decryption(key, iv, normalizedCipher) + if (plain) return plain.replace(/^["']+|["']+$/g, '').trim() + } + return '' } export function DecryptionECB(keyECB, valueECB) { - const key = CryptoJS.enc.Utf8.parse(keyECB); - const decrypt = CryptoJS.AES.decrypt(valueECB, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); - return CryptoJS.enc.Utf8.stringify(decrypt); + if (valueECB === undefined || valueECB === null || valueECB === '') return '' + const key = CryptoJS.enc.Utf8.parse(keyECB) + const normalized = normalizeCipherText(valueECB) + const options = { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } + + // 1) Hex + if (/^[0-9A-Fa-f]+$/.test(normalized) && normalized.length % 32 === 0) { + const hexDecrypt = CryptoJS.AES.decrypt( + { ciphertext: CryptoJS.enc.Hex.parse(normalized) }, + key, + options, + ) + const hexText = safeUtf8(hexDecrypt) + if (hexText) return hexText + } + + // 2) OpenSSL/Base64 string + const opensslDecrypt = CryptoJS.AES.decrypt(normalized, key, options) + const opensslText = safeUtf8(opensslDecrypt) + if (opensslText) return opensslText + + // 3) raw Base64 ciphertext + try { + const rawDecrypt = CryptoJS.AES.decrypt( + { ciphertext: CryptoJS.enc.Base64.parse(normalized) }, + key, + options, + ) + return safeUtf8(rawDecrypt) + } catch (e) { + return '' + } } export function isSit() {