聊天功能联调

master
wujingling0908 2 weeks ago
commit 52d322555e
  1. 1
      liics
  2. 171
      src/components/chat-new.vue
  3. 5
      src/components/footer-bar-input.vue
  4. 69
      src/components/history-content.vue
  5. 61
      src/components/history-main.vue
  6. 5
      src/components/upload-image.vue
  7. 6
      src/router/index.js
  8. 82
      src/store/app.js
  9. 2
      src/utils/conversationApi.js
  10. 8
      src/utils/index.js
  11. 31
      src/views/history.vue

@ -0,0 +1 @@
Subproject commit e9d7f5bf799bf2d3d3df068f35290eba414237ad

@ -2,11 +2,15 @@
import { ref, computed, inject, watch, nextTick, onMounted } from 'vue'
import { showToast } from 'vant'
import _ from 'lodash'
import { showImagePreview } from 'vant'
import xss from 'xss'
import footerBarInput from '@/components/footer-bar-input.vue'
import hotQuestionNew from '@/components/hot-question-new.vue'
import welcomes from '@/components/welcomes.vue'
import abstract from '@/components/abstract.vue'
import chatContent from '@/components/chat-content.vue'
import DynamicImage from '@/components/common/DynamicImage.vue'
import imageService from '@/utils/imageService'
import { messageSend } from '@/utils/api'
import { Post } from '@/utils/https'
import { SJHLCmdList } from '@/utils/constant'
@ -28,9 +32,85 @@ const pageNum = ref(1)
const pageSize = ref(10)
const nolist = ref(false)
const nolistTxt = ref('没有更多数据了')
function getMessageOptions(item) {
return item?.message?.[0]?.options || {}
}
function isImageMessage(item) {
const raw = item?.raw || item || {}
const options = getMessageOptions(item)
const reqAndResType = String(raw.reqAndResType || '')
const msgType = String(raw.msgType || '')
const fromFlag = String(raw.fromFlag || '')
const isProtocolImage =
(reqAndResType === '2' && fromFlag === '1' && msgType !== '01') ||
(reqAndResType === '08' && msgType === '02')
const isTextAnswer = Boolean(raw.msgText) && (msgType === '01' || fromFlag !== '1') && !isProtocolImage
return !isTextAnswer && (
item?.message?.[0]?.type === 'image' ||
isProtocolImage ||
Boolean((item?.imageUrl || item?.imageKey || options.imageUrl || options.imageKey || raw.imgUrl || raw.imgKey || raw.fileKey) && !raw.msgText)
)
}
function getItemImage(item) {
const raw = item?.raw || item || {}
const options = getMessageOptions(item)
const reqAndResType = String(raw.reqAndResType || '')
const msgType = String(raw.msgType || '')
const fromFlag = String(raw.fromFlag || '')
const imageMessage =
(reqAndResType === '2' && fromFlag === '1' && msgType !== '01') ||
(reqAndResType === '08' && msgType === '02')
const directImage = [item?.imageUrl, options.imageUrl, raw.imgUrl]
.map((value) => String(value || ''))
.find((value) => /^(data:|blob:|https?:\/\/)/i.test(value))
return String(
directImage ||
item?.imageKey ||
options.imageKey ||
raw.imgKey ||
raw.fileKey ||
item?.imageUrl ||
options.imageUrl ||
raw.imgUrl ||
(imageMessage ? raw.msgText : '') ||
'',
)
}
function getItemText(item) {
return item?.message?.[0]?.options?.label || ''
if (isImageMessage(item)) return ''
return String(getMessageOptions(item).label || '').replace(/\\n/g, '\n')
}
function isImageUploading(item) {
return isImageMessage(item) && item?.uploadStatus === 'uploading'
}
function isImageFailed(item) {
return isImageMessage(item) && item?.uploadStatus === 'failed'
}
function getItemHtml(item) {
const content = getItemText(item)
if (!content) return ''
// liics renders msgText as sanitized HTML and converts escaped newlines to
// line breaks. Keep the same behavior for msgType=01 and rich msgType=02.
return xss(content.replace(/\\"/g, '"').replace(/\n/g, '<br/>'))
}
async function previewImage(item) {
if (isImageUploading(item) || isImageFailed(item)) return
const imageKey = getItemImage(item)
if (!imageKey) return
const imageUrl = await imageService.getImageByKey(imageKey)
if (typeof imageUrl === 'string' && imageUrl) showImagePreview([imageUrl])
}
const showTyping = computed(
@ -208,6 +288,16 @@ onMounted(async () => {
getHeaderUrl()
getChatHistory()
})
watch(
chatArr,
() => {
nextTick(() => {
const container = mainContainerRef.value
if (container) container.scrollTop = container.scrollHeight
})
},
{ deep: true },
)
</script>
<template>
@ -219,7 +309,25 @@ onMounted(async () => {
<!-- <div class="message-con" v-if="chatArr.length || showTyping">
<div v-for="(item, index) in chatArr" :key="index"
:class="['msg-item', item.type === 'user' ? 'from-user' : 'from-bot']">
<div class="bubble">{{ getItemText(item) }}</div>
<div class="bubble" :class="{ 'image-bubble': isImageMessage(item) }">
<template v-if="isImageMessage(item)">
<DynamicImage
class="message-image"
:image-key="getItemImage(item)"
alt="聊天图片"
:clickable="true"
image-style="display: block; width: 100%; height: 100%; object-fit: cover;"
@click="previewImage(item)"
/>
<div v-if="isImageUploading(item)" class="upload-loading-icon">
<van-loading type="spinner" color="#ffffff" size="16px" />
</div>
<div v-else-if="isImageFailed(item)" class="upload-loading-icon upload-failed-icon">
<van-icon name="warning-o" size="22px" />
</div>
</template>
<span v-else class="message-text" v-html="getItemHtml(item)"></span>
</div>
</div>
<div class="msg-item from-bot" v-if="showTyping">
<div class="bubble typing"><span></span><span></span><span></span></div>
@ -277,6 +385,57 @@ onMounted(async () => {
letter-spacing: 0rem;
color: #17204d;
word-break: break-all;
.message-text {
white-space: normal;
:deep(p) {
margin: 0;
}
}
&.image-bubble {
// width: 130rem;
// height: 130rem;
min-width: 30rem;
min-height: 30rem;
max-width: 130rem;
max-height: 130rem;
position: relative;
background: #f5f5f5;
padding: 0;
overflow: hidden;
.message-image {
display: block;
width: 100%;
height: 100%;
min-width: 30rem;
min-height: 30rem;
border-radius: inherit;
}
.upload-loading-icon {
position: absolute;
inset: 0;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
background: rgba(50, 50, 51, 0.88);
border-radius: inherit;
}
.upload-failed-icon {
font-size: 22rem;
}
}
.message-image {
width: 100%;
height: 100%;
}
}
}
@ -287,6 +446,10 @@ onMounted(async () => {
background: linear-gradient(266deg, #025cea 0%, #3f8cf8 100%);
color: #ffffff;
border-radius: 12rem 12rem 4rem 12rem;
&.image-bubble {
border-radius: 16rem;
}
}
}
@ -297,6 +460,10 @@ onMounted(async () => {
background: #fcfdfd;
box-shadow: 0rem 4rem 10rem 0rem #e7effd;
border-radius: 12rem 12rem 12rem 4rem;
&.image-bubble {
border-radius: 16rem 16rem 16rem 2rem;
}
}
}

@ -506,6 +506,11 @@ function handleImageSent(payload) {
imageKey: payload.imageKey,
uploadStatus: 'success',
})
// liics upload/image returns the follow-up conversation record in res.data.
// Keep it in the chat list after updating the local image bubble.
if (payload.responseData) {
store?.setChatList?.(payload.responseData)
}
addFlag.value = true
imageBoxShow.value = false
scrollTopBody()

@ -1,5 +1,6 @@
<script setup>
import { ref, inject, onMounted } from 'vue'
import { showImagePreview } from 'vant'
import faceJSON from '@/mock/face.json'
import DynamicImage from '@/components/common/DynamicImage.vue'
import TextContent from '@/components/text-content.vue'
@ -14,6 +15,7 @@ import BusinessHandle from '@/components/business-handle.vue'
import EditAddress from '@/components/edit-address.vue'
import PolicyList from '@/components/policyList.vue'
import XuqiPolicyList from '@/components/xuqiPolicyList.vue'
import imageService from '@/utils/imageService'
const props = defineProps({
historylist: { type: Array, default: () => [] },
@ -85,13 +87,47 @@ function replaceALabel(str) {
return str.replace(/<a /g, '<a class="content-link" ')
}
function isImageMessage(item) {
const reqAndResType = String(item?.reqAndResType || '')
const msgType = String(item?.msgType || '')
const fromFlag = String(item?.fromFlag || '')
const isProtocolImage =
(reqAndResType === '2' && fromFlag === '1' && msgType !== '01') ||
(reqAndResType === '08' && msgType === '02')
const isTextAnswer = Boolean(item?.msgText) && (msgType === '01' || fromFlag !== '1') && !isProtocolImage
return (
!isTextAnswer && (
isProtocolImage ||
Boolean(item?.imgUrl || item?.imgKey || item?.fileKey || item?.imageUrl)
)
)
}
function getImageKey(item) {
return String(
item?.imgUrl ||
item?.imgKey ||
item?.fileKey ||
item?.imageUrl ||
(isImageMessage(item) ? item?.msgText : '') ||
'',
)
}
async function previewImage(item) {
const key = getImageKey(item)
if (!key) return
const imageUrl = await imageService.getImageByKey(key)
if (typeof imageUrl === 'string' && imageUrl) showImagePreview([imageUrl])
}
onMounted(() => { processedEmoji() })
</script>
<template>
<div class="history-content-box" :class="elder ? 'elder-wrapper' : ''" v-if="historylist && historylist.length > 0">
<div class="chat-content-box-item" v-for="(item, index) in historylist" :key="index"
v-show="item.msgText && item.msgText != 'null' || item.cmd === 'ServiceSessionOpenedEvent'">
v-show="(item.msgText && item.msgText != 'null') || isImageMessage(item) || item.cmd === 'ServiceSessionOpenedEvent'">
<div class="time-show" v-if="showTimeLabel(item)">
<span class="session-tips">{{ item.timestamp | FormatDate('MM月dd日 hh:mm') }}</span>
</div>
@ -100,9 +136,13 @@ onMounted(() => { processedEmoji() })
<!-- 右边用户消息 -->
<div class="chat-content-list" v-if="item.fromFlag == '1'">
<div class="chat-content-txt">
<div class="chat-txt-content" v-if="item.msgText.indexOf('xss') > -1 || item.msgText.indexOf('i0.a') > -1"
v-text="replaceBrEmoticon(item.msgText, item)"></div>
<div class="chat-txt-content" v-else v-html="replaceBrEmoticon(item.msgText, item)"></div>
<DynamicImage v-if="isImageMessage(item)" class="history-message-image" :image-key="getImageKey(item)"
alt="聊天图片" :clickable="true" @click="previewImage(item)" />
<template v-else-if="item.msgText">
<div class="chat-txt-content" v-if="String(item.msgText).indexOf('xss') > -1 || String(item.msgText).indexOf('i0.a') > -1"
v-text="replaceBrEmoticon(String(item.msgText), item)"></div>
<div class="chat-txt-content" v-else v-html="replaceBrEmoticon(item.msgText, item)"></div>
</template>
</div>
</div>
@ -115,7 +155,9 @@ onMounted(() => { processedEmoji() })
</div>
<div class="chat-items">
<div class="content-left-main">
<div v-if="item.msgText" v-html="replacementRagEmoticonWithLink(item.msgText, item.qaId)"></div>
<DynamicImage v-if="isImageMessage(item)" class="history-message-image history-message-image-left"
:image-key="getImageKey(item)" alt="聊天图片" :clickable="true" @click="previewImage(item)" />
<div v-else-if="item.msgText" v-html="replacementRagEmoticonWithLink(item.msgText, item.qaId)"></div>
</div>
</div>
</div>
@ -163,6 +205,17 @@ onMounted(() => { processedEmoji() })
max-width: 75%;
}
.history-message-image {
display: block;
width: 130px;
min-width: 30px;
height: 130px;
min-height: 30px;
border-radius: 16px 16px 4px 16px;
overflow: hidden;
cursor: pointer;
}
.chat-room-cont {
display: flex;
margin-bottom: 12px;
@ -202,4 +255,8 @@ onMounted(() => { processedEmoji() })
border-radius: 4px 16px 16px 16px;
padding: 14px;
}
</style>
.history-message-image-left {
border-radius: 4px 16px 16px 16px;
}
</style>

@ -50,7 +50,7 @@
<script setup>
import { ref, computed, inject, onMounted, onActivated, nextTick, watch } from 'vue'
import { showToast, Popup, DatePicker } from 'vant'
import { showToast } from 'vant'
import _ from 'lodash'
import MeScroll from 'mescroll.js'
import 'mescroll.js/mescroll.min.css'
@ -59,8 +59,14 @@ import GeriatricDiscourse from '@/components/geriatric-discourse.vue'
import NavBar from '@/components/nav-bar.vue'
import { getHiddenProp, getVisibilityState } from '@/utils'
import Utils from '@/utils/utils.js'
import { channel95500, SJHLCmdList } from '@/utils/constant'
import { Post } from '@/utils/https'
import { SJHLCmdList } from '@/utils/constant'
import { getHistoryChatInfo } from '@/utils/conversationApi'
const props = defineProps({
tokenExist: { type: Boolean, default: false },
isWX: { type: Boolean, default: false },
lostChatInfoNum: { type: [Number, String], default: null },
})
const store = inject('appStore', null)
const userId = inject('userId', ref(''))
@ -80,10 +86,10 @@ const minDate = computed(() => {
const maxDate = computed(() => new Date())
const isWX = ref(false)
const lostChatInfoNum = ref(null)
const tokenExist = ref(false)
const startDate = ref(null)
const isWX = ref(props.isWX)
const lostChatInfoNum = ref(props.lostChatInfoNum)
const tokenExist = computed(() => props.tokenExist || Boolean(store?.token))
const startDate = ref(new Date(new Date().getFullYear(), new Date().getMonth(), 1))
const pageNum = ref(1)
const pageSize = ref(10)
const nolist = ref(false)
@ -97,16 +103,15 @@ const appContent = ref(null)
onMounted(() => {
const urlParams = Utils.getUrlParams()
isWX.value = urlParams.isWX == 'false' ? false : true
tokenExist.value = urlParams.tokenExist == 'true' ? true : false
lostChatInfoNum.value = urlParams.lostChatInfoNum
isWX.value = urlParams.isWX === undefined ? isWX.value : urlParams.isWX !== 'false'
lostChatInfoNum.value = urlParams.lostChatInfoNum || lostChatInfoNum.value
nextTick(() => {
initMescroll()
store?.commit?.(
'set_mescrollContent',
appContent.value?.querySelector?.('#mescroll') || appContent.value
)
store?.setMescrollContent?.(appContent.value?.querySelector?.('#mescroll') || appContent.value)
if (tokenExist.value) {
mescroll.value?.triggerDownScroll?.()
}
var visProp = getHiddenProp()
if (visProp) {
@ -119,7 +124,7 @@ onMounted(() => {
nolistTxt.value = ''
pageNum.value = 1
historylist.value = []
store?.commit?.('delall_chatList')
store?.delAllChatList?.()
mescroll.value?.triggerDownScroll?.()
}
},
@ -131,7 +136,7 @@ onMounted(() => {
onActivated(() => {
nextTick(() => {
window.scrollTo(0, store?.state?.mescrollContent || 0)
window.scrollTo(0, store?.mescrollContent || 0)
})
})
@ -169,14 +174,14 @@ function upCallback(mescrollIns) {
}
setTimeout(() => {
Post('history/chatInfo', params)
getHistoryChatInfo(params)
.then((response) => {
if (response.code && response.code != 0) {
showToast(response.msg)
if (response?.code && response.code != 0) {
showToast(response.msg || '历史记录加载失败')
return
}
if (response.data.list.length > 0) {
var dataList = response.data.list
const dataList = Array.isArray(response?.data?.list) ? response.data.list : []
if (dataList.length > 0) {
pageNum.value++
mescroll.value?.endSuccess?.()
@ -202,7 +207,7 @@ function upCallback(mescrollIns) {
if (SJHLCmdList.includes(item.cmd) && !_.isEmpty(item.VIEW_ID)) {
item.msgText = ''
}
if (item.fromFlag === 1 && item.msgText.indexOf('&') !== -1) {
if (item.fromFlag === 1 && typeof item.msgText === 'string' && item.msgText.indexOf('&') !== -1) {
item.msgText = item.msgText.replace(/&/g, '')
}
})
@ -214,9 +219,11 @@ function upCallback(mescrollIns) {
}, 150)
} else if (topMsg) {
let view = document.querySelector('#' + topMsg.VIEW_ID)
setTimeout(() => {
mescroll.value?.scrollTo?.(view.offsetTop - 20, 0)
}, 350)
if (view) {
setTimeout(() => {
mescroll.value?.scrollTo?.(view.offsetTop - 20, 0)
}, 350)
}
}
})
} else {
@ -227,7 +234,7 @@ function upCallback(mescrollIns) {
}
})
.catch(() => {
pageNum.value--
pageNum.value = Math.max(1, pageNum.value - 1)
mescroll.value?.endErr?.()
nolist.value = true
})
@ -248,7 +255,7 @@ function onconfirm(picker) {
</script>
<style scoped>
@import '~@/assets/style/chat.css';
@import '@/assets/style/chat.css';
.historylist-box {
font-size: 16px;

@ -12,7 +12,7 @@
import { ref, inject, unref } from 'vue'
import { showToast } from 'vant'
import { compressImage } from '@/utils/CompressImage'
import { upLoadImgHttp } from '@/utils/https'
import { uploadConversationImage } from '@/utils/conversationApi'
const emit = defineEmits(['image-selected', 'image-sent', 'image-failed'])
@ -115,7 +115,7 @@ async function handleFileChange(event) {
formData.append('msgSource', String(unref(manualService) || '1'))
formData.append('timestamp', String(Date.now()))
const response = await upLoadImgHttp('FileUpLoad/uploadImgAndVideo', formData)
const response = await uploadConversationImage(formData)
if (isBusinessFailure(response)) {
throw new Error(response?.msg || '图片发送失败')
}
@ -125,6 +125,7 @@ async function handleFileChange(event) {
localId,
imageUrl: data?.imgUrl || data?.url || previewUrl,
imageKey: data?.imgKey || data?.fileKey || data?.key || '',
responseData: data,
response,
})
} catch (error) {

@ -6,6 +6,12 @@ const router = createRouter({
routes: [
{ path: '/', redirect: '/chat' },
{ path: '/chat', name: 'chat', component: () => import('../views/chat.vue') },
{
path: '/history',
name: 'history',
component: () => import('../views/history.vue'),
meta: { title: '洋洋客服-历史记录查询' },
},
// { path: '/uploadPage', name: 'uploadPage', component: () => import('../views/uploadPage.vue') },
// { path: '/claimPage', name: 'claimPage', component: () => import('../views/claimPage/index.vue'), },
// { path: '/outView', name: 'outView', component: () => import('../views/outView.vue') },

@ -378,6 +378,68 @@ export const useAppStore = defineStore('app', () => {
)
}
function getConversationImageSource(payload) {
if (!payload || typeof payload !== 'object') return ''
const options = payload.message?.[0]?.options || {}
const reqAndResType = String(payload.reqAndResType || '')
const msgType = String(payload.msgType || '')
const fromFlag = String(payload.fromFlag || '')
const isProtocolImage =
(reqAndResType === '2' && fromFlag === '1' && msgType !== '01') ||
(reqAndResType === '08' && msgType === '02')
const isTextAnswer = Boolean(payload.msgText) && (msgType === '01' || fromFlag !== '1') && !isProtocolImage
const hasExplicitImageField = !isTextAnswer && Boolean(
payload.imgUrl || payload.imgKey || payload.fileKey ||
(payload.imageUrl && !payload.msgText),
)
const isImageMessage =
!isTextAnswer && (
payload.message?.[0]?.type === 'image' ||
isProtocolImage ||
hasExplicitImageField
)
if (!isImageMessage) {
return payload.imageUrl || options.imageUrl || ''
}
// liics may return an image URL/key in any of these fields. For a
// user upload record (fromFlag=1, reqAndResType=2), msgText is the key.
return (
payload.imageUrl ||
payload.imgUrl ||
payload.imgKey ||
payload.fileKey ||
options.imageUrl ||
options.imageKey ||
payload.msgText ||
''
)
}
function isConversationImage(payload, imageUrl) {
if (!payload || typeof payload !== 'object') return false
const reqAndResType = String(payload.reqAndResType || '')
const msgType = String(payload.msgType || '')
const fromFlag = String(payload.fromFlag || '')
const isProtocolImage =
(reqAndResType === '2' && fromFlag === '1' && msgType !== '01') ||
(reqAndResType === '08' && msgType === '02')
const isTextAnswer = Boolean(payload.msgText) && (msgType === '01' || fromFlag !== '1') && !isProtocolImage
return Boolean(
!isTextAnswer && imageUrl && (
payload.message?.[0]?.type === 'image' ||
isProtocolImage ||
payload.imgUrl ||
payload.imgKey ||
payload.fileKey ||
(payload.imageUrl && !payload.msgText)
),
)
}
// liics 的 qaChat 使用 msgText/fromFlag,chat-new 使用 message/type。
// 在 store 边界统一转换,旧聊天组件继续读取 chatList,新页面读取 chatArr。
function pushConversationMessage(payload, fallbackType = 'bot') {
@ -388,9 +450,8 @@ export const useAppStore = defineStore('app', () => {
if (item == null) return
const content = getConversationMessageText(item)
const imageUrl = typeof item === 'object'
? item.imageUrl || item.imgUrl || item?.message?.[0]?.options?.imageUrl || ''
: ''
const imageUrl = getConversationImageSource(item)
const isImage = isConversationImage(item, imageUrl)
if (!content && !imageUrl) return
const fromFlag = typeof item === 'object' ? String(item.fromFlag || '') : ''
@ -422,6 +483,10 @@ export const useAppStore = defineStore('app', () => {
system: (typeof item === 'object' && item.system) || 'LIICS',
isSendSuccess: typeof item === 'object' ? item.isSendSuccess !== false : true,
imageUrl,
system: item?.system || 'LIICS',
isSendSuccess: item?.isSendSuccess !== false,
imageUrl: isImage ? imageUrl : '',
imageKey: isImage ? imageUrl : '',
raw: item,
// 兼容 chat-content:保留/补齐 liics 字段
msgText: content || (typeof item === 'object' ? item.msgText : '') || '',
@ -436,10 +501,11 @@ export const useAppStore = defineStore('app', () => {
{
value: '',
param: '',
type: imageUrl ? 'image' : 'text',
type: isImage ? 'image' : 'text',
options: {
label: content || '[图片]',
imageUrl,
label: isImage ? '[图片]' : content,
imageUrl: isImage ? imageUrl : '',
imageKey: isImage ? imageUrl : '',
readOnly: messageType !== 'user',
placeholder: '',
},
@ -466,6 +532,7 @@ export const useAppStore = defineStore('app', () => {
isSendSuccess: true,
uploadStatus: 'uploading',
imageUrl,
imageKey: '',
fileName,
message: [
{
@ -475,6 +542,7 @@ export const useAppStore = defineStore('app', () => {
options: {
label: '[图片]',
imageUrl,
imageKey: '',
readOnly: true,
placeholder: '',
},
@ -496,6 +564,8 @@ export const useAppStore = defineStore('app', () => {
const options = message.message?.[0]?.options
if (options && payload.imageUrl) options.imageUrl = payload.imageUrl
if (options && payload.imageKey) options.imageKey = payload.imageKey
if (payload.imageKey) message.imageKey = payload.imageKey
}
// —— 消息发送状态 ——

@ -11,6 +11,8 @@ export const sendVoiceMessage = (payload) => postConversation('upload/hxVoice',
export const uploadConversationImage = (formData) => upLoadImgHttp('upload/image', formData)
export const getHistoryChatInfo = (params = {}) => postConversation('history/chatInfo', params)
export const connectHumanService = (payload) => postConversation('user/connectToEasemob', payload)
export const updateReadMessages = (payload) => postConversation('HumanChat/msgCallBack', payload)

@ -504,9 +504,9 @@ export function jumpOpenWeb(type, url, urlType) {
query: {
ht: useAppStore().source,
openid: openid,
tokenExist: useAppStore().tokenExist,
isWX: useAppStore().isWX,
lostChatInfoNum: useAppStore().lostChatInfoNum,
tokenExist: Boolean(useAppStore().token),
isWX: utils.isWeiXin(),
lostChatInfoNum: useAppStore().isLostMessage || 0,
param: utils.getUrlParams().param
},
});
@ -587,4 +587,4 @@ export function handleURL(url, params) {
});
}
return url + urlPar
}
}

@ -0,0 +1,31 @@
<template>
<history-main
:tokenExist="tokenExist"
:isWX="isWX"
:lostChatInfoNum="lostChatInfoNum"
/>
</template>
<script setup>
import { computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import HistoryMain from '@/components/history-main.vue'
import { useAppStore } from '@/store/app.js'
import Utils from '@/utils/utils.js'
const route = useRoute()
const store = useAppStore()
const tokenExist = computed(() => Boolean(store.token || route.query.tokenExist === 'true'))
const isWX = computed(() =>
route.query.isWX === undefined ? Utils.isWeiXin() : route.query.isWX !== 'false',
)
const lostChatInfoNum = computed(() => route.query.lostChatInfoNum || 0)
onMounted(() => {
if (tokenExist.value && !store.headerAvatar?.robotUrl) {
const request = store.getHeaderUrl?.()
request?.catch((error) => console.warn('获取历史记录头像失败:', error))
}
})
</script>
Loading…
Cancel
Save