feat: 会话列表对接

feature/task1.0.0__0514__ch
向文可 3 years ago
parent 4c92155014
commit 78628cc8a7

@ -1,65 +1,65 @@
{ {
"name": "msb-shop-admin", "name": "msb-shop-admin",
"author": { "author": {
"name": "向文可", "name": "向文可",
"email": "1041367524@qq.com" "email": "1041367524@qq.com"
}, },
"private": true, "private": true,
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build:test": "vite build --mode test", "build:test": "vite build --mode test",
"build:beta": "vite build --mode beta", "build:beta": "vite build --mode beta",
"build:prod": "vite build --mode prod", "build:prod": "vite build --mode prod",
"preview": "vite preview", "preview": "vite preview",
"prepare": "husky install", "prepare": "husky install",
"lint": "eslint src/**/*.{vue,js,jsx} --fix" "lint": "eslint src/**/*.{vue,js,jsx} --fix"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons": "^0.0.11", "@element-plus/icons": "^0.0.11",
"@vueup/vue-quill": "^1.0.0-beta.8", "@vueup/vue-quill": "^1.0.0-beta.8",
"axios": "^0.26.1", "axios": "^0.26.1",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"dayjs": "^1.11.0", "dayjs": "^1.11.0",
"element-plus": "^2.1.7", "element-plus": "^2.1.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"qs": "^6.10.3", "qs": "^6.10.3",
"quill-image-uploader": "^1.2.2", "quill-image-uploader": "^1.2.2",
"sortablejs": "^1.14.0", "sortablejs": "^1.14.0",
"vue": "^3.2.25", "vue": "^3.2.25",
"vue-router": "^4.0.14", "vue-router": "^4.0.14",
"vuex": "^4.0.2", "vuex": "^4.0.2",
"vuex-persistedstate": "^4.1.0" "vuex-persistedstate": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^13.2.1", "@commitlint/cli": "^13.2.1",
"@commitlint/config-conventional": "^13.2.0", "@commitlint/config-conventional": "^13.2.0",
"@nabla/vite-plugin-eslint": "^1.4.0", "@nabla/vite-plugin-eslint": "^1.4.0",
"@originjs/vite-plugin-global-style": "^1.0.2", "@originjs/vite-plugin-global-style": "^1.0.2",
"@types/node": "^17.0.21", "@types/node": "^17.0.21",
"@vitejs/plugin-legacy": "^1.7.1", "@vitejs/plugin-legacy": "^1.7.1",
"@vitejs/plugin-vue": "^2.2.0", "@vitejs/plugin-vue": "^2.2.0",
"@vitejs/plugin-vue-jsx": "^1.3.8", "@vitejs/plugin-vue-jsx": "^1.3.8",
"airbnb": "^0.0.2", "airbnb": "^0.0.2",
"consola": "^2.15.3", "consola": "^2.15.3",
"eslint": "^8.11.0", "eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.5.0", "eslint-plugin-vue": "^8.5.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"less": "^4.1.2", "less": "^4.1.2",
"lint-staged": "^12.3.7", "lint-staged": "^12.3.7",
"prettier": "^2.6.0", "prettier": "^2.6.0",
"unplugin-auto-import": "^0.6.4", "unplugin-auto-import": "^0.6.4",
"unplugin-vue-components": "^0.18.0", "unplugin-vue-components": "^0.18.0",
"vite": "^2.8.0", "vite": "^2.8.0",
"vite-plugin-remove-console": "^0.0.6", "vite-plugin-remove-console": "^0.0.6",
"vite-svg-loader": "^3.1.2" "vite-svg-loader": "^3.1.2"
}, },
"lint-staged": { "lint-staged": {
"src/**/*.{jsx,tsx,ts,js,vue}": [ "src/**/*.{jsx,tsx,ts,js,vue}": [
"prettier --write", "prettier --write",
"eslint --fix" "eslint --fix"
] ]
} }
} }

