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

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. 11
      src/components/hot-question-new.vue
  7. 13
      src/components/welcomes.vue
  8. 17
      src/store/app.js
  9. 82
      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";
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 () => {
</template>
<style lang="scss" scoped>
.abstract-con {
position: relative;
z-index: 1;
flex-direction: column;
justify-content: center;
padding: 10rem 12rem;
gap: 12rem;
align-self: stretch;
margin-top: 10rem;
background: linear-gradient(77deg, #fcfdfd80 -1%, #f5f7fee6 99%);
box-sizing: border-box;

@ -104,7 +104,9 @@ const changeCheck = (item) => {
const scrollTopBody = () => {
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 (err) {
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-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" />
</div>
<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-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" />
</div>
<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-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" />
</div>
<div class="chat-content-image-box" @click="ImagePreviewBox(item.msgText)" :data-timestamp="item.timestamp">

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

@ -78,7 +78,12 @@ onMounted(async () => {
</script>
<template>
<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>
<span>{{ item.name }}</span>
<img src="@/assets/images/chat/line.svg" alt="" />
@ -91,6 +96,8 @@ onMounted(async () => {
flex-direction: column;
gap: 10rem;
margin-top: 16rem;
position: relative;
z-index: 1;
.item {
border-radius: 24rem;
@ -133,7 +140,7 @@ onMounted(async () => {
}
}
>span {
> span {
font-family: PingFang SC;
font-size: 14rem;
font-weight: normal;

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

@ -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
}
}

@ -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 输出对齐)
try {
const rawDecrypt = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Base64.parse(normalized) },
key,
options,
)
return CryptoJS.enc.Utf8.stringify(rawDecrypt)
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)
// 兼容跨年: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) return ''
return Decryption(key, iv, cipherText)
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() {

Loading…
Cancel
Save