This commit is contained in:
@zuopngfei 2025-11-06 14:07:51 +08:00
parent 8250bb2877
commit d5239d0654
7 changed files with 100 additions and 61 deletions

1
components.d.ts vendored
View File

@ -41,6 +41,7 @@ declare module 'vue' {
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
ElSelectV2: typeof import('element-plus/es')['ElSelectV2'] ElSelectV2: typeof import('element-plus/es')['ElSelectV2']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElTable: typeof import('element-plus/es')['ElTable'] ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabPane: typeof import('element-plus/es')['ElTabPane']

View File

@ -19,10 +19,11 @@ export const addApp = (data) => {
}) })
} }
export const deleteRobot = (id) => { export const deleteRobot = (data) => {
return request({ return request({
url: `admin/app/${id}`, url: `admin/app/delete`,
method: 'delete', method: 'post',
data
}) })
} }
export const editRobot = (id, data) => { export const editRobot = (id, data) => {

View File

@ -12,11 +12,11 @@
<!-- <span v-if="element.sender_id != sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.robotInfo.name }}</span> <!-- <span v-if="element.sender_id != sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.robotInfo.name }}</span>
<span v-if="element.sender_id == sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.userInfo.nickname }}</span> --> <span v-if="element.sender_id == sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.userInfo.nickname }}</span> -->
<div class="text" v-if="element.msg_type == 1"> <div class="text" v-if="element.msg_type == 1">
<pre>{{ element.content.messages }}</pre> <pre>{{ element.content.message }}</pre>
</div> </div>
<div class="image" v-if="element.msg_type == 2"> <div class="image" v-if="element.msg_type == 2">
<el-image :src="element.content.messages" fit="cover" <el-image :src="element.content.message" fit="cover"
:preview-src-list="[element.content.messages]" preview-teleported> :preview-src-list="[element.content.message]" preview-teleported>
<template #placeholder> <template #placeholder>
<div class="image-slot">Loading<span class="dot">...</span></div> <div class="image-slot">Loading<span class="dot">...</span></div>
</template> </template>

View File

