样式优化以及功能位置摆放

master
wujingling0908 2 weeks ago
parent 469f44dbae
commit 098453dc86
  1. BIN
      src/assets/images/chat/chat-avator.png
  2. 13
      src/components/abstract.vue
  3. 4
      src/components/appraise-popup.vue
  4. 6
      src/components/chat-content.vue
  5. 5
      src/components/chat-new.vue
  6. 13
      src/components/hot-question-new.vue
  7. 13
      src/components/welcomes.vue
  8. 17
      src/store/app.js
  9. 96
      src/utils/index.js

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

@ -5,10 +5,11 @@ import { messageSend } from '@/utils/api'
import { getHiddenProp, getVisibilityState, decryptByVersionNo1, isSit, Encrypt, isDev, isProd } from "@/utils"; import { getHiddenProp, getVisibilityState, decryptByVersionNo1, isSit, Encrypt, isDev, isProd } from "@/utils";
const store = inject('appStore', null) const store = inject('appStore', null)
const businessHandleInfo = ref({}) const businessHandleInfo = ref({})
const orderCount = ref(0)
const abstractList = computed(() => [ const abstractList = computed(() => [
{ {
title: '保障状态', title: '保障状态',
num: 3, num: orderCount.value,
text: '保单保障中', text: '保单保障中',
unit: '张', unit: '张',
name: '查保单', name: '查保单',
@ -61,7 +62,7 @@ const getIdentityCardno = () => {
if (!versionNo || !pidCipher) return '' if (!versionNo || !pidCipher) return ''
const identityCardno = decryptByVersionNo1(versionNo, pidCipher) const identityCardno = decryptByVersionNo1(versionNo, pidCipher)
console.log('decrypt pid =>', JSON.stringify(identityCardno)) console.log(decryptByVersionNo1(versionNo, '0FLzZwhD08nkUE4BjRGOSEri4KWO'),id);
return isIdentityNo(identityCardno) ? identityCardno.trim() : '' return isIdentityNo(identityCardno) ? identityCardno.trim() : ''
} }
@ -79,6 +80,8 @@ const getOrderCount = () => {
Post('policy/queryCount', { Post('policy/queryCount', {
dentityCardno: identityCardno, dentityCardno: identityCardno,
userId: store.userId userId: store.userId
}).then(res => {
orderCount.value = res?.data?.policyCount || 0
}) })
} catch (error) { } catch (error) {
console.log(error) console.log(error)
@ -86,7 +89,7 @@ const getOrderCount = () => {
} }
const toDetail = (record) => { const toDetail = (record) => {
const text = record?.name const text = record?.name
console.log('text',text); console.log('text', text);
if (!text) return if (!text) return
messageSend(text).catch((err) => { messageSend(text).catch((err) => {
console.log(err) console.log(err)
@ -125,12 +128,14 @@ onMounted(async () => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.abstract-con { .abstract-con {
position: relative;
z-index: 1;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
padding: 10rem 12rem; padding: 10rem 12rem;
gap: 12rem; gap: 12rem;
align-self: stretch; align-self: stretch;
margin-top: 10rem;
background: linear-gradient(77deg, #fcfdfd80 -1%, #f5f7fee6 99%); background: linear-gradient(77deg, #fcfdfd80 -1%, #f5f7fee6 99%);
box-sizing: border-box; box-sizing: border-box;

@ -104,7 +104,9 @@ const changeCheck = (item) => {
const scrollTopBody = () => { const scrollTopBody = () => {
setTimeout(() => { setTimeout(() => {
try { try {
mescrollContent.value.scrollTop = mescrollContent.value.scrollHeight || 0 const el = mescrollContent.value
if (!el || typeof el !== 'object') return
el.scrollTop = el.scrollHeight || 0
} catch (err) { } catch (err) {
console.log(err.message) console.log(err.message)
} }

@ -36,7 +36,7 @@
<div class="chat-content-list" v-if="item.msgText && item.fromFlag == '1' && item.reqAndResType == '1'"> <div class="chat-content-list" v-if="item.msgText && item.fromFlag == '1' && item.reqAndResType == '1'">
<!-- 文本消息 --> <!-- 文本消息 -->
<div class="chat-content-txt"> <div class="chat-content-txt">
<div class="send-message-loading" v-if="sendLoading && item.timestamp == timestamp"> <div class="send-message-loading" v-if="(sendLoading && String(item.timestamp) === String(timestamp)) || item.sendLoading">
<van-loading type="spinner" size="18px" /> <van-loading type="spinner" size="18px" />
</div> </div>
<div class="chat-txt-content" v-if="item.msgText"> <div class="chat-txt-content" v-if="item.msgText">
@ -56,7 +56,7 @@
<div class="chat-content-list" v-if="item.msgText && item.fromFlag == '1' && item.reqAndResType == '10'"> <div class="chat-content-list" v-if="item.msgText && item.fromFlag == '1' && item.reqAndResType == '10'">
<!-- 文本消息 --> <!-- 文本消息 -->
<div class="chat-content-txt"> <div class="chat-content-txt">
<div class="send-message-loading" v-if="sendLoading && item.timestamp == timestamp"> <div class="send-message-loading" v-if="(sendLoading && String(item.timestamp) === String(timestamp)) || item.sendLoading">
<van-loading type="spinner" size="18px" /> <van-loading type="spinner" size="18px" />
</div> </div>
<div class="chat-txt-content" v-if="item.msgText"> <div class="chat-txt-content" v-if="item.msgText">
@ -96,7 +96,7 @@
<!-- 图片消息 --> <!-- 图片消息 -->
<div class="chat-content-list" v-if="item.msgText && item.fromFlag == '1' && item.reqAndResType == '2'"> <div class="chat-content-list" v-if="item.msgText && item.fromFlag == '1' && item.reqAndResType == '2'">
<div class="chat-content-image"> <div class="chat-content-image">
<div class="send-message-loading" v-if="sendLoading && item.timestamp == timestamp"> <div class="send-message-loading" v-if="(sendLoading && String(item.timestamp) === String(timestamp)) || item.sendLoading">
<van-loading type="spinner" size="18px" /> <van-loading type="spinner" size="18px" />
</div> </div>
<div class="chat-content-image-box" @click="ImagePreviewBox(item.msgText)" :data-timestamp="item.timestamp"> <div class="chat-content-image-box" @click="ImagePreviewBox(item.msgText)" :data-timestamp="item.timestamp">

@ -307,6 +307,10 @@ const getChatHistory = async () => {
}) })
} }
onMounted(async () => { onMounted(async () => {
await nextTick()
if (mainContainerRef.value) {
store?.setMescrollContent?.(mainContainerRef.value)
}
getHeaderUrl() getHeaderUrl()
getChatHistory() getChatHistory()
}) })
@ -338,6 +342,7 @@ watch(
<welcomes /> <welcomes />
<abstract /> <abstract />
<hotQuestionNew /> <hotQuestionNew />
<chatContent :historylist="chatArr" :isWX="isWX" :tokenExist="tokenExist" :lostChatInfoNum="lostChatInfoNum" />
</div> </div>
<div class="chat-footer"> <div class="chat-footer">
<footer-bar v-if="tokenExist" :tokenExist="tokenExist" /> <footer-bar v-if="tokenExist" :tokenExist="tokenExist" />

@ -78,7 +78,12 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div class="ask-con"> <div class="ask-con">
<div class="item" v-for="(item, index) in questionList" :key="index" @click="handleAskClick(item)"> <div
class="item"
v-for="(item, index) in questionList"
:key="index"
@click="handleAskClick(item)"
>
<div class="icon"><span>#</span></div> <div class="icon"><span>#</span></div>
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<img src="@/assets/images/chat/line.svg" alt="" /> <img src="@/assets/images/chat/line.svg" alt="" />
@ -91,7 +96,9 @@ onMounted(async () => {
flex-direction: column; flex-direction: column;
gap: 10rem; gap: 10rem;
margin-top: 16rem; margin-top: 16rem;
position: relative;
z-index: 1;
.item { .item {
border-radius: 24rem; border-radius: 24rem;
display: flex; display: flex;
@ -133,7 +140,7 @@ onMounted(async () => {
} }
} }
>span { > span {
font-family: PingFang SC; font-family: PingFang SC;
font-size: 14rem; font-size: 14rem;
font-weight: normal; font-weight: normal;

@ -20,12 +20,13 @@ onMounted(() => {
<div class="welcome-con"> <div class="welcome-con">
<div class="welcome-title">{{ welcomeWord }}</div> <div class="welcome-title">{{ welcomeWord }}</div>
<div class="sub-title">{{ welcomeSubWord }}</div> <div class="sub-title">{{ welcomeSubWord }}</div>
<img src="@/assets/images/chat/chat-avator.png" alt="" srcset="" />
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.welcome-con { .welcome-con {
padding: 23rem 8rem; padding: 33rem 8rem;
position: relative;
.welcome-title { .welcome-title {
font-family: PingFang SC; font-family: PingFang SC;
font-size: 20rem; font-size: 20rem;
@ -43,6 +44,7 @@ onMounted(() => {
} }
.sub-title { .sub-title {
width: calc(100% - 100rem);
font-family: PingFang SC; font-family: PingFang SC;
font-size: 18rem; font-size: 18rem;
font-weight: 600; font-weight: 600;
@ -56,5 +58,12 @@ onMounted(() => {
background-clip: text; background-clip: text;
text-fill-color: transparent; text-fill-color: transparent;
} }
img {
position: absolute;
right: 0;
top: -3rem;
width: 180rem;
}
} }
</style> </style>

@ -91,7 +91,7 @@ export const useAppStore = defineStore('app', () => {
const navigationCorpus = ref(initialNavigationCorpus) const navigationCorpus = ref(initialNavigationCorpus)
const routeAction = ref('push') const routeAction = ref('push')
const direction = ref('forward') const direction = ref('forward')
const mescrollContent = ref('') const mescrollContent = ref(null)
const isLeaveMessage = ref(false) const isLeaveMessage = ref(false)
const isLeaveMessageSuccess = ref(initialIsLeaveMessageSuccess) const isLeaveMessageSuccess = ref(initialIsLeaveMessageSuccess)
const manualService = ref(1) const manualService = ref(1)
@ -725,6 +725,8 @@ export const useAppStore = defineStore('app', () => {
console.log('发送消息==', sendParmas.value) console.log('发送消息==', sendParmas.value)
timestamp.value = sendParmas.value.timestamp timestamp.value = sendParmas.value.timestamp
sendLoading.value = true
sendParmas.value.sendLoading = true
// 存储正在发送的那条数据 // 存储正在发送的那条数据
setCurrentItemId(sendParmas.value.timestamp) setCurrentItemId(sendParmas.value.timestamp)
@ -774,14 +776,18 @@ export const useAppStore = defineStore('app', () => {
} }
function setFindActiveItem(findActiveItem) { function setFindActiveItem(findActiveItem) {
if (chatList.value && chatList.value.length > 0) { const syncList = (list) => {
chatList.value.forEach((item) => { if (!list || list.length === 0) return
list.forEach((item) => {
if (currentItemId.value == item.timestamp) { if (currentItemId.value == item.timestamp) {
item.isSendSuccess = findActiveItem item.isSendSuccess = findActiveItem
item.sendLoading = false item.sendLoading = false
} }
}) })
} }
syncList(chatList.value)
syncList(chatArr.value)
sendLoading.value = false
} }
function setActiveItemExtra(data) { function setActiveItemExtra(data) {
@ -813,7 +819,9 @@ export const useAppStore = defineStore('app', () => {
function scrollBottom() { function scrollBottom() {
setTimeout(() => { setTimeout(() => {
try { 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) { } catch (error) {
console.log(error.message) console.log(error.message)
} }
@ -1078,6 +1086,7 @@ export const useAppStore = defineStore('app', () => {
return await sendWebsocketMessage(sendparam) return await sendWebsocketMessage(sendparam)
} catch (error) { } catch (error) {
issubmit.value = true issubmit.value = true
sendLoading.value = false
throw error throw error
} }
} }

@ -371,6 +371,26 @@ export function getHost() {
return hostName; 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) { export function Encrypt(Okey, Oiv, payload) {
const key = CryptoJS.enc.Utf8.parse(Okey); const key = CryptoJS.enc.Utf8.parse(Okey);
const iv = CryptoJS.enc.Utf8.parse(Oiv); 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 '' if (typeof value !== 'string' && typeof value !== 'number') return ''
const key = CryptoJS.enc.Utf8.parse(Okey); const key = CryptoJS.enc.Utf8.parse(Okey);
const iv = CryptoJS.enc.Utf8.parse(Oiv); 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 } const options = { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
// 1) 历史 Hex 密文 // 1) 历史 Hex 密文
@ -396,38 +416,80 @@ export function Decryption(Okey, Oiv, value) {
key, key,
options, options,
) )
const hexText = CryptoJS.enc.Utf8.stringify(hexDecrypt) const hexText = safeUtf8(hexDecrypt)
if (hexText) return hexText if (hexText) return hexText
} }
// 2) 兼容原 OpenSSL/Base64 字符串解密(ASR 等存量逻辑) // 2) 兼容原 OpenSSL/Base64 字符串解密(ASR 等存量逻辑)
const opensslDecrypt = CryptoJS.AES.decrypt(normalized, key, options) const opensslDecrypt = CryptoJS.AES.decrypt(normalized, key, options)
const opensslText = CryptoJS.enc.Utf8.stringify(opensslDecrypt) const opensslText = safeUtf8(opensslDecrypt)
if (opensslText) return opensslText if (opensslText) return opensslText
// 3) 显式按裸 Base64 ciphertext 再试(与 Encrypt 输出对齐) // 3) 显式按裸 Base64 ciphertext 再试(与 Encrypt 输出对齐)
const rawDecrypt = CryptoJS.AES.decrypt( try {
{ ciphertext: CryptoJS.enc.Base64.parse(normalized) }, const rawDecrypt = CryptoJS.AES.decrypt(
key, { ciphertext: CryptoJS.enc.Base64.parse(normalized) },
options, key,
) options,
return CryptoJS.enc.Utf8.stringify(rawDecrypt) )
return safeUtf8(rawDecrypt)
} catch (e) {
return ''
}
} }
export function decryptByVersionNo1(versionNo1Cipher, cipherText) { export function decryptByVersionNo1(versionNo1Cipher, cipherText) {
if (!versionNo1Cipher || !cipherText) return '' if (!versionNo1Cipher || !cipherText) return ''
const normalizedCipher = normalizeCipherText(cipherText)
const year = new Date().getFullYear() const year = new Date().getFullYear()
const versionNo1 = DecryptionECB(`${year}${year}${year}${year}`, versionNo1Cipher) // 兼容跨年:versionNo1 可能按加密当年的 year 生成
const key = versionNo1.substring(0, 16) const yearCandidates = [year, year - 1, year - 2, year + 1]
const iv = versionNo1.substring(16, 32)
if (key.length !== 16 || iv.length !== 16) return '' for (const y of yearCandidates) {
return Decryption(key, iv, cipherText) 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) { export function DecryptionECB(keyECB, valueECB) {
const key = CryptoJS.enc.Utf8.parse(keyECB); if (valueECB === undefined || valueECB === null || valueECB === '') return ''
const decrypt = CryptoJS.AES.decrypt(valueECB, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); const key = CryptoJS.enc.Utf8.parse(keyECB)
return CryptoJS.enc.Utf8.stringify(decrypt); 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() { export function isSit() {

Loading…
Cancel
Save