@ -16,12 +16,17 @@
</template> </template>
<script setup> <script setup>
import LayoutMain from './components/main.vue';
import LayoutAside from './components/aside.vue'; import LayoutAside from './components/aside.vue';
import LayoutMenu from './components/menu.vue'; import LayoutFooter from './components/footer.vue';
import LayoutHeader from './components/header.vue'; import LayoutHeader from './components/header.vue';
import LayoutMain from './components/main.vue';
import LayoutMenu from './components/menu.vue';
import LayoutTabs from './components/tabs.vue'; import LayoutTabs from './components/tabs.vue';
import LayoutFooter from './components/footer.vue'; const store = useStore();
store.dispatch(
'chat/connect',
'eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQwNzA4ODAwMDAsImlkIjoyfQ.NlKpMH_bjVHlbj_GfAf196W4GUhpqmHY3efw7W_1F8WQeovw1ZgEeYo_oY24Q38z_yh6WIplq730ohoU5SLkLxdIxC60T9SoHwgmxOdd8w_Mo0ksOcsQs3xLytJca1KVvOfAT_flTjgOe0q5iSJeNCblTY4hsP6dn1aE69pqcng'
);
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

@ -0,0 +1,127 @@
import { ElMessage } from '@/plugins/element-plus';
import { UUID } from '@/utils/chat';
const state = () => ({
socket: null,
heart: null,
queue: [],
task: [],
sessionData: {},
});
const getters = {};
const mutations = {
setSocket: (state, data) => (state.socket = data),
setHeart: (state, data) => (state.heart = data),
setTask: (state, data) => (state.task = data),
addTask: (state, data) => state.task.push(data),
delTask: (state, data) => state.task.splice(data, 1),
receive: (state, { code, traceType, content }) => {
if (code === 200) {
switch (traceType) {
case 27:
state.sessionData = content;
break;
default:
break;
}
}
},
};
const actions = {
/**
* 创建连接
*/
connect: ({ state, commit, dispatch }, token) => {
return new Promise((resolve, reject) => {
if (window.WebSocket) {
const socket = new WebSocket('ws://192.168.10.93:8090/ws?client=' + token);
socket.onmessage = ({ data }) => {
data = JSON.parse(data);
if (data.traceType !== 0) {
let index = state.task.findIndex((item) => item.traceId === data.traceId);
if (index !== -1) {
commit('delTask', index);
commit('receive', data);
console.info('[chat] msg', data);
} else {
console.info('[chat] deprecated', data);
}
}
};
socket.onopen = () => {
commit(
'setHeart',
setInterval(() => {
dispatch('heart');
}, 3000)
);
console.info('[chat] open');
resolve(socket);
};
socket.onclose = () => {
clearInterval(state.heart);
console.info('[chat] close');
};
socket.onerror = (e) => {
clearInterval(state.heart);
console.info('[chat] error', e);
reject(e);
};
commit('setSocket', socket);
} else {
ElMessage.error('当前浏览器不支持 WebSocket');
reject('not support websocket');
}
});
},
/**
* 发送心跳任务监测
*/
heart: ({ state, dispatch }) => {
dispatch('send', {
traceType: 26,
content: { storeId: 1 },
});
console.info('[chat] heart');
state.task.forEach((item) => {
dispatch('send', item);
});
},
/**
* 执行任务
*/
invoke: ({ commit, dispatch }, data) => {
data.traceId = UUID();
commit('addTask', data);
dispatch('send', data);
},
/**
* 发送数据
*/
send: ({ state }, data) => {
if (window.WebSocket) {
if (state.socket?.readyState === WebSocket.OPEN) {
data.traceId = data.traceId || UUID();
state.socket.send(JSON.stringify(data));
if (data.traceType !== 26) {
console.info('[chat] send', data);
}
}
}
},
/**
* 查询回话列表
*/
querySession: ({ dispatch }) => {
dispatch('invoke', {
traceType: 27,
content: { storeId: 1 },
});
},
};
export default {
state,
getters,
mutations,
actions,
};

