This commit is contained in:
@zuopngfei 2025-10-20 18:54:15 +08:00
parent 1dbf6ffa5a
commit c48931dd31
10 changed files with 60 additions and 32 deletions

12
components.d.ts vendored
View File

@ -12,10 +12,13 @@ declare module 'vue' {
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader'] ElHeader: typeof import('element-plus/es')['ElHeader']
@ -23,21 +26,30 @@ declare module 'vue' {
ElImage: typeof import('element-plus/es')['ElImage'] ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElMain: typeof import('element-plus/es')['ElMain'] ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination'] ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow'] ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
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']
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']
ElTabs: typeof import('element-plus/es')['ElTabs'] ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTour: typeof import('element-plus/es')['ElTour']
ElTourStep: typeof import('element-plus/es')['ElTourStep']
IndexUser: typeof import('./src/components/TagClass/indexUser.vue')['default'] IndexUser: typeof import('./src/components/TagClass/indexUser.vue')['default']
KeyWords: typeof import('./src/components/KeyWords/index.vue')['default'] KeyWords: typeof import('./src/components/KeyWords/index.vue')['default']
MaterialPublic: typeof import('./src/components/MaterialPublic/index.vue')['default'] MaterialPublic: typeof import('./src/components/MaterialPublic/index.vue')['default']

View File

@ -3,14 +3,20 @@ import request from '@/utils/request'
// export const getUserList = (params) => {
// return request({
// url: `admin/app/users`,
// method: 'get',
// params
// })
// }
export const getUserList = (params) => { export const getUserList = (params) => {
return request({ return request({
url: `admin/app/users`, url: `admin/messages/latest`,
method: 'get', method: 'get',
params params
}) })
} }
export const send_message = (data) => { export const send_message = (data) => {
return request({ return request({
url: `admin/send_message`, url: `admin/send_message`,

View File

@ -1,12 +1,12 @@
<template> <template>
<div v-for="(element, index) in list"> <div v-for="(element, index) in list">
<div class="materia-element" :class="{ robotSend : element.sender_id != sendeInfo.userInfo.user_id }"> <div class="materia-element" :class="{ robotSend : element.sender_id != sendeInfo.userInfo.user_id }">
<el-image v-if="element.sender_id == sendeInfo.robotInfo.wx_id" class="tobot-image" :src="sendeInfo.robotInfo.head_url"></el-image> <!-- <el-image v-if="element.sender_id == sendeInfo.robotInfo.wx_id" class="tobot-image" :src="sendeInfo.robotInfo.head_url"></el-image>
<template v-else> <template v-else> -->
<el-image v-if="sendeInfo.userInfo.user_id != element.sender_id" class="tobot-image" :src="robot_avatar"></el-image> <el-image v-if="sendeInfo.userInfo.user_id != element.sender_id" class="tobot-image" :src="robot_avatar"></el-image>
<el-image v-if="sendeInfo.userInfo.user_id == element.sender_id" class="tobot-image" :src="sendeInfo.userInfo.user_avatar"></el-image> <el-image v-if="sendeInfo.userInfo.user_id == element.sender_id" class="tobot-image" :src="sendeInfo.userInfo.user_avatar"></el-image>
<!-- <span class="tobot-image text-logo" v-else> {{ element.sender_name.charAt(0) }} </span> --> <!-- <span class="tobot-image text-logo" v-else> {{ element.sender_name.charAt(0) }} </span> -->
</template> <!-- </template> -->
<div class="materia-info"> <div class="materia-info">
<!-- <span class="robot-name">{{ element.sender_name }}</span> --> <!-- <span class="robot-name">{{ element.sender_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.robotInfo.name }}</span>

View File

@ -2,10 +2,10 @@
<div style="width: 100%;"> <div style="width: 100%;">
<el-input v-model="centent" suffix-icon="Search" @input="inputSearch" placeholder="搜索"></el-input> <el-input v-model="centent" suffix-icon="Search" @input="inputSearch" placeholder="搜索"></el-input>
<ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto" :style="'height:' + height" :infinite-scroll-immediate="false" infinite-scroll-distance="5"> <ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto" :style="'height:' + height" :infinite-scroll-immediate="false" infinite-scroll-distance="5">
<li v-for="item in cardlist" :key="item.user_id" class="infinite-list-item" :class="{ active: item.active }" <li v-for="item in cardlist" :key="item.sender_id" class="infinite-list-item" :class="{ active: item.active }"
@click="handleItem(item)"> @click="handleItem(item)">
<el-image class="user-avatar" :src="item.user_avatar"></el-image> <el-image class="user-avatar" :src="item.sender_avatar"></el-image>
<span class="nickname">{{ item.user_name }}</span> <span class="nickname">{{ item.sender_name }}</span>
<span class="sex"> <span class="sex">
<!-- <el-icon v-if="item.sex == '男'" style="color: rgb(121.3, 187.1, 255);"> <!-- <el-icon v-if="item.sex == '男'" style="color: rgb(121.3, 187.1, 255);">
<svg class="icon" aria-hidden="true"> <svg class="icon" aria-hidden="true">
@ -67,7 +67,7 @@ const handleItem = (row) => {
let arr = [] let arr = []
props.cardlist.forEach((el) => { props.cardlist.forEach((el) => {
if (el.active === true) { if (el.active === true) {
arr.push(el.user_id) arr.push(el.sender_id)
} }
}) })
setTimeout(() => { setTimeout(() => {
@ -81,8 +81,8 @@ const handleItem = (row) => {
// true // true
row.active = true row.active = true
// emitcardlistactive // emitcardlistactive
emits('change', row.user_id) emits('change', row.sender_id)
emits("update:modelValue", row.user_id) emits("update:modelValue", row.sender_id)
} }
} }

View File

@ -14,7 +14,7 @@ const goHome = () => {
<template> <template>
<div class="logo"> <div class="logo">
SCRM 小程序聊天管理
</div> </div>
</template> </template>

View File

@ -1,3 +1,3 @@
export default { export default {
title: '猕猴桃 Scrm', //项目名称 title: '小程序聊天管理', //项目名称
} }

View File

@ -69,7 +69,7 @@ const sendeInfo = reactive({
// //
const msgQuery = reactive({ const msgQuery = reactive({
page: 1, page: 1,
page_size: 190, page_size: 100,
app_id: route.query.app_id, app_id: route.query.app_id,
user_id: '' user_id: ''
}) })
@ -81,22 +81,24 @@ const isAutoScroll = ref(true) // 是否自动滚动到底部
const messageTimer = ref(null) // const messageTimer = ref(null) //
// //
const onSelectUser = (id) => { const onSelectUser = async (id) => {
const u = userList.value.find((it) => it.user_id == id) const u = userList.value.find((it) => it.sender_id == id)
if (u) { if (u) {
userList.value.forEach((it) => (it.active = it.user_id === id)) userList.value.forEach((it) => (it.active = it.sender_id === id))
activeUser.value = u activeUser.value = u
sendeInfo.userInfo = { user_id: u.user_id, user_name: u.user_name, user_avatar: u.user_avatar } sendeInfo.userInfo = { user_id: u.sender_id, user_name: u.sender_name, user_avatar: u.sender_avatar }
sendeInfo.robotInfo = { wx_id: 'robot', head_url: mihoutai } sendeInfo.robotInfo = { wx_id: 'robot', head_url: mihoutai }
// //
messages.value = [] messages.value = []
msgQuery.page = 1 msgQuery.page = 1
msgQuery.user_id = u.user_id msgQuery.user_id = u.sender_id
noMoreMessages.value = false noMoreMessages.value = false
// await // await
scrollToBottom() scrollToBottom()
// await scrollToBottom()
getMessages() getMessages()
// //
@ -225,7 +227,7 @@ const loadMoreMessages = () => {
// //
const send = () => { const send = () => {
if (!activeUser.value.user_id) { if (!activeUser.value.sender_id) {
ElMessage({ type: 'warning', message: '请先选择一个联系人' }) ElMessage({ type: 'warning', message: '请先选择一个联系人' })
return return
} }
@ -259,7 +261,7 @@ const send = () => {
messages: content messages: content
}), }),
msg_type: 1, msg_type: 1,
to_user_id: activeUser.value.user_id to_user_id: activeUser.value.sender_id
}).then(() => { }).then(() => {
msg._sending = false msg._sending = false
msg._failed = false msg._failed = false
@ -312,13 +314,13 @@ const handleImageChange = (e) => {
form.append('file', file) form.append('file', file)
form.append('app_id', route.query.app_id) form.append('app_id', route.query.app_id)
form.append('msg_type', 2) form.append('msg_type', 2)
form.append('to_user_id', activeUser.value.user_id) form.append('to_user_id', activeUser.value.sender_id)
uploadFile(form, import.meta.env.VITE_APP_BASE_API + 'admin/upload/image').then((resp) => { uploadFile(form, import.meta.env.VITE_APP_BASE_API + 'admin/upload/image').then((resp) => {
send_message({ send_message({
app_id: route.query.app_id, app_id: route.query.app_id,
content: JSON.stringify({ messages: import.meta.env.VITE_APP_BASE_API + resp.preview_image_url }), content: JSON.stringify({ messages: import.meta.env.VITE_APP_BASE_API + resp.preview_image_url }),
msg_type: 2, msg_type: 2,
to_user_id: activeUser.value.user_id to_user_id: activeUser.value.sender_id
}).then(() => { }).then(() => {
msg._sending = false msg._sending = false
msg._failed = false msg._failed = false
@ -343,7 +345,7 @@ const handleRetry = (id) => {
app_id: route.query.app_id, app_id: route.query.app_id,
content: msg.content.content, content: msg.content.content,
msg_type: 1, msg_type: 1,
to_user_id: activeUser.value.user_id to_user_id: activeUser.value.sender_id
}).then(() => { }).then(() => {
msg._sending = false msg._sending = false
}).catch(() => { }).catch(() => {
@ -356,7 +358,7 @@ const handleRetry = (id) => {
form.append('file', msg.content.file) form.append('file', msg.content.file)
form.append('app_id', route.query.app_id) form.append('app_id', route.query.app_id)
form.append('msg_type', 2) form.append('msg_type', 2)
form.append('to_user_id', activeUser.value.user_id) form.append('to_user_id', activeUser.value.sender_id)
send_message(form).then(() => { send_message(form).then(() => {
msg._sending = false msg._sending = false
}).catch(() => { }).catch(() => {
@ -399,8 +401,8 @@ const getUsers = (append = false) => {
userList.value = res.list || [] userList.value = res.list || []
} }
// //
if (!append && userList.value.length && !activeUser.value.user_id) { if (!append && userList.value.length && !activeUser.value.sender_id) {
onSelectUser(userList.value[0].user_id) onSelectUser(userList.value[0].sender_id)
} }
}) })
} }
@ -459,7 +461,7 @@ onUnmounted(() => {
// //
watch(activeUser, (newVal) => { watch(activeUser, (newVal) => {
if (newVal.user_id) { if (newVal.sender_id) {
restartMessageTimer() restartMessageTimer()
} else { } else {
stopMessageTimer() stopMessageTimer()

View File

@ -12,7 +12,7 @@ import { getTime } from '@/utils/time';
<div class="box"> <div class="box">
<div class="bottom"> <div class="bottom">
<h3 class="title">尊敬的用户{{ getTime() }} </h3> <h3 class="title">尊敬的用户{{ getTime() }} </h3>
<p class="subtitle">欢迎使用 猕猴桃 SCRM 系统</p> <p class="subtitle">欢迎使用 小程序聊天管理 系统</p>
</div> </div>
</div> </div>
</el-card> </el-card>

View File

@ -40,7 +40,7 @@
<el-col :span="12"> <el-col :span="12">
<div class="row-login"> <div class="row-login">
<div class="login-form"> <div class="login-form">
<h2>SCRM 登录</h2> <h2>小程序聊天管理 登录</h2>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="0"> <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="0">
<el-form-item label="" prop="username" style="margin-bottom: 30px;"> <el-form-item label="" prop="username" style="margin-bottom: 30px;">
<el-input v-model="ruleForm.username" placeholder="用户名" prefix-icon="User" size="large" /> <el-input v-model="ruleForm.username" placeholder="用户名" prefix-icon="User" size="large" />

View File

@ -94,7 +94,9 @@
<el-form-item label="小程序ID" prop="app_id"> <el-form-item label="小程序ID" prop="app_id">
<el-input v-model="ruleForm.app_id" placeholder="请输入小程序ID" size="large" /> <el-input v-model="ruleForm.app_id" placeholder="请输入小程序ID" size="large" />
</el-form-item> </el-form-item>
<el-form-item label="App_Secret" prop="app_secret">
<el-input v-model="ruleForm.app_secret" placeholder="请输入app_secret" size="large" />
</el-form-item>
<el-form-item label="头像" prop="avatar"> <el-form-item label="头像" prop="avatar">
<Upload v-model="ruleForm.avatar" type="image" accept="image/*" :action="robotHost" <Upload v-model="ruleForm.avatar" type="image" accept="image/*" :action="robotHost"
@ -206,6 +208,11 @@ const rules = ref({
required: true, required: true,
message: '请上传小程序头像', message: '请上传小程序头像',
trigger: 'change', trigger: 'change',
}],
app_secret: [{
required: true,
message: '请上传小程序头像',
trigger: 'change',
}] }]
}) })
const query = reactive({ const query = reactive({
@ -260,7 +267,8 @@ const submitRobot = async () => {
description: ruleForm.value.description, description: ruleForm.value.description,
name: ruleForm.value.name, name: ruleForm.value.name,
app_id: ruleForm.value.app_id, app_id: ruleForm.value.app_id,
avatar: ruleForm.value.avatar avatar: ruleForm.value.avatar,
app_secret: ruleForm.value.app_secret
}) })
// editItem = ruleForm.value // editItem = ruleForm.value
// robotCards.value[editIndex] = editItem // robotCards.value[editIndex] = editItem