@ -104,11 +104,13 @@ const formatLastMessage = (user) => {
try { try {
// JSONcontent // JSONcontent
const contentObj = JSON.parse(user.content) const contentObj = {
message: JSON.parse(user.content).message || JSON.parse(user.content).messages
}
// messages // messages
if (contentObj.messages) { if (contentObj.message) {
const content = String(contentObj.messages).slice(0, 30) const content = String(contentObj.message).slice(0, 30)
return content.length > 30 ? content + '...' : content return content.length > 30 ? content + '...' : content
} }

View File

@ -209,7 +209,7 @@ const sortMessagesByTime = (messages) => {
id: m.id || m._id || m.message_id, id: m.id || m._id || m.message_id,
send_time: m.send_time, send_time: m.send_time,
content: typeof m.content === 'object' ? content: typeof m.content === 'object' ?
(m.content.messages || JSON.stringify(m.content)) : (m.content.message || JSON.stringify(m.content)) :
String(m.content).slice(0, 20) + '...' String(m.content).slice(0, 20) + '...'
}))) })))
@ -248,10 +248,13 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
const newMessages = list.map(item => { const newMessages = list.map(item => {
// ChatRecord // ChatRecord
try { try {
item.content = JSON.parse(item.content) const text = JSON.parse(item.content)
item.content = {
message: text.message || text.messages
}
} catch (e) { } catch (e) {
// //
item.content = { messages: item.content } item.content = { message: item.content }
} }
return item return item
}) })
@ -301,13 +304,13 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
if (existingMsg.msg_type === 1) { if (existingMsg.msg_type === 1) {
// //
const existingContent = existingMsg._tempContent || const existingContent = existingMsg._tempContent ||
(typeof existingMsg.content === 'object' ? existingMsg.content.messages : existingMsg.content) (typeof existingMsg.content === 'object' ? existingMsg.content.message : existingMsg.content)
const newContent = typeof newMsg.content === 'object' ? newMsg.content.messages : newMsg.content const newContent = typeof newMsg.content === 'object' ? newMsg.content.message : newMsg.content
contentMatch = existingContent === newContent contentMatch = existingContent === newContent
} else if (existingMsg.msg_type === 2) { } else if (existingMsg.msg_type === 2) {
// //
if (existingMsg._tempImageFile) { if (existingMsg._tempImageFile) {
const newContent = typeof newMsg.content === 'object' ? newMsg.content.messages : newMsg.content const newContent = typeof newMsg.content === 'object' ? newMsg.content.message : newMsg.content
// //
if (typeof newContent === 'string' && newContent.includes(existingMsg._tempImageFile.name.split('.')[0])) { if (typeof newContent === 'string' && newContent.includes(existingMsg._tempImageFile.name.split('.')[0])) {
contentMatch = true contentMatch = true
@ -345,10 +348,10 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
// ID使 // ID使
const existingContentStr = typeof existingMsg.content === 'object' ? const existingContentStr = typeof existingMsg.content === 'object' ?
(existingMsg.content.messages || JSON.stringify(existingMsg.content)) : (existingMsg.content.message || JSON.stringify(existingMsg.content)) :
String(existingMsg.content) String(existingMsg.content)
const newContentStr = typeof newMsg.content === 'object' ? const newContentStr = typeof newMsg.content === 'object' ?
(newMsg.content.messages || JSON.stringify(newMsg.content)) : (newMsg.content.message || JSON.stringify(newMsg.content)) :
String(newMsg.content) String(newMsg.content)
const compositeMatch = existingMsg.sender_id === newMsg.sender_id && const compositeMatch = existingMsg.sender_id === newMsg.sender_id &&
@ -363,7 +366,7 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
// 3. // 3.
console.log('发现新消息:', { console.log('发现新消息:', {
id: newMsg.id || newMsg._id || newMsg.message_id, id: newMsg.id || newMsg._id || newMsg.message_id,
content: typeof newMsg.content === 'object' ? newMsg.content.messages : newMsg.content content: typeof newMsg.content === 'object' ? newMsg.content.message : newMsg.content
}) })
currentMessages.push(newMsg) currentMessages.push(newMsg)
hasUpdates = true hasUpdates = true
@ -481,7 +484,7 @@ const send = async () => {
msg.sender_id === '888888' && msg.sender_id === '888888' &&
msg.msg_type === 1 && msg.msg_type === 1 &&
typeof msg.content === 'object' && typeof msg.content === 'object' &&
msg.content.messages === content && msg.content.message === content &&
(now - (msg._timestamp || 0)) < 5000 (now - (msg._timestamp || 0)) < 5000
) )
@ -503,7 +506,7 @@ const send = async () => {
sender_id: '888888', sender_id: '888888',
sender_name: '平台', sender_name: '平台',
msg_type: 1, msg_type: 1,
content: { messages: content }, content: { message: content },
send_time: '刚刚', send_time: '刚刚',
_timestamp: now, _timestamp: now,
_tempContent: content // _tempContent: content //
@ -520,7 +523,7 @@ const send = async () => {
send_message({ send_message({
app_id: route.query.app_id, app_id: route.query.app_id,
content: JSON.stringify({ content: JSON.stringify({
messages: content message: content
}), }),
msg_type: 1, msg_type: 1,
to_user_id: activeUser.value.sender_id to_user_id: activeUser.value.sender_id
@ -590,7 +593,7 @@ const handleImageChange = (e) => {
sender_id: '888888', sender_id: '888888',
sender_name: '平台', sender_name: '平台',
msg_type: 2, msg_type: 2,
content: { messages: url }, content: { message: url },
send_time: '刚刚', send_time: '刚刚',
_timestamp: now, _timestamp: now,
_tempImageFile: file // _tempImageFile: file //
@ -615,7 +618,7 @@ const handleImageChange = (e) => {
send_message({ send_message({
app_id: route.query.app_id, app_id: route.query.app_id,
msg_type: 2, msg_type: 2,
content: JSON.stringify({ messages: imageUrl }), content: JSON.stringify({ message: imageUrl }),
to_user_id: activeUser.value.sender_id to_user_id: activeUser.value.sender_id
}).then(() => { }).then(() => {
// //

View File

@ -11,10 +11,9 @@
@selection-change="handleSelectionChange" row-key="id"> @selection-change="handleSelectionChange" row-key="id">
<template #empty> <template #empty>
<span v-if="tableLoading">加载中...</span> <span v-if="tableLoading">加载中...</span>
<span v-if="!tableLoading && query.robot_id">暂无数据</span> <span v-if="!tableLoading">暂无数据</span>
<span v-if="!query.robot_id && !tableLoading" style="font-weight: bold;">请选择一个客服</span>
</template> </template>
<el-table-column type="selection" width="55" :reserve-selection="true" /> <!-- <el-table-column type="selection" width="55" :reserve-selection="true" /> -->
<el-table-column prop="avatar" label="头像" align="center"> <el-table-column prop="avatar" label="头像" align="center">
<template #default="scoped"> <template #default="scoped">
<el-image style="width: 30px;display: block;margin: auto;border-radius: 4px;" <el-image style="width: 30px;display: block;margin: auto;border-radius: 4px;"
@ -28,7 +27,7 @@
<el-table-column prop="created_at" label="添加时间" align="center" <el-table-column prop="created_at" label="添加时间" align="center"
min-width="160"></el-table-column> min-width="160"></el-table-column>
<el-table-column prop="" label="操作" width="100" align="center" fixed="right"> <el-table-column prop="" label="操作" align="center" fixed="right" width="220">
<template #default="scoped"> <template #default="scoped">
<div class="mht-operations"> <div class="mht-operations">
@ -104,12 +103,12 @@
<div class="dialog-box"> <div class="dialog-box">
<el-table ref="appTableRef" :data="logList" style="width: 100%" max-height="500px" row-key="id" <el-table ref="appTableRef" :data="logList" style="width: 100%" max-height="500px" row-key="id"
@selection-change="appSelectionChange"> @selection-change="appSelectionChange" v-loading="tableLoading">
<template #empty> <template #empty>
<span v-if="tableLoading">加载中...</span> <span v-if="tableLoading">加载中...</span>
<span v-if="!tableLoading">暂无数据</span> <span v-if="!tableLoading">暂无数据</span>
</template> </template>
<el-table-column type="selection" width="55" :reserve-selection="true" :selectable="isSelectable" /> <el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column prop="avatar" label="头像" align="center"> <el-table-column prop="avatar" label="头像" align="center">
<template #default="scoped"> <template #default="scoped">
<el-image style="width: 30px;display: block;margin: auto;border-radius: 4px;" <el-image style="width: 30px;display: block;margin: auto;border-radius: 4px;"
@ -214,9 +213,14 @@ const query = reactive({
const total = ref(0) const total = ref(0)
const mpTable = ref([]) const mpTable = ref([])
const getTable = async () => { const getTable = async () => {
const res = await robotList(query) tableLoading.value = true
robotCards.value = res.list const res = await robotList(query).then(res => {
total.value = res.total robotCards.value = res.list
total.value = res.total
tableLoading.value = false
}).catch(() => {
tableLoading.value = false
})
} }
const listLoad = () => { const listLoad = () => {
@ -329,16 +333,16 @@ const handleDelete = (item, index) => {
} }
) )
.then(async () => { .then(async () => {
ElNotification({
message: '删除中,请稍等',
type: 'success',
duration: 2000
})
await deleteRobot({ await deleteRobot({
ids: String(item.id) ids: String(item.id)
}) })
// getGroups()
robotCards.value.splice(index, 1) robotCards.value.splice(index, 1)
ElNotification({
message: '删除成功',
type: 'success',
duration: 2000
})
}) })
.catch(() => { .catch(() => {
@ -381,7 +385,6 @@ const openLog = async () => {
dialogLog.value = true dialogLog.value = true
tableLoading.value = true tableLoading.value = true
const res = await appList(logQuery) const res = await appList(logQuery)
tableLoading.value = false
logList.value = res.list logList.value = res.list
total.value = res.total total.value = res.total
@ -392,6 +395,8 @@ const openLog = async () => {
page: 1, page: 1,
page_size: 100 page_size: 100
}) })
tableLoading.value = false
bandAppIds.value = res2.list.map(item => item.id) bandAppIds.value = res2.list.map(item => item.id)
// appSelects // appSelects
appSelects = [...bandAppIds.value] appSelects = [...bandAppIds.value]

View File

@ -11,10 +11,9 @@
@selection-change="handleSelectionChange" row-key="user_id"> @selection-change="handleSelectionChange" row-key="user_id">
<template #empty> <template #empty>
<span v-if="tableLoading">加载中...</span> <span v-if="tableLoading">加载中...</span>
<span v-if="!tableLoading && query.robot_id">暂无数据</span> <span v-if="!tableLoading">暂无数据</span>
<span v-if="!query.robot_id && !tableLoading" style="font-weight: bold;">请选择一个小程序</span>
</template> </template>
<el-table-column type="selection" width="55" :reserve-selection="true" /> <!-- <el-table-column type="selection" width="55" :reserve-selection="true" /> -->
<el-table-column prop="avatar" label="头像" align="center"> <el-table-column prop="avatar" label="头像" align="center">
<template #default="scoped"> <template #default="scoped">
<el-image style="width: 30px;display: block;margin: auto;border-radius: 4px;" <el-image style="width: 30px;display: block;margin: auto;border-radius: 4px;"
@ -26,6 +25,14 @@
<el-table-column prop="app_secret" label="AppSecret" /> <el-table-column prop="app_secret" label="AppSecret" />
<el-table-column prop="template_id" label="模版ID" /> <el-table-column prop="template_id" label="模版ID" />
<el-table-column prop="description" label="描述" /> <el-table-column prop="description" label="描述" />
<el-table-column prop="message_total" label="会话量" />
<el-table-column prop="check_status_text" label="状态">
<template #default="scoped">
<el-tag v-if="scoped.row.check_status_text"
:type="scoped.row.check_status_text === '正常' ? 'success' : 'danger'">{{
scoped.row.check_status_text }}</el-tag>
</template>
</el-table-column>
<el-table-column label="意图关键字" align="center"> <el-table-column label="意图关键字" align="center">
<template #default="scoped"> <template #default="scoped">
<div class="mht-operations"> <div class="mht-operations">
@ -36,7 +43,7 @@
<el-table-column prop="created_at" label="添加时间" align="center" <el-table-column prop="created_at" label="添加时间" align="center"
min-width="160"></el-table-column> min-width="160"></el-table-column>
<el-table-column prop="" label="操作" width="100" align="center" fixed="right"> <el-table-column prop="" label="操作" align="center" fixed="right" width="280">
<template #default="scoped"> <template #default="scoped">
<div class="mht-operations"> <div class="mht-operations">
@ -49,7 +56,7 @@
</el-button> </el-button>
<el-button type="primary" link @click="handleDetail(scoped.row)"> <el-button type="primary" link @click="handleDetail(scoped.row)">
详情 进入会话
</el-button> </el-button>
<el-button type="danger" link @click="handeleDelete(scoped.row)"> <el-button type="danger" link @click="handeleDelete(scoped.row)">
@ -89,11 +96,11 @@
<el-input v-model="ruleForm.template_id" placeholder="请输入模板ID" size="large" /> <el-input v-model="ruleForm.template_id" placeholder="请输入模板ID" size="large" />
</el-form-item> </el-form-item>
<el-form-item label="头像" prop="avatar"> <el-form-item label="头像">
<Upload v-model="ruleForm.avatar" type="image" accept="image/*" :action="robotHost" <Upload v-model="ruleForm.avatar" type="image" accept="image/*" :action="robotHost"
@change="changeFile" @success="uploadSuccess" @error="uploadError"></Upload> @change="changeFile" @success="uploadSuccess" @error="uploadError"></Upload>
</el-form-item> </el-form-item>
<el-form-item label="小程序描述" prop="description"> <el-form-item label="小程序描述">
<el-input type="textarea" row="3" v-model="ruleForm.description" placeholder="请输入小程序描述" <el-input type="textarea" row="3" v-model="ruleForm.description" placeholder="请输入小程序描述"
size="large" /> size="large" />
</el-form-item> </el-form-item>
@ -145,9 +152,15 @@
</template> </template>
</el-dialog> </el-dialog>
<el-dialog v-model="qrDialog" title="小程序二维码" width="500px" align-center :before-close="handleClose"> <el-dialog v-model="qrDialog" title="小程序二维码" width="610px" align-center :before-close="handleClose">
<div class="dialog-box" style="text-align: center;"> <div class="dialog-box">
<img :src="qrCode" style="width: 200px;" alt="">
<div>小程序路径</div>
<el-space style="margin: 20px 0">
<el-input v-model="qrPath" style="width: 400px;margin-right: 10px;" placeholder="请输入小程序路径" />
<el-button type="primary" @click="getQrCode">获取二维码</el-button>
</el-space>
<img :src="qrCode" style="width: 200px;margin: auto;display: block;" alt="">
</div> </div>
<template #footer> <template #footer>
@ -292,9 +305,14 @@ const query = reactive({
let robtoTotal = 0 let robtoTotal = 0
const mpTable = ref([]) const mpTable = ref([])
const getTable = async () => { const getTable = async () => {
const res = await appList(query) tableLoading.value = true
robotCards.value = res.list const res = await appList(query).then(res => {
total.value = res.total robotCards.value = res.list
total.value = res.total
tableLoading.value = false
}).catch(() => {
tableLoading.value = false
})
} }
@ -395,15 +413,17 @@ const handeleDelete = (item, index) => {
} }
) )
.then(async () => { .then(async () => {
await deleteRobot({
ids: String(item.id)
})
// getGroups()
robotCards.value.splice(index, 1)
ElNotification({ ElNotification({
message: '删除中,请稍等', message: '删除成功',
type: 'success', type: 'success',
duration: 2000 duration: 2000
}) })
await deleteRobot(item.id)
// getGroups()
robotCards.value.splice(index, 1)
}) })
.catch(() => { .catch(() => {
@ -489,13 +509,20 @@ const handleDetail = (row) => {
const qrCode = ref('') const qrCode = ref('')
const qrDialog = ref(false) const qrDialog = ref(false)
const qrPath = ref('pages/contact/index')
let qrRow = {}
const handleqr = (row) => { const handleqr = (row) => {
qrCode.value = ''
qrDialog.value = true
qrRow = row
}
const getQrCode = () => {
getQr({ getQr({
"app_id": row.app_id, "app_id": qrRow.app_id,
"app_secret": row.app_secret, "app_secret": qrRow.app_secret,
"path": "pages/contact/index" "path": qrPath.value
}).then((res => { }).then((res => {
qrDialog.value = true
qrCode.value = `data:image/png;base64,${res.data}` qrCode.value = `data:image/png;base64,${res.data}`
})) }))
} }