消息逻辑完善

This commit is contained in:
BianLzhaoMin 2024-12-25 14:24:34 +08:00
parent 4f947e09b1
commit d86c8b4b33
13 changed files with 5749 additions and 5365 deletions

9
components.d.ts vendored
View File

@ -23,13 +23,11 @@ declare module 'vue' {
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElCountdown: typeof import('element-plus/es')['ElCountdown']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
@ -37,7 +35,6 @@ declare module 'vue' {
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElProgress: typeof import('element-plus/es')['ElProgress']
@ -45,9 +42,6 @@ declare module 'vue' {
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
@ -71,4 +65,7 @@ declare module 'vue' {
UploadComponentNewTwo: typeof import('./src/components/uploadComponentNewTwo/index.vue')['default']
UploadImg: typeof import('./src/components/uploadImg.vue')['default']
}
export interface ComponentCustomProperties {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
}
}

6
env/.env.dev vendored
View File

@ -8,9 +8,9 @@ VITE_API_URL = '/proxyApi'
# VITE_proxyTarget = 'http://10.40.92.74:8080' #盛旭
# VITE_proxyTarget = 'http://192.168.2.246:28080' # 马帅
# VITE_proxyTarget = 'http://192.168.2.127:28080' # 梁超
VITE_proxyTarget = 'http://36.33.26.201:17788/proxyApi' # 测试服务
# VITE_proxyTarget = 'http://192.168.0.244:28580' # 测试服务
# VITE_proxyTarget = 'http://192.168.2.122:28080' # 梁超
# VITE_proxyTarget = 'http://36.33.26.201:17788/proxyApi' # 测试服务
VITE_proxyTarget = 'http://192.168.0.244:28580' # 测试服务
# VITE_proxyTarget = 'http://192.168.2.75:28080' # 盛旭
# VITE_proxyTarget = 'http://10.40.92.185:9206' # 赵福海 ( 设备类型)

10522
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@
"file-saver": "^2.0.5",
"force": "^0.0.3",
"js-base64": "^3.7.7",
"lodash-es": "^4.17.21",
"mitt": "^3.0.1",
"moment": "^2.29.4",
"nprogress": "^0.2.0",

View File

@ -157,13 +157,19 @@ const isMyInfoPage = () => {
if (
route.path.indexOf('cart') !== -1 ||
route.path.indexOf('demand-details') !== -1 ||
route.path.indexOf('order-confirm') !== -1
route.path.indexOf('order-confirm') !== -1 ||
route.path.indexOf('message') !== -1
) {
return true
} else {
return false
}
}
const onJumpMessage = () => {
router.push({
name: 'message',
})
}
</script>
<template>
@ -248,6 +254,10 @@ const isMyInfoPage = () => {
</div>
</div>
</div>
<div class="header-item">
<a @click="onJumpMessage"> 消息 </a>
<div class="line"></div>
</div>
<div class="header-item">
<a @click="onClickMyUser"> 个人中心 </a>
<div class="line"></div>

View File

@ -0,0 +1,19 @@
import { get, post } from '../../index'
// 消息 获取消息列表
export const getMessageListApi = () => {
return get('/material-mall/bm_message/list', {})
}
// 消息 获取消息详情
export const getMessageInfoApi = (data: any) => {
return get('/material-mall/bm_message/listOneToOne', data)
}
// 消息 发送消息
export const sendMessageApi = (data: any) => {
return post('/material-mall/bm_message', data)
}
// 消息 发送消息
export const messageIsReadApi = (data: any) => {
return post('/material-mall/bm_message/edit', data)
}

View File

@ -63,7 +63,7 @@ service.interceptors.response.use(
export function get(url: string, params: any) {
return new Promise((resolve, reject) => {
NProgress.start()
// NProgress.start()
service
.get(url, { params })
.then((res: any) => {
@ -75,14 +75,14 @@ export function get(url: string, params: any) {
}
})
.catch((err) => {
NProgress.done()
// NProgress.done()
reject(err.data)
})
})
}
export function post(url: string, params: any) {
return new Promise((resolve, reject) => {
NProgress.start()
// NProgress.start()
service
.post(url, params, {
headers: { 'Content-Type': 'application/json; charset=utf-8' }
@ -96,7 +96,7 @@ export function post(url: string, params: any) {
}
})
.catch((err) => {
NProgress.done()
// NProgress.done()
reject(err)
})
})

View File

@ -250,6 +250,13 @@ const onJumpUser = () => {
}
}
}
//
const onJumpMessage = () => {
router.push({
name: 'message',
})
}
</script>
<template>
@ -305,6 +312,10 @@ const onJumpUser = () => {
</div>
</div>
<div class="header-item">
<a @click="onJumpMessage">消息</a>
<div class="line"></div>
</div>
<div class="header-item">
<a @click="onJumpUser">个人中心</a>
<div class="line"></div>

View File

@ -20,9 +20,10 @@ const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
app.component('vue-qr-code', VueQrcode)
}
app.component('QuillEditor', QuillEditor); // 全局注册
app.component('vue-qr-code', VueQrcode)
app.use(pinia)
app.use(router)

View File

@ -205,6 +205,18 @@ const routes: Array<RouteRecordRaw> = [
isLogin: true
},
},
// 消息
{
path: '/message',
name: 'message',
component: () => import('views/message/index.vue'),
meta: {
title: '消息',
keepAlive: true,
AuthFlag: false,
isLogin: true
},
},
// 个人中心
{
@ -761,11 +773,6 @@ const white = ['/qr-code']
// 使页面跳转后滚动条恢复至顶部
router.beforeEach((to, from, next) => {
// const origin = window.location.href
// if (origin.indexOf('ticket') != -1) {
// console.log(origin.split('ticket=')[1], 'origin.split爱玩送ticket')
// // getLoginIwsInfo({ ticket: 996 })
// }
const store = mainStore()
if (to.meta.isLogin && !to.meta.AuthFlag) {
if (store.token) {

View File

@ -250,16 +250,16 @@
<el-table-column align="center" type="index" label="序号" width="80" />
<el-table-column align="center" prop="qcTime" label="检测日期" />
<el-table-column align="center" prop="maintenanceAlarmDay" label="有效期" />
<el-table-column align="center" prop="fileInfo" label="检测证明" >
<el-table-column align="center" prop="fileInfo" label="检测证明">
<template #default="row">
<a
v-if="row.row.fileInfo && row.row.fileInfo.fileUrl"
:href="row.row.fileInfo.fileUrl"
target="_blank"
style="color: #00a288; text-decoration: underline"
>查看</a
>
<span v-else>无检测证明</span>
<a
v-if="row.row.fileInfo && row.row.fileInfo.fileUrl"
:href="row.row.fileInfo.fileUrl"
target="_blank"
style="color: #00a288; text-decoration: underline"
>查看</a
>
<span v-else>无检测证明</span>
</template>
</el-table-column>
<el-table-column align="center" prop="comName" label="检测单位" />
@ -450,44 +450,59 @@
<!-- 在线聊 -->
<el-dialog
title="在线聊"
v-model="dialogChatVisible"
width="80%"
:before-close="handleClose"
>
<div class="chat-container" style="height: 700px; display: flex;">
<div class="contact-list" style="width: 20%; height: 100%;">
<div class="contact-list-header">聊天列表</div>
<el-menu default-active="1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose">
<el-menu-item v-for="(contact, index) in contacts" :key="index" :index="index.toString()">
<el-avatar :src="contact.avatar" />
{{ contact.name }}
<el-badge :value="contact.unreadCount" class="custom-badge" v-if="contact.unreadCount > 0"></el-badge>
</el-menu-item>
<!-- 其他联系人 -->
</el-menu>
</div>
<div class="chat-content" style="width: 70%; height: 100%; display: flex; flex-direction: column;">
<!-- 聊天内容框占右侧高度的80% -->
<div class="message-box" style="flex: 8; overflow-y: auto;">
<!-- 聊天消息 -->
<div class="chat-content-header" style="text-align: left;">{{ "聊天人" }}</div>
<div class="message" v-for="message in messages" :key="message.id">
<el-avatar :src="message.avatar" />
<div class="message-text">{{ message.text }}</div>
</div>
title="在线聊"
v-model="dialogChatVisible"
width="80%"
:before-close="handleClose"
>
<div class="chat-container" style="height: 700px; display: flex">
<div class="contact-list" style="width: 20%; height: 100%">
<div class="contact-list-header">聊天列表</div>
<el-menu
default-active="1"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
>
<el-menu-item
v-for="(contact, index) in contacts"
:key="index"
:index="index.toString()"
>
<el-avatar :src="contact.avatar" />
{{ contact.name }}
<el-badge
:value="contact.unreadCount"
class="custom-badge"
v-if="contact.unreadCount > 0"
></el-badge>
</el-menu-item>
<!-- 其他联系人 -->
</el-menu>
</div>
<div
class="chat-content"
style="width: 70%; height: 100%; display: flex; flex-direction: column"
>
<!-- 聊天内容框占右侧高度的80% -->
<div class="message-box" style="flex: 8; overflow-y: auto">
<!-- 聊天消息 -->
<div class="chat-content-header" style="text-align: left">
{{ '聊天人' }}
</div>
<div class="message" v-for="message in messages" :key="message.id">
<el-avatar :src="message.avatar" />
<div class="message-text">{{ message.text }}</div>
</div>
</div>
<!-- 发送消息的输入框占右侧高度的20% -->
<div class="input-box" style="flex: 1; display: flex">
<el-input v-model="newMessage" class="chat-input"></el-input>
<el-button type="primary" @click="sendMessage">发送</el-button>
</div>
</div>
</div>
<!-- 发送消息的输入框占右侧高度的20% -->
<div class="input-box" style="flex: 1;display: flex;">
<el-input
v-model="newMessage"
class="chat-input"
></el-input>
<el-button type="primary" @click="sendMessage">发送</el-button>
</div>
</div>
</div>
</el-dialog>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
@ -1037,7 +1052,7 @@ const dialogVisibles = ref(false)
const imgIndex = ref(0)
const dialogImageUrls = ref([])
//
const picturesPreview = (row:any) => {
const picturesPreview = (row: any) => {
dialogImageUrls.value = []
// window.open(url)
@ -1107,16 +1122,28 @@ const contacts = ref([
])
const unreadCount = ref(1)
const messages = ref([
{ id: 1, avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', text: '你好,欢迎来到在线聊!' },
//
],)
{
id: 1,
avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
text: '你好,欢迎来到在线聊!',
},
//
])
const dialogChatVisible = ref(false)
const onChat = () => {
dialogChatVisible.value = true
// dialogChatVisible.value = true
const { companyId, companyName, ownId } = pageData.value
router.push({
// path: `/orderConfirm/${props.id}`,
name: 'message',
query: {
companyId,
companyName,
ownId,
},
})
}
onMounted(() => {
// getUnreadCount()
})
@ -1568,53 +1595,53 @@ onMounted(() => {
}
}
.chat-container {
display: flex;
display: flex;
}
.contact-list {
width: 200px;
/* border-right: 1px solid #ccc; */
width: 200px;
/* border-right: 1px solid #ccc; */
}
.chat-content {
flex: 1;
padding: 10px;
overflow-y: auto;
background-color: #f8f8f8;
flex: 1;
padding: 10px;
overflow-y: auto;
background-color: #f8f8f8;
}
.message {
display: flex;
align-items: center;
margin-bottom: 10px;
display: flex;
align-items: center;
margin-bottom: 10px;
}
.message-text {
margin-left: 10px;
margin-left: 10px;
}
.chat-input {
width: 100%;
width: 100%;
}
.custom-badge {
position: absolute;
top: 0;
right: 0;
transform: translate(50%, -50%);
margin-right: 10px;
position: absolute;
top: 0;
right: 0;
transform: translate(50%, -50%);
margin-right: 10px;
}
.contact-list-header {
font-size: 18px;
font-weight: bold;
text-align: left;
padding: 10px;
border-bottom: 1px solid #e5e5e5;
font-size: 18px;
font-weight: bold;
text-align: left;
padding: 10px;
border-bottom: 1px solid #e5e5e5;
}
.chat-content-header {
font-size: 18px;
font-weight: bold;
text-align: center;
padding: 10px;
background-color: #f8f8f8;
border-bottom: 1px solid #e5e5e5;
font-size: 18px;
font-weight: bold;
text-align: center;
padding: 10px;
background-color: #f8f8f8;
border-bottom: 1px solid #e5e5e5;
}
</style>

316
src/views/message/index.vue Normal file
View File

@ -0,0 +1,316 @@
<template>
<!-- 消息页面 -->
<div style="display: flex; flex-direction: column; height: 100vh">
<div style="width: 100%; background-color: #f5f5f5">
<Header class="wapper" />
</div>
<div class="container">
<div class="message-left">
<el-card style="width: 100%; height: 100%">
<h2>聊天列表</h2>
<div
class="user-list"
v-for="item in messageList"
:key="item.uuid"
@click="onClickMessage(item)"
>
<div>
<el-avatar :size="40" @error="errorHandler">
<img
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
/>
</el-avatar>
</div>
<div class="message-info">
<div>
{{
myCompanyId == item.toCompany
? item.fromCompanyName
: item.toCompanyName
}}
</div>
<div>
<span>
{{ item.messageContent }}
</span>
<div>
<el-badge is-dot class="item" v-if="item.isRead != 1" />
</div>
</div>
</div>
</div>
</el-card>
</div>
<div class="message-box">
<h2>{{ rightCompanyName }}</h2>
<div class="message-info-list">
<div class="message-content" v-for="item in messageDetails" :key="item.uuid">
<div
class="message-info-right"
:key="item"
v-if="myCompanyId == item.fromCompany"
>
<div class="message-items">{{ item.messageContent }}</div>
<div>
<el-avatar :size="40" @error="errorHandler">
<img
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
/>
</el-avatar>
</div>
</div>
<div class="message-info" v-else>
<div>
<el-avatar :size="40" @error="errorHandler">
<img
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
/>
</el-avatar>
</div>
<div class="message-items">{{ item.messageContent }}</div>
</div>
</div>
</div>
<div class="message-send">
<el-input
v-model="sendParams.messageContent"
style="width: 88%; margin-right: 16px"
:autosize="{ minRows: 2, maxRows: 30 }"
type="textarea"
/>
<el-icon
size="36"
color="#00a288"
style="cursor: pointer"
@click="onSendMessage"
>
<Promotion />
</el-icon>
</div>
</div>
</div>
<FooterInfo />
</div>
</template>
<script setup lang="ts">
import Header from 'components/header/index.vue'
import FooterInfo from 'components/FooterInfo/index.vue'
import {
getMessageListApi,
getMessageInfoApi,
sendMessageApi,
messageIsReadApi,
} from 'http/api/message/index'
import { mainStore } from 'store/main'
import { debounce } from 'lodash-es'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
const myCompanyId = ref('')
const queryDetailsCompanyId = ref('')
const rightCompanyName = ref('')
const store: any = mainStore()
const route = useRoute()
const messageList = ref<any>([])
const messageDetails = ref<any>([])
const messageInterval = ref<any>(null)
const messageListInterval = ref<any>(null)
const sendParams = ref<any>({
toUser: '',
toCompany: '',
messageContent: '',
toCompanyName: '',
fromCompanyName: '',
})
//
const getMessageListData = async () => {
const { data: res }: any = await getMessageListApi()
messageList.value = res
}
//
const getMessageInfoData = async (toCompany: any) => {
const { data: res }: any = await getMessageInfoApi({ toCompany })
messageDetails.value = res
}
//
const onClickMessage = async (item: any) => {
const { toCompany, fromCompany, toCompanyName, fromCompanyName, toUser, fromUser } = item
const isForm = myCompanyId.value == fromCompany
rightCompanyName.value = isForm ? toCompanyName : fromCompanyName
sendParams.value.toUser = isForm ? toUser : fromUser
sendParams.value.toCompany = isForm ? toCompany : fromCompany
sendParams.value.toCompanyName = isForm ? toCompanyName : fromCompanyName
queryDetailsCompanyId.value = isForm ? toCompany : fromCompany
const res: any = await messageIsReadApi({
toCompany: myCompanyId.value,
fromCompany: sendParams.value.toCompany,
})
console.log(res, '消息已读')
if (!messageInterval.value) {
messageInterval.value = setInterval(() => {
getMessageInfoData(queryDetailsCompanyId.value)
}, 5000)
}
}
//
const onSendMessage = debounce(async () => {
sendParams.value.fromCompanyName = localStorage.getItem('currentCompanyName')
const res: any = await sendMessageApi(sendParams.value)
if (res.code == 200) {
sendParams.value.messageContent = ''
}
}, 500)
const errorHandler = () => true
onMounted(() => {
getMessageListData()
if (route.query && route.query.companyId) {
const { companyId, companyName, ownId }: any = route.query
rightCompanyName.value = companyName
sendParams.value.toUser = ownId
sendParams.value.toCompany = companyId
sendParams.value.toCompanyName = companyName
queryDetailsCompanyId.value = companyId
getMessageInfoData(queryDetailsCompanyId.value)
setTimeout(() => {
messageInterval.value = setInterval(() => {
getMessageInfoData(queryDetailsCompanyId.value)
}, 5000)
}, 3000)
}
myCompanyId.value = store.userInfo.companyId
setTimeout(() => {
messageListInterval.value = setInterval(() => {
getMessageListData()
}, 5000)
}, 3000)
})
onBeforeMount(() => {
if (messageInterval.value) {
clearInterval(messageInterval.value)
messageInterval.value = null
}
if (messageListInterval.value) {
clearInterval(messageListInterval.value)
messageListInterval.value = null
}
})
</script>
<style lang="scss" scoped>
.container {
width: 1552px;
margin: 15px auto;
flex: 1;
display: flex;
background: #eeeff6;
font-size: 16px;
padding: 20px 10px;
background-color: #fff;
border-radius: 15px;
h2 {
margin-bottom: 16px;
font-size: 18px;
font-weight: bold;
}
.message-left {
width: 30%;
.user-list {
padding: 10px;
display: flex;
align-items: center;
border-bottom: 1px solid #ccc;
cursor: pointer;
.message-info {
padding-left: 10px;
width: 100%;
div {
padding: 8px 0;
}
& div:first-child {
font-size: 16px;
font-weight: bold;
}
& div:last-child {
display: flex;
align-items: center;
justify-content: space-between;
}
}
}
}
.message-box {
flex: 1;
height: 100%;
padding-left: 50px;
display: flex;
flex-direction: column;
h2 {
padding: 18px 0;
border-bottom: 1px solid #cccc;
}
.message-info-list {
flex: 1;
// max-height: 500px;
overflow-y: auto !important;
}
// .message-content {
// }
.message-send {
min-height: 100px;
padding: 10px 30px;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #ccc;
}
.message-info,
.message-info-right {
display: flex;
align-items: center;
margin-top: 10px;
padding: 0 15px;
.message-items {
display: inline-block;
width: auto;
margin-left: 12px;
padding: 10px 12px;
background-color: #fff;
box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.1);
}
}
.message-info-right {
justify-content: flex-end;
.message-items {
margin-right: 12px !important;
}
}
}
}
</style>

View File

@ -74,6 +74,7 @@ export default ({ mode }: any) => {
},
server: {
host: '0.0.0.0',
port: 8102,
// port: Number(envInfo.VITE_PORT),
// open: envInfo.VITE_OPEN,
proxy: {