添加聊天模块

This commit is contained in:
icssoa 2022-06-10 17:33:59 +08:00
parent 8218a4ba81
commit f70f4b5d73
16 changed files with 798 additions and 548 deletions

View File

@ -1 +1 @@
[["/admin/base/comm",[["post","/personUpdate",""],["get","/uploadMode",""],["get","/permmenu",""],["get","/person",""],["post","/upload",""],["post","/logout",""],["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/open",[["get","/refreshToken",""],["get","/captcha",""],["post","/login",""],["get","/html",""],["get","/eps",""],["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/sys/department",[["post","/delete",""],["post","/update",""],["post","/order",""],["post","/list",""],["post","/add",""],["","/page",""],["","/info",""]]],["/admin/base/sys/log",[["post","/setKeep",""],["get","/getKeep",""],["post","/clear",""],["post","/page",""],["","/list",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/sys/menu",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/base/sys/param",[["post","/delete",""],["post","/update",""],["get","/html",""],["get","/info",""],["post","/page",""],["post","/add",""],["","/list",""]]],["/admin/base/sys/role",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/base/sys/user",[["post","/delete",""],["post","/update",""],["post","/move",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/demo/goods",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/page",""],["post","/list",""],["post","/add",""]]],["/admin/space/info",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/space/type",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/task/info",[["post","/delete",""],["post","/update",""],["post","/start",""],["post","/once",""],["post","/stop",""],["get","/info",""],["post","/page",""],["get","/log",""],["post","/add",""],["","/list",""]]],["/test",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]]]
[["/admin/base/comm",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/personUpdate",""],["","/uploadMode",""],["","/permmenu",""],["","/logout",""]]],["/admin/base/open",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/refreshToken",""],["","/captcha",""],["","/login",""],["","/html",""],["","/eps",""]]],["/admin/base/sys/department",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/order",""]]],["/admin/base/sys/log",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/setKeep",""],["","/getKeep",""],["","/clear",""]]],["/admin/base/sys/menu",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/sys/param",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/html",""]]],["/admin/base/sys/role",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/sys/user",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/move",""]]],["/admin/demo/goods",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/space/info",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/space/type",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/task/info",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""],["","/start",""],["","/once",""],["","/stop",""],["","/log",""]]],["/chat/message",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/chat/session",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/test",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]]]

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@
"nprogress": "^0.2.0",
"pinia": "^2.0.12",
"quill": "^1.3.7",
"socket.io-client": "^4.5.1",
"store": "^2.0.12",
"unocss": "^0.31.0",
"vue": "^3.2.32",

View File

@ -86,6 +86,7 @@ export function useEps(service: Service) {
})
.catch((err) => {
console.error("[Eps] 获取失败!", err.message);
createDts([]);
});
}
}

View File