@ -310,6 +310,15 @@ export const emojiData = [
], ],
}, },
]; ];
//生成UUID
export function UUID() {
let d = _.now();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + _.random(16)) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
}
//emoji表情编码 //emoji表情编码
export function utf16toEntities(str) { export function utf16toEntities(str) {
const patt = /[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则 const patt = /[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则

@ -1,5 +1,6 @@
<template> <template>
<div class="chat-container" @click="emojiVisible = false"> <div class="chat-container" @click="emojiVisible = false">
{{ $store.state.chat }}
<div class="header"> <div class="header">
<p>当前客服小爱</p> <p>当前客服小爱</p>
<el-button type="text" @click="summaryVisible = !summaryVisible"> <el-button type="text" @click="summaryVisible = !summaryVisible">
@ -25,16 +26,21 @@
:class="{ active: state.currentIndex === index }" :class="{ active: state.currentIndex === index }"
@click="handleChangeSession(index)" @click="handleChangeSession(index)"
> >
<el-badge class="session-count" :hidden="item.count === 0" :max="99" :value="item.count"> <el-badge
<el-avatar circle :src="item.avatar" /> class="session-count"
:hidden="item.unreadCount === 0"
:max="99"
:value="item.unreadCount"
>
<el-avatar circle :src="item.fromAvatar" />
</el-badge> </el-badge>
<div class="session-info"> <div class="session-info">
<div class="row"> <div class="row">
<div class="session-name">{{ item.name }}</div> <div class="session-name">{{ item.fromNickname }}</div>
<div class="session-time">{{ item.time }}</div> <div class="session-time">{{ parseTime(item.lastMessage.createTimeStamp) }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="session-content">{{ item.content }}</div> <div class="session-content">{{ parseContent(item.lastMessage.payload) }}</div>
</div> </div>
</div> </div>
</li> </li>
@ -44,7 +50,7 @@
<div class="content-header"> <div class="content-header">
<div class="content-header-left"> <div class="content-header-left">
<div class="name sex-1"> <div class="name sex-1">
{{ currentSession.name }} {{ currentSession?.name }}
</div> </div>
</div> </div>
<div class="content-header-right"> <div class="content-header-right">
@ -103,9 +109,11 @@
</template> </template>
<script setup> <script setup>
import { emojiData, entitiestoUtf16 } from '@/utils/emoji.js'; import { emojiData, entitiestoUtf16 } from '@/utils/chat.js';
import MessageItem from './message.vue'; import MessageItem from './message.vue';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const store = useStore();
store.dispatch('chat/querySession');
// //
const summaryVisible = ref(true); const summaryVisible = ref(true);
@ -121,32 +129,24 @@
currentIndex: 0, currentIndex: 0,
message: '', message: '',
}); });
const sessionList = reactive([ const sessionList = computed(() => {
{ return store.state.chat.sessionData?.sessionVOS || [];
name: '小可爱', });
avatar: 'https://placem.at/people',
count: 1,
time: '2019-12-12',
content: '你好,请问这个能有优惠吗?',
},
{
name: '小可爱',
avatar: 'https://placem.at/people',
count: 0,
time: '2019-12-12',
content: '你好,请问这个能有优惠吗?',
},
{
name: '小可爱',
avatar: 'https://placem.at/people',
count: 0,
time: '2019-12-12',
content: '你好,请问这个能有优惠吗?',
},
]);
const currentSession = computed(() => { const currentSession = computed(() => {
return sessionList[state.currentIndex]; return sessionList[state.currentIndex];
}); });
//
const parseTime = (timestamp) => dayjs(new Date(timestamp)).format('MM-DD HH:mm:ss');
const parseContent = (payload) => {
payload = JSON.parse(payload);
if ('text' in payload) {
payload = payload.text;
} else {
payload = '[未知消息]';
}
return payload;
};
const sessionMessageList = reactive([ const sessionMessageList = reactive([
{ {
type: 'text', type: 'text',

Loading…
Cancel
Save