ソースを参照

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

icssoa 4 年 前
コミット
58907611f6

+ 1 - 1
package.json

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

+ 1 - 1
src/config/env.js

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

+ 4 - 3
src/cool/modules/chat/components/chat.vue

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

+ 51 - 19
src/cool/modules/chat/components/input.vue

@@ -8,13 +8,27 @@
 					<emoji @select="onEmojiSelect" />
 				</li>
 				<!-- 图片上传 -->
-				<li hidden>
-					<cl-upload accept="image/*" list-type :on-success="onImageSelect">
+				<li>
+					<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="" />
 					</cl-upload>
 				</li>
 				<!-- 视频上传 -->
-				<li hidden>
+				<li>
 					<cl-upload
 						accept="video/*"
 						list-type
@@ -57,7 +71,6 @@
 <script>
 import { mapGetters } from "vuex";
 import Emoji from "./emoji";
-import eventBus from "../utils/event-bus";
 
 export default {
 	components: {
@@ -76,29 +89,48 @@ export default {
 	},
 
 	computed: {
-		...mapGetters(["session"])
+		...mapGetters(["session", "messageList"])
 	},
 
 	methods: {
-		// 上传前
+		// 上传前,获取图片预览地址
 		onBeforeUpload(file, key) {
-			const data = {
-				content: {
-					[`${key}Url`]: ""
-				},
-				type: 0,
-				contentType: MODES.indexOf(key),
-				uid: file.uid,
-				loading: true,
-				progress: "0%"
+			const next = (options = {}) => {
+				const data = {
+					content: {
+						[`${key}Url`]: ""
+					},
+					type: 0,
+					uid: file.uid,
+					loading: true,
+					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) {
-			const item = this.message.list.find(e => e.uid == file.uid);
+			const item = this.messageList.find(e => e.uid == file.uid);
 
 			if (item) {
 				item.progress = e.percent + "%";
@@ -107,7 +139,7 @@ export default {
 
 		// 上传成功
 		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) {
 				item.loading = false;
@@ -204,7 +236,7 @@ export default {
 
 		// 追加消息
 		append(data) {
-			eventBus.$emit("message-append", data);
+			this.$store.commit("APPEND_MESSAGE_LIST", data);
 		}
 	}
 };

+ 39 - 33
src/cool/modules/chat/components/message.vue

@@ -1,5 +1,5 @@
 <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
 			class="cl-chat-message__scroller scroller1"
 			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
 				>
@@ -18,17 +18,19 @@
 			<div class="cl-chat-message__list">
 				<div
 					class="cl-chat-message__item"
-					v-for="item in messageList"
+					v-for="item in list"
 					:key="item.id || item.uid"
 					:class="[item.type == 0 ? `is-right` : `is-left`, `is-${item.mode}`]"
 				>
+					<!-- 日期 -->
 					<div class="date" v-if="item._date">
 						<span>{{ item._date }}</span>
 					</div>
 
+					<!-- 内容 -->
 					<div class="main">
 						<!-- 头像 -->
-						<div class="avatar" @tap="toUserDetail(item)">
+						<div class="avatar">
 							<img :src="item.avatarUrl" />
 						</div>
 
@@ -53,7 +55,17 @@
 										:key="item.uid"
 										:src="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>
 
 								<!-- 表情 -->
@@ -90,7 +102,7 @@
 					</div>
 				</div>
 
-				<!-- voice -->
+				<!-- 音频 -->
 				<div class="voice">
 					<audio style="display: none" ref="voice" :src="voice.url" controls></audio>
 				</div>
@@ -106,19 +118,17 @@ import { isString } from "cl-admin/utils";
 import eventBus from "../utils/event-bus";
 import IconVoice from "./icon-voice";
 
-// 消息类型
-const ModeList = ["text", "image", "emoji", "voice", "video"];
-
 export default {
 	components: {
 		IconVoice
 	},
 
+	inject: ["chat"],
+
 	data() {
 		return {
 			loading: false,
 			visible: false,
-			list: [],
 			pagination: {
 				page: 1,
 				size: 20,
@@ -140,12 +150,12 @@ export default {
 	},
 
 	computed: {
-		...mapGetters(["userInfo", "session"]),
+		...mapGetters(["userInfo", "session", "messageList"]),
 
-		messageList() {
+		list() {
 			let date = "";
 
-			return this.list.map(e => {
+			return this.messageList.map(e => {
 				// 时间间隔
 				e._date = date
 					? dayjs(e.createTime).isBefore(dayjs(date).add(1, "minute"))
@@ -178,15 +188,18 @@ export default {
 					...e,
 					avatarUrl,
 					nickName,
-					mode: ModeList[e.contentType]
+					mode: this.chat.modes[e.contentType]
 				};
 			});
 		}
 	},
 
 	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) {
 			this.refresh();
@@ -196,7 +209,7 @@ export default {
 	destroyed() {
 		clearTimeout(this.voice.timer);
 
-		this.list.map(e => {
+		this.messageList.map(e => {
 			e.isPlay = false;
 		});
 	},
@@ -206,7 +219,7 @@ export default {
 		onTap(item) {
 			// 播放语音
 			if (item.mode == "voice") {
-				this.list.map(e => {
+				this.messageList.map(e => {
 					this.$set(e, "isPlay", e.id == item.id ? e.isPlay : false);
 				});
 
@@ -245,11 +258,13 @@ export default {
 				sort: "desc"
 			};
 
+			// 加载动画
+			this.loading = true;
+
 			// 首页处理
 			if (data.page === 1) {
-				this.loading = true;
 				this.visible = false;
-				this.list = [];
+				this.$store.commit("CLEAR_MESSAGE_LIST");
 			}
 
 			// 完成
@@ -269,7 +284,7 @@ export default {
 					// 分页信息
 					this.pagination = res.pagination;
 					// 追加数据
-					this.prepend.apply(this, res.list);
+					this.$store.commit("PREPEND_MESSAGE_LIST", res.list);
 
 					if (data.page === 1) {
 						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;
 				color: #fff;
 				border-radius: 3px;
-				padding: 2px 5px;
+				padding: 3px 5px 2px 5px;
 				letter-spacing: 1px;
 			}
 		}
@@ -474,8 +478,10 @@ export default {
 		&.is-video {
 			.item {
 				video {
+					display: block;
 					max-width: 300px;
 					max-height: 300px;
+					border-radius: 10px;
 				}
 			}
 		}

+ 1 - 1
src/cool/modules/chat/components/session.vue

@@ -91,7 +91,7 @@ export default {
 
 	created() {
 		// 监听列表刷新
-		eventBus.$on("session-refresh", this.refresh);
+		eventBus.$on("session.refresh", this.refresh);
 
 		// PC 端下首次请求读取第一个消息
 		this.refresh().then(res => {

+ 3 - 1
src/cool/modules/chat/store/index.js

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

+ 67 - 0
src/cool/modules/chat/store/message.js

@@ -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 = [];
+		}
+	}
+};

+ 1 - 1
src/cool/modules/chat/store/session.js

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

+ 6 - 0
src/cool/modules/upload/filters/index.js

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

+ 48 - 0
src/cool/modules/upload/filters/oss.js

@@ -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 });
+}

+ 2 - 1
src/cool/modules/upload/index.js

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