@ -50,7 +50,8 @@
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";
import { computed, defineComponent, reactive } from "vue";
import { useStore } from "../store";
export default defineComponent({
name: "cl-chat"
@ -60,17 +61,22 @@ export default defineComponent({
<script lang="ts" setup>
import { nextTick, provide, ref, watch } from "vue";
import { module } from "/@/cool/utils";
import { useCool } from "/@/cool";
import { useCool, config } from "/@/cool";
import { useBase } from "/$/base";
import ChatMessage from "./message.vue";
import ChatSession from "./session.vue";
import { Notebook, ArrowLeft, BellFilled } from "@element-plus/icons-vue";
import { debounce } from "lodash";
import io from "socket.io-client";
import { Socket } from "socket.io-client";
import ChatMessage from "./message.vue";
import ChatSession from "./session.vue";
const { service } = useCool();
const { mitt } = useCool();
//
const { app } = useBase();
const { session, message } = useStore();
//
const { app, user } = useBase();
//
const { options } = module.get("upload");
@ -81,21 +87,51 @@ const visible = ref(false);
//
const isExpand = ref(true);
//
const chat = reactive({
inputValue: "",
session: {
loading: false,
value: null,
list: []
},
message: {
loading: false,
list: [],
pagination: {}
},
//
scrollToBottom: debounce(() => {
// Socket
let socket: Socket;
//
function connect() {
return refresh();
if (!socket) {
socket = io(config.host + `/chat`, {
auth: {
token: user.token
}
});
socket.on("connect", () => {
console.log(`connect ${user.info?.nickName}`);
//
socket.on("message", (msg) => {
console.log(msg);
mitt("chat-message", msg);
});
refresh();
});
socket.on("disconnect", (err) => {
console.error(err);
});
}
}
//
function open() {
visible.value = true;
connect();
}
//
function close() {
visible.value = false;
}
//
const scrollToBottom = debounce(() => {
nextTick(() => {
const box = document.querySelector(".cl-chat .chat-message .list");
box?.scroll({
@ -103,40 +139,18 @@ const chat = reactive({
behavior: "smooth"
});
});
}, 300),
//
async getSession() {
this.session.loading = true;
await service.im.session.page().then((res) => {
chat.session.list = res.list;
}, 300);
//
if (!this.session.value) {
this.setSession(res.list[0]);
}
});
this.session.loading = false;
},
//
async setSession(data: any) {
//
this.message.list = [];
//
this.session.value = data;
//
await this.getMessage();
//
this.scrollToBottom();
},
//
async getMessage() {
this.message.loading = true;
await service.im.message.page().then((res) => {
chat.message.list = res.list;
chat.message.pagination = res.pagination;
});
this.message.loading = false;
}
//
async function refresh() {
await session.get();
await message.get();
scrollToBottom();
}
provide("chat", {
socket,
scrollToBottom
});
//
@ -150,18 +164,6 @@ watch(
}
);
//
function open() {
visible.value = true;
}
//
function close() {
visible.value = false;
}
provide("chat", chat);
defineExpose({
open,
close

View File

@ -1,20 +1,12 @@
<template>
<div
class="chat-message"
v-loading="chat?.message.loading"
element-loading-text="消息列表加载中"
>
<div class="chat-message" v-loading="message?.loading" element-loading-text="消息列表加载中">
<!-- 头部 -->
<div class="head">
<template v-if="chat?.session.value">
<template v-if="session?.value">
<div class="avatar">
<el-avatar
:size="30"
shape="square"
:src="chat?.session.value.avatar"
></el-avatar>
<el-avatar :size="30" shape="square" :src="session?.value.avatar"></el-avatar>
</div>
<span class="name">{{ chat?.session.value.nickName }}聊天中</span>
<span class="name">{{ session?.value.nickName }}聊天中</span>
<ul class="tools">
<li></li>
@ -61,7 +53,7 @@
<div class="input">
<el-input
v-model="chat.inputValue"
v-model="value"
type="textarea"
:rows="4"
resize="none"
@ -71,20 +63,26 @@
}"
placeholder="输入内容"
></el-input>
<el-button size="small" type="success" @click="send">发送</el-button>
<el-button size="small" type="success" @click="send" :disabled="!value"
>发送</el-button
>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue-demi";
import { computed, ref } from "vue";
import { useChat } from "../hooks";
import { useStore } from "../store";
const { chat } = useChat();
const { message, session } = useStore();
const value = ref("");
//
const list = computed(() => chat?.message.list);
const list = computed(() => message.list);
function send() {
chat?.scrollToBottom();

View File

@ -2,15 +2,21 @@
<div class="chat-session">
<div class="head">
<el-input v-model="keyWord" placeholder="关键字搜索" clearable></el-input>
<ul class="tools">
<li @click="session.get()">
<el-icon :size="16"><Refresh /></el-icon>
</li>
</ul>
</div>
<div class="list scroller1" v-loading="chat?.session.loading">
<div class="list scroller1" v-loading="session?.loading">
<div
class="item"
v-for="(item, index) in list"
:key="index"
:class="{
'is-active': item.id == chat?.session.value?.id
'is-active': item.id == session?.value?.id
}"
@click="toDetail(item)"
>
@ -39,29 +45,24 @@
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from "vue";
import { computed, ref } from "vue";
import { useChat } from "../hooks";
import { useCool } from "/@/cool";
import { useStore } from "../store";
import { Refresh } from "@element-plus/icons-vue";
const { service } = useCool();
const { chat } = useChat();
const { session } = useStore();
//
const keyWord = ref("");
//
const list = computed(
() => chat?.session.list.filter((e) => e.nickName.includes(keyWord.value)) || []
);
const list = computed(() => session?.list.filter((e) => e.nickName.includes(keyWord.value)) || []);
//
function toDetail(item: any) {
chat?.setSession(item);
}
onMounted(() => {
chat?.getSession();
});
</script>
<style lang="scss" scoped>
@ -80,6 +81,26 @@ onMounted(() => {
height: 30px;
background-color: #eee !important;
}
.tools {
display: inline-flex;
align-items: center;
li {
height: 30px;
width: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin-left: 10px;
border-radius: 3px;
&:hover {
background-color: #eee;
}
}
}
}
.list {

View File

@ -1,33 +1,5 @@
import { inject } from "vue";
declare interface Item {
id: string;
avatar: string;
nickName: string;
[key: string]: any;
}
declare interface Chat {
inputValue: string;
session: {
loading: boolean;
value: Item;
list: Item[];
};
message: {
loading: boolean;
list: Item[];
pagination: {
page: number;
total: number;
size: number;
};
};
scrollToBottom(): void;
getSession(params?: any): void;
setSession(data: any): void;
getMessage(params?: any): void;
}
import { Chat } from "../types";
export function useChat() {
const chat = inject<Chat>("chat");

View File

View File

@ -1,8 +1,8 @@
import { BaseService, Service } from "/@/cool";
import Mock from "mockjs";
@Service("im/message")
class ImMessage extends BaseService {
@Service("chat/message")
class ChatMessage extends BaseService {
page() {
return new Promise((resolve) => {
const data = Mock.mock({
@ -44,4 +44,4 @@ class ImMessage extends BaseService {
}
}
export default ImMessage;
export default ChatMessage;

View File

@ -1,8 +1,8 @@
import { BaseService, Service } from "/@/cool";
import Mock from "mockjs";
@Service("im/session")
class ImSession extends BaseService {
@Service("chat/session")
class ChatSession extends BaseService {
page() {
return new Promise((resolve) => {
const data = Mock.mock({
@ -40,4 +40,4 @@ class ImSession extends BaseService {
}
}
export default ImSession;
export default ChatSession;

View File

@ -0,0 +1,12 @@
import { useMessageStore } from "./message";
import { useSessionStore } from "./session";
export function useStore() {
const session = useSessionStore();
const message = useMessageStore();
return {
session,
message
};
}

View File

@ -0,0 +1,36 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { useCool } from "/@/cool";
export const useMessageStore = defineStore("chat-message", () => {
const { service } = useCool();
// 加载状态
const loading = ref(false);
// 列表
const list = ref<any[]>([]);
// 分页
const pagination = ref();
// 获取列表
async function get(params?: any) {
loading.value = true;
// 发送请求
await service.chat.message.page(params).then((res) => {
list.value = res.list;
pagination.value = res.pagination;
});
loading.value = false;
}
return {
loading,
list,
pagination,
get
};
});

View File

@ -0,0 +1,50 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { useCool } from "/@/cool";
export const useSessionStore = defineStore("chat-session", () => {
const { service } = useCool();
// 加载状态
const loading = ref(false);
// 列表
const list = ref<any[]>([]);
// 选中
const value = ref<any>();
// 获取列表
async function get(params?: any) {
loading.value = true;
// 发送请求
await service.chat.session.page(params).then((res) => {
// 默认加载第一个会话的消息
if (!value.value) {
set(res.list[0]);
}
// 设置列表
list.value = res.list;
});
loading.value = false;
}
// 设置值
function set(data: any) {
// 清空消息列表
list.value = [];
// 设置值
value.value = data;
}
return {
loading,
list,
value,
get,
set
};
});

31
src/modules/chat/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
import { Socket } from "socket.io-client";
export declare interface Item {
id: string;
avatar: string;
nickName: string;
[key: string]: any;
}
export declare interface Chat {
socket?: Socket;
inputValue: string;
session: {
loading: boolean;
value?: Item;
list: Item[];
};
message: {
loading: boolean;
list: Item[];
pagination: {
page: number;
total: number;
size: number;
};
};
scrollToBottom(): void;
getSession(params?: any): void;
setSession(data: any): void;
getMessage(params?: any): void;
}

View File

@ -1178,6 +1178,11 @@
resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
"@socket.io/component-emitter@~3.1.0":
version "3.1.0"
resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
@ -2750,7 +2755,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -3006,6 +3011,22 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
engine.io-client@~6.2.1:
version "6.2.2"
resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz#c6c5243167f5943dcd9c4abee1bfc634aa2cbdd0"
integrity sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
engine.io-parser "~5.0.3"
ws "~8.2.3"
xmlhttprequest-ssl "~2.0.0"
engine.io-parser@~5.0.3:
version "5.0.4"
resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
enhanced-resolve@^5.0.0, enhanced-resolve@^5.9.2:
version "5.9.2"
resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz#0224dcd6a43389ebfb2d55efee517e5466772dd9"
@ -5746,6 +5767,24 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
socket.io-client@^4.5.1:
version "4.5.1"
resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz#cab8da71976a300d3090414e28c2203a47884d84"
integrity sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
engine.io-client "~6.2.1"
socket.io-parser "~4.2.0"
socket.io-parser@~4.2.0:
version "4.2.0"
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz#3f01e5bc525d94aa52a97ed5cbc12e229bbc4d6b"
integrity sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
sortablejs@1.14.0:
version "1.14.0"
resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
@ -6616,6 +6655,11 @@ wrappy@1:
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@~8.2.3:
version "8.2.3"
resolved "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
xlsx@^0.16.9:
version "0.16.9"
resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.9.tgz#dacd5bb46bda6dd3743940c9c3dc1e2171826256"
@ -6632,6 +6676,11 @@ xlsx@^0.16.9:
wmf "~1.0.1"
word "~0.3.0"
xmlhttprequest-ssl@~2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"