im页面完成
This commit is contained in:
parent
4dfbb67c65
commit
8c4c26f554
@ -13,8 +13,8 @@ TypeScript cannot handle type information for `.vue` imports by default, so we r
|
|||||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||||
|
|
||||||
1. Disable the built-in TypeScript Extension
|
1. Disable the built-in TypeScript Extension
|
||||||
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||||
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||||
|
|
||||||
## Customize configuration
|
## Customize configuration
|
||||||
@ -44,5 +44,3 @@ npm run build
|
|||||||
```sh
|
```sh
|
||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
zmj 分支
|
|
||||||
|
22
src/api/talk.ts
Normal file
22
src/api/talk.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import request from "@/utils/request/indexs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定scoket
|
||||||
|
*/
|
||||||
|
export const bindScoket = (data: any) =>
|
||||||
|
request.post({ url: "/common/im/doBindUid", data });
|
||||||
|
// 发送消息
|
||||||
|
export const sendMsgApi = (data: any) =>
|
||||||
|
request.post({ url: "/common/im/sendTextMsg", data });
|
||||||
|
// 发送文件
|
||||||
|
export const sendFileApi = (data: any) =>
|
||||||
|
request.post({ url: "/common/im/sendFileMsg", data });
|
||||||
|
// 消息列表
|
||||||
|
export const getMsgListApi = (data: any) =>
|
||||||
|
request.post({ url: "/common/im/msgList", data });
|
||||||
|
// 片区经理id
|
||||||
|
export const getAreaManagerApi = (data: any) =>
|
||||||
|
request.post({ url: "/common/im/getAreaManager", data });
|
||||||
|
// 联系人列表
|
||||||
|
export const getContactListApi = (data: any) =>
|
||||||
|
request.post({ url: "/common/im/contactList", data });
|
BIN
src/assets/images/SP.png
Normal file
BIN
src/assets/images/SP.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/images/XC.png
Normal file
BIN
src/assets/images/XC.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
@ -2,11 +2,19 @@ const config = {
|
|||||||
terminal: 1, //终端
|
terminal: 1, //终端
|
||||||
title: "后台管理系统", //网站默认标题
|
title: "后台管理系统", //网站默认标题
|
||||||
version: "1.6.0", //版本号
|
version: "1.6.0", //版本号
|
||||||
baseUrl: `${
|
baseUrl: `${import.meta.env.VITE_APP_BASE_URL}/`, //请求接口域名
|
||||||
import.meta.env.VITE_APP_BASE_URL
|
|
||||||
}/`, //请求接口域名
|
|
||||||
urlPrefix: "adminapi", //请求默认前缀
|
urlPrefix: "adminapi", //请求默认前缀
|
||||||
timeout: 20 * 1000, //请求超时时长
|
timeout: 20 * 1000, //请求超时时长
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
// const config = {
|
||||||
|
// terminal: 1, //终端
|
||||||
|
// title: "后台管理系统", //网站默认标题
|
||||||
|
// version: "1.6.0", //版本号
|
||||||
|
// baseUrl: `https://worker-task.lihaink.cn`, //请求接口域名
|
||||||
|
// urlPrefix: "adminapi", //请求默认前缀
|
||||||
|
// timeout: 20 * 1000, //请求超时时长
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default config;
|
||||||
|
131
src/utils/request/indexs.ts
Normal file
131
src/utils/request/indexs.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { merge } from "lodash";
|
||||||
|
import configs from "@/config";
|
||||||
|
import { Axios } from "./axios";
|
||||||
|
import {
|
||||||
|
ContentTypeEnum,
|
||||||
|
RequestCodeEnum,
|
||||||
|
RequestMethodsEnum,
|
||||||
|
} from "@/enums/requestEnums";
|
||||||
|
import type { AxiosHooks } from "./type";
|
||||||
|
import { clearAuthInfo, getToken } from "../auth";
|
||||||
|
import feedback from "../feedback";
|
||||||
|
import NProgress from "nprogress";
|
||||||
|
import { AxiosError, type AxiosRequestConfig } from "axios";
|
||||||
|
import router from "@/router";
|
||||||
|
import { PageEnum } from "@/enums/pageEnum";
|
||||||
|
|
||||||
|
// 处理axios的钩子函数
|
||||||
|
const axiosHooks: AxiosHooks = {
|
||||||
|
requestInterceptorsHook(config) {
|
||||||
|
NProgress.start();
|
||||||
|
const { withToken, isParamsToData } = config.requestOptions;
|
||||||
|
const params = config.params || {};
|
||||||
|
const headers = config.headers || {};
|
||||||
|
|
||||||
|
// 添加token
|
||||||
|
if (withToken) {
|
||||||
|
const token = getToken();
|
||||||
|
headers.token = token;
|
||||||
|
}
|
||||||
|
// POST请求下如果无data,则将params视为data
|
||||||
|
if (
|
||||||
|
isParamsToData &&
|
||||||
|
!Reflect.has(config, "data") &&
|
||||||
|
config.method?.toUpperCase() === RequestMethodsEnum.POST
|
||||||
|
) {
|
||||||
|
config.data = params;
|
||||||
|
config.params = {};
|
||||||
|
}
|
||||||
|
config.headers = headers;
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
requestInterceptorsCatchHook(err) {
|
||||||
|
NProgress.done();
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
async responseInterceptorsHook(response) {
|
||||||
|
NProgress.done();
|
||||||
|
const { isTransformResponse, isReturnDefaultResponse } =
|
||||||
|
response.config.requestOptions;
|
||||||
|
|
||||||
|
//返回默认响应,当需要获取响应头及其他数据时可使用
|
||||||
|
if (isReturnDefaultResponse) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
// 是否需要对数据进行处理
|
||||||
|
if (!isTransformResponse) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
const { code, data, show, msg } = response.data;
|
||||||
|
switch (code) {
|
||||||
|
case RequestCodeEnum.SUCCESS:
|
||||||
|
if (show) {
|
||||||
|
msg && feedback.msgSuccess(msg);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
case RequestCodeEnum.FAIL:
|
||||||
|
if (show) {
|
||||||
|
msg && feedback.msgError(msg);
|
||||||
|
}
|
||||||
|
return Promise.reject(data);
|
||||||
|
case RequestCodeEnum.LOGIN_FAILURE:
|
||||||
|
clearAuthInfo();
|
||||||
|
router.push(PageEnum.LOGIN);
|
||||||
|
return Promise.reject();
|
||||||
|
case RequestCodeEnum.OPEN_NEW_PAGE:
|
||||||
|
window.location.href = data.url;
|
||||||
|
return data;
|
||||||
|
default:
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responseInterceptorsCatchHook(error) {
|
||||||
|
NProgress.done();
|
||||||
|
if (error.code !== AxiosError.ERR_CANCELED) {
|
||||||
|
error.message && feedback.msgError(error.message);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultOptions: AxiosRequestConfig = {
|
||||||
|
//接口超时时间
|
||||||
|
timeout: configs.timeout,
|
||||||
|
// 基础接口地
|
||||||
|
baseURL: configs.baseUrl,
|
||||||
|
|
||||||
|
// baseURL: "http://192.168.1.11:8081/",
|
||||||
|
|
||||||
|
//请求头
|
||||||
|
headers: { "Content-Type": ContentTypeEnum.JSON, version: configs.version },
|
||||||
|
// 处理 axios的钩子函数
|
||||||
|
axiosHooks: axiosHooks,
|
||||||
|
// 每个接口可以单独配置
|
||||||
|
requestOptions: {
|
||||||
|
// 是否将params视为data参数,仅限post请求
|
||||||
|
isParamsToData: true,
|
||||||
|
//是否返回默认的响应
|
||||||
|
isReturnDefaultResponse: false,
|
||||||
|
// 需要对返回数据进行处理
|
||||||
|
isTransformResponse: true,
|
||||||
|
// 接口拼接地址
|
||||||
|
urlPrefix: "",
|
||||||
|
// 忽略重复请求
|
||||||
|
ignoreCancelToken: false,
|
||||||
|
// 是否携带token
|
||||||
|
withToken: true,
|
||||||
|
// 开启请求超时重新发起请求请求机制
|
||||||
|
isOpenRetry: true,
|
||||||
|
// 重新请求次数
|
||||||
|
retryCount: 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function createAxios(opt?: Partial<AxiosRequestConfig>) {
|
||||||
|
return new Axios(
|
||||||
|
// 深度合并
|
||||||
|
merge(defaultOptions, opt || {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const request = createAxios();
|
||||||
|
export default request;
|
686
src/views/business/index.vue
Normal file
686
src/views/business/index.vue
Normal file
@ -0,0 +1,686 @@
|
|||||||
|
<template>
|
||||||
|
<div class="box">
|
||||||
|
<div class="talk-list">
|
||||||
|
<div class="mt-4 serch">
|
||||||
|
<el-input v-model="queryUserName" placeholder="搜索联系人" class="input-with-select">
|
||||||
|
<template #append>
|
||||||
|
<el-button :icon="Search" @click="serchUserFn" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
<div style="height: 5vh;"></div>
|
||||||
|
|
||||||
|
<div class="contacts" v-for="(item, index) in userList" :key="index" @click="choseTalkFn(item, index)">
|
||||||
|
<div class="l">
|
||||||
|
<div style="position: relative;">
|
||||||
|
<el-image style="width: 40px; height: 40px" :src="item.avatar" />
|
||||||
|
<div class="brage" v-if="item.no_read_num">{{ item.no_read_num }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="tit-a">
|
||||||
|
<div>{{ item.name }}</div>
|
||||||
|
<div v-if="item.last_msg_type == 'text'" class="value">{{ item.last_msg_content }}</div>
|
||||||
|
<div v-if="item.last_msg_type == 'image'" class="value">[图片]</div>
|
||||||
|
<div v-if="item.last_msg_type == 'video'" class="value">[视频]</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="r">
|
||||||
|
{{ timeFn(item.last_msg_time) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="talk-detail" id="content">
|
||||||
|
<div class="top">
|
||||||
|
{{ talkDetail.name }}
|
||||||
|
</div>
|
||||||
|
<div style="height: 4vh"></div>
|
||||||
|
<div class="center" id="center" @scroll="handleScroll">
|
||||||
|
<div class="talk" v-for="(item, index) in talkDetail.talkList" :key="index">
|
||||||
|
|
||||||
|
<div style="text-align: center;" v-show="index % 5 == 0">{{ timeFn(item.create_time) }}</div>
|
||||||
|
<!-- 我的消息 -->
|
||||||
|
<div class="my_task" v-if="item.from_user_id == user_id">
|
||||||
|
<div class="content">
|
||||||
|
<!-- 文本 -->
|
||||||
|
<div v-if="item.type == 'text'">
|
||||||
|
<a :href="item.content" v-if="isLink(item.content)" target="_blank"
|
||||||
|
style="color: #576B95;">{{
|
||||||
|
item.content }}
|
||||||
|
</a>
|
||||||
|
<div v-else> {{ item.content }}</div>
|
||||||
|
</div>
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div v-if="item.type == 'image'">
|
||||||
|
<el-image style="width: 100px;height: auto;" :src="item.content" :preview-src-list="srcList"
|
||||||
|
@click="PreviewImgFn(item)"></el-image>
|
||||||
|
</div>
|
||||||
|
<!-- 视频 -->
|
||||||
|
<div v-if="item.type == 'video'">
|
||||||
|
<video controls :src="item.content"></video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-image style="width: 40px; height: 40px" :src="userStore.userInfo.avatar" />
|
||||||
|
</div>
|
||||||
|
<!-- 对方消息 -->
|
||||||
|
<div class="to_task" v-else>
|
||||||
|
<el-image style="width: 40px; height: 40px;margin-right: 5px;" :src="item.from_user_avatar" />
|
||||||
|
<div class="content">
|
||||||
|
<!-- 文本 -->
|
||||||
|
<div v-if="item.type == 'text'">
|
||||||
|
<a :href="item.content" target="_blank" v-if="isLink(item.content)"
|
||||||
|
style="color: #576B95;">{{
|
||||||
|
item.content }}
|
||||||
|
</a>
|
||||||
|
<div v-else> {{ item.content }}</div>
|
||||||
|
</div>
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div v-if="item.type == 'image'">
|
||||||
|
<el-image style="width: 100px;height: auto;" :src="item.content" :preview-src-list="srcList"
|
||||||
|
@click="PreviewImgFn(item)"></el-image>
|
||||||
|
</div>
|
||||||
|
<!-- 视频 -->
|
||||||
|
<div v-if="item.type == 'video'">
|
||||||
|
<video controls :src="item.content"></video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- 本地 -->
|
||||||
|
<div v-show="item.status" class="my_task" v-for="item, index in local">
|
||||||
|
<!-- <div></div> -->
|
||||||
|
<!-- <div >上传中</div> -->
|
||||||
|
<div class="content" v-loading="true">
|
||||||
|
<div style="width: 120px;margin-right: 10px;">
|
||||||
|
<video controls :src="item.localSrc" v-if="item.type == 'video'"></video>
|
||||||
|
<img controls :src="item.localSrc" v-else />
|
||||||
|
</div>
|
||||||
|
<!-- <el-progress :text-inside="true" :stroke-width="14" :percentage="item.percentage" /> -->
|
||||||
|
</div>
|
||||||
|
<el-image style="width: 40px; height: 40px;margin-right: 10px;" :src="url" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div style="display: flex;">
|
||||||
|
<el-upload :action="base_url + '/common/im/sendFileMsg'" :data="{
|
||||||
|
from_user_id: user_id,
|
||||||
|
to_user_id: talkDetail.to_user_id,
|
||||||
|
scene: 1,
|
||||||
|
type: 'image',
|
||||||
|
msg_id: msg_id
|
||||||
|
|
||||||
|
}" multiple :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="changeFile">
|
||||||
|
<img class="img_cls" src="@/assets/images/XC.png" style="width: 50px;height: 50px;" />
|
||||||
|
</el-upload>
|
||||||
|
<el-upload :action="base_url + '/common/im/sendFileMsg'" :data="{
|
||||||
|
from_user_id: user_id,
|
||||||
|
to_user_id: talkDetail.to_user_id,
|
||||||
|
scene: 1,
|
||||||
|
type: 'video',
|
||||||
|
msg_id: msg_id
|
||||||
|
}" multiple :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="changeFile1">
|
||||||
|
|
||||||
|
<img style="width: 50px; height: 50px" src="@/assets/images/SP.png" />
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<el-input v-model="textarea" style="border: none;outline: none;" placeholder="请输入..."
|
||||||
|
@keydown="keyup_submit(event)" />
|
||||||
|
<div class="send-btn" @click="sendMsgFn">发送</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div style="height: 30vh;"></div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" scoped setup>
|
||||||
|
import { reactive, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { Search } from '@element-plus/icons-vue'
|
||||||
|
import feedback from '@/utils/feedback'
|
||||||
|
import {
|
||||||
|
sendMsgApi, getMsgListApi, bindScoket, sendFileApi, getContactListApi
|
||||||
|
} from "@/api/talk"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import useUserStore from "@/stores/modules/user";
|
||||||
|
import { inject } from "vue";
|
||||||
|
const base_url: any = inject("base_url").replace('adminapi', '');
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const user_id = userStore.userInfo.id
|
||||||
|
let page_no = 1
|
||||||
|
let to_id: any = null
|
||||||
|
// 图片预览
|
||||||
|
const srcList: any = reactive(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const PreviewImgFn = (item: any) => {
|
||||||
|
srcList[0] = item.content
|
||||||
|
}
|
||||||
|
// 上拉加载
|
||||||
|
const handleScroll = (e) => {
|
||||||
|
let top = e.target.scrollTop
|
||||||
|
if (top <= 0) {
|
||||||
|
page_no++
|
||||||
|
getMsgListApi({
|
||||||
|
from_user_id: user_id,
|
||||||
|
to_user_id: to_id,
|
||||||
|
scene: 1,
|
||||||
|
page_no: page_no,
|
||||||
|
page_size: 10
|
||||||
|
}).then(res2 => {
|
||||||
|
if (res2.length <= 0) return
|
||||||
|
talkDetail.talkList = res2.reverse().concat(talkDetail.talkList)
|
||||||
|
e.target.scrollTop = 100
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bind = false
|
||||||
|
// 获取用户列表
|
||||||
|
const userList = reactive([])
|
||||||
|
let queryList = reactive([])
|
||||||
|
const talkDetail = reactive({
|
||||||
|
name: "",
|
||||||
|
to_user_id: "",
|
||||||
|
talkList: [],
|
||||||
|
index: 0,
|
||||||
|
})
|
||||||
|
getContactListApi({ user_id: user_id }).then(res => {
|
||||||
|
res.forEach((element: any) => {
|
||||||
|
userList.push(element)
|
||||||
|
queryList.push(element)
|
||||||
|
});
|
||||||
|
// queryList = userList
|
||||||
|
console.log(queryList, 6666)
|
||||||
|
talkDetail.name = userList[0].name
|
||||||
|
talkDetail.to_user_id = userList[0].id
|
||||||
|
to_id = userList[0].id
|
||||||
|
getMsgListApi({
|
||||||
|
from_user_id: user_id,
|
||||||
|
to_user_id: userList[0].id,
|
||||||
|
scene: 1,
|
||||||
|
page_no: 1,
|
||||||
|
page_size: 10
|
||||||
|
}).then(res2 => {
|
||||||
|
talkDetail.talkList = res2.reverse()
|
||||||
|
// console.log(talkDetail)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const url = 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
|
||||||
|
// 用户搜索
|
||||||
|
const queryUserName = ref('')
|
||||||
|
const serchUserFn = () => {
|
||||||
|
getContactListApi({ user_id: user_id, keywords: queryUserName.value }).then(res => {
|
||||||
|
userList.splice(0, userList.length);
|
||||||
|
res.forEach((element: any) => {
|
||||||
|
// @ts-ignore
|
||||||
|
userList.push(element)
|
||||||
|
// queryList.push(element)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 发送文本消息
|
||||||
|
const isLink = (text: string) => {
|
||||||
|
var pattern = /^(https?:\/\/|www\.|.*\.com).*$/i;
|
||||||
|
return pattern.test(text);
|
||||||
|
}
|
||||||
|
const textarea = ref("")
|
||||||
|
const sendMsgFn = () => {
|
||||||
|
if (!textarea.value) return
|
||||||
|
sendMsgApi({
|
||||||
|
from_user_id: user_id,
|
||||||
|
to_user_id: talkDetail.to_user_id,
|
||||||
|
type: "text",
|
||||||
|
content: textarea.value,
|
||||||
|
scene: 1,
|
||||||
|
msg_id: msg_id.value
|
||||||
|
}).then(res => {
|
||||||
|
talkDetail.talkList.push(res)
|
||||||
|
userList[talkDetail.index].last_msg_content = textarea.value
|
||||||
|
userList[talkDetail.index].last_msg_time = res.create_time
|
||||||
|
userList[talkDetail.index].last_msg_type = 'text'
|
||||||
|
// userList[talkDetail.index].last_msg_content = textarea.value
|
||||||
|
scrollFn()
|
||||||
|
textarea.value = ""
|
||||||
|
generateRandId()
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const keyup_submit = (evt: any) => {
|
||||||
|
var evt = window.event || evt;
|
||||||
|
if (evt.keyCode == 13) {
|
||||||
|
sendMsgFn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 发送媒体消息
|
||||||
|
let msg_id: any = ref('')
|
||||||
|
const generateRandId = () => {
|
||||||
|
var d = new Date().getTime();
|
||||||
|
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
||||||
|
var r = (d + Math.random() * 16) % 16 | 0;
|
||||||
|
d = Math.floor(d / 16);
|
||||||
|
return (c == "x" ? r : r & 0x3 | 0x8).toString(16);
|
||||||
|
});
|
||||||
|
// return uuid;
|
||||||
|
msg_id.value = uuid
|
||||||
|
}
|
||||||
|
generateRandId()
|
||||||
|
const local = reactive([])
|
||||||
|
let localData = () => {
|
||||||
|
return {
|
||||||
|
status: false,
|
||||||
|
localType: null,
|
||||||
|
localSrc: null,
|
||||||
|
type: "",
|
||||||
|
percentage: 0,
|
||||||
|
msg_id: msg_id.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const changeFile = (res) => {
|
||||||
|
console.log(res.type)
|
||||||
|
|
||||||
|
if (!res.type.includes('image')) {
|
||||||
|
ElMessage.error('请选择正确的图片格式!!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
local.push(localData())
|
||||||
|
let type = 'image'
|
||||||
|
var localIndex = local.findIndex(function (element) {
|
||||||
|
return element.msg_id === msg_id.value;
|
||||||
|
});
|
||||||
|
// userList[talkDetail.index].last_msg_type = type
|
||||||
|
let file = res
|
||||||
|
local[localIndex].type = type
|
||||||
|
const blob = file;
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
//监听文件读取结束后事件
|
||||||
|
reader.onloadend = function (e) {
|
||||||
|
local[localIndex].status = true
|
||||||
|
local[localIndex].localSrc = e.target?.result
|
||||||
|
scrollFn()
|
||||||
|
generateRandId()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const changeFile1 = (res) => {
|
||||||
|
if (!res.type.includes('video')) {
|
||||||
|
ElMessage.error('请选择正确的视频格式!!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
local.push(localData())
|
||||||
|
let type = 'video'
|
||||||
|
var localIndex = local.findIndex(function (element) {
|
||||||
|
return element.msg_id === msg_id.value;
|
||||||
|
});
|
||||||
|
// userList[talkDetail.index].last_msg_type = type
|
||||||
|
let file = res
|
||||||
|
local[localIndex].type = type
|
||||||
|
const blob = file;
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
//监听文件读取结束后事件
|
||||||
|
reader.onloadend = function (e) {
|
||||||
|
local[localIndex].status = true
|
||||||
|
local[localIndex].localSrc = e.target?.result
|
||||||
|
scrollFn()
|
||||||
|
generateRandId()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const handleAvatarSuccess: any = (
|
||||||
|
res: any
|
||||||
|
) => {
|
||||||
|
if (res.show == 0) {
|
||||||
|
talkDetail.talkList.push(res.data)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
feedback.msgError(res.msg)
|
||||||
|
}
|
||||||
|
var localIndex = local.findIndex(function (element) {
|
||||||
|
return element.msg_id === res.data.msg_id;
|
||||||
|
});
|
||||||
|
local[localIndex].status = false
|
||||||
|
userList[talkDetail.index].last_msg_type = res.data.type
|
||||||
|
console.log(res.data)
|
||||||
|
|
||||||
|
}
|
||||||
|
// 选择聊天
|
||||||
|
const choseTalkFn = (item: any, index: number) => {
|
||||||
|
userList[index].no_read_num = 0
|
||||||
|
talkDetail.name = item.name
|
||||||
|
talkDetail.to_user_id = item.id
|
||||||
|
talkDetail.index = index
|
||||||
|
to_id = item.id
|
||||||
|
getMsgListApi({
|
||||||
|
from_user_id: user_id,
|
||||||
|
to_user_id: item.id,
|
||||||
|
scene: 1,
|
||||||
|
page_no: 1,
|
||||||
|
page_size: 10
|
||||||
|
}).then(res2 => {
|
||||||
|
talkDetail.talkList = res2.reverse()
|
||||||
|
scrollFn()
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 滚动底部
|
||||||
|
let scrollTop: any = null
|
||||||
|
onMounted(() => {
|
||||||
|
scrollTop = document.getElementById('center')
|
||||||
|
|
||||||
|
})
|
||||||
|
const scrollFn = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollTop.scrollTop = 9999999
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
// 时间处理
|
||||||
|
const timeFn = (time) => {
|
||||||
|
const currentDate = new Date(); // 当前日期对象
|
||||||
|
const targetDate = new Date(time * 1000);
|
||||||
|
if (isSameDay(currentDate, targetDate)) {
|
||||||
|
const hours = targetDate.getHours(); // 小时
|
||||||
|
const minutes = targetDate.getMinutes(); // 分钟
|
||||||
|
return hours + ':' + (minutes < 10 ? '0' + minutes : minutes);
|
||||||
|
|
||||||
|
} else if (isYesterday(currentDate, targetDate)) {
|
||||||
|
return '昨天';
|
||||||
|
}
|
||||||
|
const month = targetDate.getMonth() + 1; // 月份(注意月份从0开始,需要加1)
|
||||||
|
const day = targetDate.getDate(); // 日期
|
||||||
|
return month + '月' + day + '日';
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
const isSameDay = (date1, date2) => {
|
||||||
|
return (
|
||||||
|
date1.getFullYear() === date2.getFullYear() &&
|
||||||
|
date1.getMonth() === date2.getMonth() &&
|
||||||
|
date1.getDate() === date2.getDate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const isYesterday = (currentDate, targetDate) => {
|
||||||
|
const yesterday = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - 1);
|
||||||
|
return isSameDay(yesterday, targetDate);
|
||||||
|
}
|
||||||
|
// scoket
|
||||||
|
const socket = new WebSocket('wss://worker-task.lihaink.cn/wss');
|
||||||
|
const onSocketOpen = (event: any) => {
|
||||||
|
console.log('WebSocket连接已打开');
|
||||||
|
}
|
||||||
|
const onSocketMessage = (event: any) => {
|
||||||
|
let data = JSON.parse(event.data)
|
||||||
|
if (!bind) {
|
||||||
|
let client_id = (JSON.parse(event.data).client_id)
|
||||||
|
bindScoket({
|
||||||
|
client_id: client_id,
|
||||||
|
user_id: user_id,
|
||||||
|
scene: 1,
|
||||||
|
}).then(res => {
|
||||||
|
bind = true
|
||||||
|
console.log('绑定成功')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (data.id) {
|
||||||
|
var index = userList.findIndex(function (element) {
|
||||||
|
return element.id == data.from_user_id;
|
||||||
|
});
|
||||||
|
// talkDetail.talkList.push(data)
|
||||||
|
if (data.from_user_id == talkDetail.to_user_id) {
|
||||||
|
console.log(data, "收到scoket消息")
|
||||||
|
talkDetail.talkList.push(data)
|
||||||
|
userList[index].last_msg_content = data.content
|
||||||
|
userList[index].last_msg_time = data.create_time
|
||||||
|
userList[index].last_msg_type = data.type
|
||||||
|
|
||||||
|
scrollFn()
|
||||||
|
} else {
|
||||||
|
userList[index].no_read_num++
|
||||||
|
userList[index].last_msg_content = data.content
|
||||||
|
userList[index].last_msg_time = data.create_time
|
||||||
|
userList[index].last_msg_type = data.type
|
||||||
|
|
||||||
|
// userList.forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
const onSocketClose = (event: any) => {
|
||||||
|
console.log('WebSocket连接已关闭');
|
||||||
|
}
|
||||||
|
const onSocketError = (event: any) => {
|
||||||
|
console.error('WebSocket连接发生错误');
|
||||||
|
}
|
||||||
|
socket.addEventListener('open', onSocketOpen);
|
||||||
|
socket.addEventListener('message', onSocketMessage);
|
||||||
|
socket.addEventListener('close', onSocketClose);
|
||||||
|
socket.addEventListener('error', onSocketError);
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
socket.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
// border-bottom: 1px solid black;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.serch {
|
||||||
|
background-color: #F6F6F6;
|
||||||
|
position: fixed;
|
||||||
|
top: 9vh;
|
||||||
|
width: 17vw;
|
||||||
|
z-index: 99;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.talk-list {
|
||||||
|
width: 20vw;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 85vh;
|
||||||
|
position: relative;
|
||||||
|
// background-color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.contacts {
|
||||||
|
// margin-bottom: 10px;
|
||||||
|
// background-color: red;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.l {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.tit-a {
|
||||||
|
margin-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 8vw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts:active {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.talk-list::-webkit-scrollbar {
|
||||||
|
width: 2px;
|
||||||
|
/* 滚动条宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.talk-list::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #888;
|
||||||
|
/* 滚动条颜色 */
|
||||||
|
border-radius: 1px;
|
||||||
|
/* 滚动条圆角 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.img_cls {
|
||||||
|
|
||||||
|
margin-right: 10PX;
|
||||||
|
margin-bottom: 10PX;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.talk-list:hover {
|
||||||
|
// background-color: #888;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.talk-detail {
|
||||||
|
width: 80vw;
|
||||||
|
height: 85vh;
|
||||||
|
|
||||||
|
.top {
|
||||||
|
position: fixed;
|
||||||
|
height: 4vh;
|
||||||
|
width: 70vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 9;
|
||||||
|
border-bottom: 1px solid #EAE8E7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
height: 65vh;
|
||||||
|
|
||||||
|
overflow-y: scroll;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
position: fixed;
|
||||||
|
background-color: white;
|
||||||
|
height: 20vh;
|
||||||
|
border-top: 1px solid #EAE8E7;
|
||||||
|
width: 70vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
// padding-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// overflow: scroll;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.center::-webkit-scrollbar {
|
||||||
|
width: 2px;
|
||||||
|
/* 滚动条宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.center::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #888;
|
||||||
|
/* 滚动条颜色 */
|
||||||
|
border-radius: 5px;
|
||||||
|
/* 滚动条圆角 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.talk {
|
||||||
|
// height: 4vh;
|
||||||
|
// background-color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn {
|
||||||
|
width: 50px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: green;
|
||||||
|
color: white;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
float: right;
|
||||||
|
margin-top: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-btn:active {
|
||||||
|
background-color: yellowgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my_task {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background-color: #95EC69;
|
||||||
|
border-radius: 5px;
|
||||||
|
// height: 30px;
|
||||||
|
padding: 5px;
|
||||||
|
// height: 40px;
|
||||||
|
// min-height: 40px;
|
||||||
|
margin-right: 5px;
|
||||||
|
line-height: 30px;
|
||||||
|
max-width: 20vw;
|
||||||
|
// overflow: hidden;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.to_task {
|
||||||
|
display: flex;
|
||||||
|
// justify-content: flex-end;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background-color: #95EC69;
|
||||||
|
border-radius: 5px;
|
||||||
|
// height: 30px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
// height: 40px;
|
||||||
|
// min-height: 40px;
|
||||||
|
margin-right: 5px;
|
||||||
|
line-height: 30px;
|
||||||
|
max-width: 20vw;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brage {
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 12px;
|
||||||
|
top: -8px;
|
||||||
|
right: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user