模块 chat 添加图片,视频上传

This commit is contained in:
icssoa 2021-03-22 21:54:10 +08:00
parent 9edba6ebed
commit 58907611f6
12 changed files with 224 additions and 61 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "cool-admin-vue", "name": "cool-admin-vue",
"version": "3.1.3", "version": "3.1.4",
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",

View File

@ -13,7 +13,7 @@ export const host = "https://show.cool-admin.com";
// Socket // Socket
export const socketUrl = (isDev ? `${host}` : "") + "/socket"; export const socketUrl = (isDev ? `${host}` : "") + "/socket";
// 请求地址 // 请求地址,本地会使用代理请求
export const baseUrl = (function() { export const baseUrl = (function() {
let proxy = getUrlParam("proxy"); let proxy = getUrlParam("proxy");

View File

@ -75,6 +75,7 @@ export default {
data() { data() {
return { return {
modes: ["text", "image", "emoji", "voice", "video"], //
visible: false, visible: false,
socket: null socket: null
}; };
@ -95,7 +96,7 @@ export default {
}, },
created() { created() {
// this.socket = io(`${socketUrl}/?isAdmin=true&token=${token}`); // this.socket = io(`${socketUrl}?isAdmin=true&token=${token}`);
// this.socket.on("connect", () => { // this.socket.on("connect", () => {
// console.log("socket connect"); // console.log("socket connect");
// }); // });
@ -149,7 +150,7 @@ export default {
}); });
// //
eventBus.$emit("message-append", { this.$store.commit("APPEND_MESSAGE_LIST", {
contentType, contentType,
content: JSON.parse(content), content: JSON.parse(content),
type: 1 type: 1
@ -177,7 +178,7 @@ export default {
}); });
} else { } else {
// //
eventBus.$emit("session-refresh"); eventBus.$emit("session.refresh");
} }
} catch (e) { } catch (e) {
console.error("消息格式异常", e); console.error("消息格式异常", e);

View File

@ -8,13 +8,27 @@
<emoji @select="onEmojiSelect" /> <emoji @select="onEmojiSelect" />
</li> </li>
<!-- 图片上传 --> <!-- 图片上传 -->
<li hidden> <li>
<cl-upload accept="image/*" list-type :on-success="onImageSelect"> <cl-upload
accept="image/*"
list-type
:before-upload="
f => {
onBeforeUpload(f, 'image');
}
"
:on-progress="onUploadProgress"
:on-success="
(r, f) => {
onUploadSuccess(r, f, 'image');
}
"
>
<img src="../static/images/image.png" alt="" /> <img src="../static/images/image.png" alt="" />
</cl-upload> </cl-upload>
</li> </li>
<!-- 视频上传 --> <!-- 视频上传 -->
<li hidden> <li>
<cl-upload <cl-upload
accept="video/*" accept="video/*"
list-type list-type
@ -57,7 +71,6 @@
<script> <script>
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
import Emoji from "./emoji"; import Emoji from "./emoji";
import eventBus from "../utils/event-bus";
export default { export default {
components: { components: {
@ -76,29 +89,48 @@ export default {
}, },
computed: { computed: {
...mapGetters(["session"]) ...mapGetters(["session", "messageList"])
}, },
methods: { methods: {
// //
onBeforeUpload(file, key) { onBeforeUpload(file, key) {
const data = { const next = (options = {}) => {
content: { const data = {
[`${key}Url`]: "" content: {
}, [`${key}Url`]: ""
type: 0, },
contentType: MODES.indexOf(key), type: 0,
uid: file.uid, uid: file.uid,
loading: true, loading: true,
progress: "0%" progress: "0%",
contentType: this.chat.modes.indexOf(key),
...options
};
this.append(data);
}; };
this.append(data); if (key == "image") {
const fileReader = new FileReader();
fileReader.onload = e => {
next({
content: {
imageUrl: e.target.result
}
});
};
fileReader.readAsDataURL(file);
} else {
next();
}
}, },
// //
onUploadProgress(e, file) { onUploadProgress(e, file) {
const item = this.message.list.find(e => e.uid == file.uid); const item = this.messageList.find(e => e.uid == file.uid);
if (item) { if (item) {
item.progress = e.percent + "%"; item.progress = e.percent + "%";
@ -107,7 +139,7 @@ export default {
// //
onUploadSuccess(res, file, key) { onUploadSuccess(res, file, key) {
const item = this.message.list.find(e => e.uid == file.uid); const item = this.messageList.find(e => e.uid == file.uid);
if (item) { if (item) {
item.loading = false; item.loading = false;
@ -204,7 +236,7 @@ export default {
// //
append(data) { append(data) {
eventBus.$emit("message-append", data); this.$store.commit("APPEND_MESSAGE_LIST", data);
} }
} }
}; };

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="cl-chat-message" v-loading="loading" element-loading-text="消息加载中"> <div class="cl-chat-message" v-loading="!visible && loading" element-loading-text="消息加载中">
<div <div
class="cl-chat-message__scroller scroller1" class="cl-chat-message__scroller scroller1"
ref="scroller" ref="scroller"
@ -8,7 +8,7 @@
}" }"
> >
<!-- 加载更多 --> <!-- 加载更多 -->
<div class="cl-chat-message__more" v-if="list.length > 0"> <div class="cl-chat-message__more" v-show="list.length > 0">
<el-button round size="mini" :loading="loading" @click="onLoadmore" <el-button round size="mini" :loading="loading" @click="onLoadmore"
>加载更多</el-button >加载更多</el-button
> >
@ -18,17 +18,19 @@
<div class="cl-chat-message__list"> <div class="cl-chat-message__list">
<div <div
class="cl-chat-message__item" class="cl-chat-message__item"
v-for="item in messageList" v-for="item in list"
:key="item.id || item.uid" :key="item.id || item.uid"
:class="[item.type == 0 ? `is-right` : `is-left`, `is-${item.mode}`]" :class="[item.type == 0 ? `is-right` : `is-left`, `is-${item.mode}`]"
> >
<!-- 日期 -->
<div class="date" v-if="item._date"> <div class="date" v-if="item._date">
<span>{{ item._date }}</span> <span>{{ item._date }}</span>
</div> </div>
<!-- 内容 -->
<div class="main"> <div class="main">
<!-- 头像 --> <!-- 头像 -->
<div class="avatar" @tap="toUserDetail(item)"> <div class="avatar">
<img :src="item.avatarUrl" /> <img :src="item.avatarUrl" />
</div> </div>
@ -53,7 +55,17 @@
:key="item.uid" :key="item.uid"
:src="item.content.imageUrl" :src="item.content.imageUrl"
:preview-src-list="[item.content.imageUrl]" :preview-src-list="[item.content.imageUrl]"
></el-image> :z-index="3000"
:style="item.style"
>
<template #placeholder>
<img
:src="item.content.imageUrl"
:style="item.style"
alt=""
/>
</template>
</el-image>
</template> </template>
<!-- 表情 --> <!-- 表情 -->
@ -90,7 +102,7 @@
</div> </div>
</div> </div>
<!-- voice --> <!-- 音频 -->
<div class="voice"> <div class="voice">
<audio style="display: none" ref="voice" :src="voice.url" controls></audio> <audio style="display: none" ref="voice" :src="voice.url" controls></audio>
</div> </div>
@ -106,19 +118,17 @@ import { isString } from "cl-admin/utils";
import eventBus from "../utils/event-bus"; import eventBus from "../utils/event-bus";
import IconVoice from "./icon-voice"; import IconVoice from "./icon-voice";
//
const ModeList = ["text", "image", "emoji", "voice", "video"];
export default { export default {
components: { components: {
IconVoice IconVoice
}, },
inject: ["chat"],
data() { data() {
return { return {
loading: false, loading: false,
visible: false, visible: false,
list: [],
pagination: { pagination: {
page: 1, page: 1,
size: 20, size: 20,
@ -140,12 +150,12 @@ export default {
}, },
computed: { computed: {
...mapGetters(["userInfo", "session"]), ...mapGetters(["userInfo", "session", "messageList"]),
messageList() { list() {
let date = ""; let date = "";
return this.list.map(e => { return this.messageList.map(e => {
// //
e._date = date e._date = date
? dayjs(e.createTime).isBefore(dayjs(date).add(1, "minute")) ? dayjs(e.createTime).isBefore(dayjs(date).add(1, "minute"))
@ -178,15 +188,18 @@ export default {
...e, ...e,
avatarUrl, avatarUrl,
nickName, nickName,
mode: ModeList[e.contentType] mode: this.chat.modes[e.contentType]
}; };
}); });
} }
}, },
created() { created() {
eventBus.$on("message-refresh", this.refresh); //
eventBus.$on("message-append", this.append); eventBus.$on("message.refresh", this.refresh);
//
eventBus.$on("message.scrollToBottom", this.scrollToBottom);
if (this.session) { if (this.session) {
this.refresh(); this.refresh();
@ -196,7 +209,7 @@ export default {
destroyed() { destroyed() {
clearTimeout(this.voice.timer); clearTimeout(this.voice.timer);
this.list.map(e => { this.messageList.map(e => {
e.isPlay = false; e.isPlay = false;
}); });
}, },
@ -206,7 +219,7 @@ export default {
onTap(item) { onTap(item) {
// //
if (item.mode == "voice") { if (item.mode == "voice") {
this.list.map(e => { this.messageList.map(e => {
this.$set(e, "isPlay", e.id == item.id ? e.isPlay : false); this.$set(e, "isPlay", e.id == item.id ? e.isPlay : false);
}); });
@ -245,11 +258,13 @@ export default {
sort: "desc" sort: "desc"
}; };
//
this.loading = true;
// //
if (data.page === 1) { if (data.page === 1) {
this.loading = true;
this.visible = false; this.visible = false;
this.list = []; this.$store.commit("CLEAR_MESSAGE_LIST");
} }
// //
@ -269,7 +284,7 @@ export default {
// //
this.pagination = res.pagination; this.pagination = res.pagination;
// //
this.prepend.apply(this, res.list); this.$store.commit("PREPEND_MESSAGE_LIST", res.list);
if (data.page === 1) { if (data.page === 1) {
this.scrollToBottom(); this.scrollToBottom();
@ -301,17 +316,6 @@ export default {
}); });
} }
}); });
},
//
prepend(...data) {
this.list.unshift(...data.reverse());
},
//
append(...data) {
this.list.push(...data);
this.scrollToBottom();
} }
} }
}; };
@ -349,7 +353,7 @@ export default {
font-size: 12px; font-size: 12px;
color: #fff; color: #fff;
border-radius: 3px; border-radius: 3px;
padding: 2px 5px; padding: 3px 5px 2px 5px;
letter-spacing: 1px; letter-spacing: 1px;
} }
} }
@ -474,8 +478,10 @@ export default {
&.is-video { &.is-video {
.item { .item {
video { video {
display: block;
max-width: 300px; max-width: 300px;
max-height: 300px; max-height: 300px;
border-radius: 10px;
} }
} }
} }

View File

@ -91,7 +91,7 @@ export default {
created() { created() {
// //
eventBus.$on("session-refresh", this.refresh); eventBus.$on("session.refresh", this.refresh);
// PC // PC
this.refresh().then(res => { this.refresh().then(res => {

View File

@ -1,5 +1,7 @@
import session from "./session"; import session from "./session";
import message from "./message";
export default { export default {
session session,
message
}; };

View File

@ -0,0 +1,67 @@
import { isArray } from "cl-admin/utils";
import eventBus from "../utils/event-bus";
export default {
state: {
list: []
},
getters: {
messageList: state => state.list
},
mutations: {
// 设置列表
SET_MESSAGE_LIST(state, data) {
state.list = data;
},
// 追加数据
APPEND_MESSAGE_LIST(state, data) {
const next = options => {
state.list.push({
...data,
...options
});
eventBus.$emit("message.scrollToBottom");
};
// 图片预览、大小处理
if (data.contentType === 1) {
const image = new Image();
image.onload = () => {
let height = 0;
let width = 0;
if (image.width > 200) {
width = 200;
height = (image.height * 200) / image.width;
}
next({
style: {
height: height + "px",
width: width + "px"
}
});
};
image.src = data.content.imageUrl;
} else {
next();
}
},
// 追加数据到头部
PREPEND_MESSAGE_LIST(state, data) {
const list = isArray(data) ? data : [data];
state.list.unshift(...list.reverse());
},
// 清空列表
CLEAR_MESSAGE_LIST(state) {
state.list = [];
}
}
};

View File

@ -22,7 +22,7 @@ export default {
SET_SESSION(state, data) { SET_SESSION(state, data) {
state.current = data; state.current = data;
state.current.serviceUnreadCount = 0; state.current.serviceUnreadCount = 0;
eventBus.$emit("message-refresh", { page: 1 }); eventBus.$emit("message.refresh", { page: 1 });
}, },
// 清空会话信息 // 清空会话信息

View File

@ -0,0 +1,6 @@
import { video_poster, image_resize } from "./oss";
export default {
video_poster,
image_resize
};

View File

@ -0,0 +1,48 @@
import { isArray, isObject } from "cl-admin/utils";
function parse(rules, { url, size }) {
if (!url) {
return "";
}
if (url.indexOf("http") !== 0) {
return url;
}
let h = 0;
let w = 0;
if (isArray(size)) {
h = size[0];
w = size[1];
} else if (isObject(size)) {
h = size.h;
w = size.w;
if (size.m) {
rules.push(size.m);
}
} else {
h = w = size;
}
url += url.includes("?") ? "&" : "?";
if (h) {
rules.push(`h_${h}`);
}
if (w) {
rules.push(`w_${w}`);
}
return `${url}${rules.join(",")}`;
}
export function video_poster(url, size) {
return parse(["x-oss-process=video/snapshot,t_1000,f_jpg"], { url, size });
}
export function image_resize(url, size) {
return parse(["x-oss-process=image/resize"], { url, size });
}

View File

@ -1,4 +1,5 @@
import service from "./service"; import service from "./service";
import components from "./components"; import components from "./components";
import filters from "./filters";
export default { components, service }; export default { components, service, filters };