mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 22:20:27 +08:00
808 lines
15 KiB
Vue
808 lines
15 KiB
Vue
|
<template>
|
|||
|
<div class="chat-wrap">
|
|||
|
<!-- 聊天窗口 -->
|
|||
|
<cl-dialog :visible.sync="visible" v-bind="conf">
|
|||
|
<div class="chat-box">
|
|||
|
<!-- 会话区域 -->
|
|||
|
<div class="chat-box__session">
|
|||
|
<div class="chat-box__session-search">
|
|||
|
<el-input
|
|||
|
v-model="session.keyWord"
|
|||
|
placeholder="搜索"
|
|||
|
prefix-icon="el-icon-search"
|
|||
|
size="small"
|
|||
|
clearable
|
|||
|
@clear="onSearch"
|
|||
|
@keyup.enter.native="onSearch"
|
|||
|
></el-input>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 会话列表 -->
|
|||
|
<ul class="chat-box__session-list scroller1">
|
|||
|
<li
|
|||
|
class="chat-box__session-item"
|
|||
|
v-for="(item, index) in sessionList"
|
|||
|
:key="index"
|
|||
|
:class="{
|
|||
|
'is-active': session.current ? item.id == session.current.id : false
|
|||
|
}"
|
|||
|
@click="sessionDetail(item)"
|
|||
|
@contextmenu.stop.prevent="openSessionCM($event, item.id, index)"
|
|||
|
>
|
|||
|
<!-- 头像 -->
|
|||
|
<div class="avatar">
|
|||
|
<el-badge
|
|||
|
:value="item.serviceUnreadCount"
|
|||
|
:hidden="item.serviceUnreadCount === 0"
|
|||
|
:max="99"
|
|||
|
>
|
|||
|
<img :src="item.headimgurl" alt="" />
|
|||
|
</el-badge>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 昵称,内容 -->
|
|||
|
<div class="det">
|
|||
|
<p class="name">{{ item.nickname }}</p>
|
|||
|
<p class="content">{{ item.lastMessage }}</p>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 会话详情 -->
|
|||
|
<div class="chat-box__detail">
|
|||
|
<template v-if="session.current">
|
|||
|
<div
|
|||
|
class="chat-box__detail-container scroller1"
|
|||
|
ref="scroller"
|
|||
|
v-loading="message.loading"
|
|||
|
>
|
|||
|
<!-- 加载更多 -->
|
|||
|
<div class="chat-box__detail-more" v-if="message.list.length > 0">
|
|||
|
<el-button
|
|||
|
round
|
|||
|
size="mini"
|
|||
|
:loading="message.loading"
|
|||
|
@click="onLoadmore"
|
|||
|
>加载更多</el-button
|
|||
|
>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 消息列表 -->
|
|||
|
<message :list="message.list" />
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="chat-box__detail-footer">
|
|||
|
<!-- 工具栏 -->
|
|||
|
<div class="chat-box__opbar">
|
|||
|
<ul>
|
|||
|
<!-- 表情 -->
|
|||
|
<li>
|
|||
|
<el-popover
|
|||
|
v-model="emoji.visible"
|
|||
|
placement="top-start"
|
|||
|
width="470"
|
|||
|
trigger="click"
|
|||
|
>
|
|||
|
<emoji @select="onEmojiSelect" />
|
|||
|
<img
|
|||
|
slot="reference"
|
|||
|
src="../static/images/emoji.png"
|
|||
|
alt=""
|
|||
|
/>
|
|||
|
</el-popover>
|
|||
|
</li>
|
|||
|
<!-- 图片上传 -->
|
|||
|
<li>
|
|||
|
<cl-upload
|
|||
|
accept="image/*"
|
|||
|
list-type
|
|||
|
:on-success="onImageSelect"
|
|||
|
>
|
|||
|
<img src="../static/images/image.png" alt="" />
|
|||
|
</cl-upload>
|
|||
|
</li>
|
|||
|
<!-- 视频上传 -->
|
|||
|
<li>
|
|||
|
<cl-upload
|
|||
|
accept="video/*"
|
|||
|
list-type
|
|||
|
:before-upload="
|
|||
|
(f) => {
|
|||
|
onBeforeUpload(f, 'video');
|
|||
|
}
|
|||
|
"
|
|||
|
:on-progress="onUploadProgress"
|
|||
|
:on-success="
|
|||
|
(r, f) => {
|
|||
|
onUploadSuccess(r, f, 'video');
|
|||
|
}
|
|||
|
"
|
|||
|
>
|
|||
|
<img src="../static/images/video.png" alt="" />
|
|||
|
</cl-upload>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 输入框,发送按钮 -->
|
|||
|
<div class="chat-box__input">
|
|||
|
<el-input
|
|||
|
v-model="message.value"
|
|||
|
placeholder="请描述您想咨询的问题"
|
|||
|
type="textarea"
|
|||
|
:rows="5"
|
|||
|
@keyup.enter.native="onTextSend"
|
|||
|
></el-input>
|
|||
|
|
|||
|
<el-button
|
|||
|
type="primary"
|
|||
|
size="mini"
|
|||
|
:disabled="!message.value"
|
|||
|
@click="onTextSend"
|
|||
|
>发送</el-button
|
|||
|
>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</cl-dialog>
|
|||
|
|
|||
|
<!-- MP3 -->
|
|||
|
<div class="mp3">
|
|||
|
<audio style="display: none" ref="sound" src="../static/notify.mp3" controls></audio>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import dayjs from "dayjs";
|
|||
|
import io from "socket.io-client";
|
|||
|
import { isString, debounce } from "cl-admin/utils";
|
|||
|
import { mapGetters } from "vuex";
|
|||
|
import { socketUrl } from "@/config/env";
|
|||
|
import Emoji from "./emoji";
|
|||
|
import Message from "./message";
|
|||
|
import { parseContent } from "../utils";
|
|||
|
|
|||
|
// 消息模式
|
|||
|
const MODES = ["text", "image", "emoji", "voice", "video"];
|
|||
|
|
|||
|
export default {
|
|||
|
name: "cl-chat",
|
|||
|
|
|||
|
components: {
|
|||
|
Message,
|
|||
|
Emoji
|
|||
|
},
|
|||
|
|
|||
|
data() {
|
|||
|
return {
|
|||
|
visible: false,
|
|||
|
conf: {
|
|||
|
title: "聊天对话框",
|
|||
|
props: {
|
|||
|
modal: true,
|
|||
|
"custom-class": "chat-box__wrap",
|
|||
|
"append-to-body": true,
|
|||
|
"close-on-click-modal": false,
|
|||
|
width: "1000px"
|
|||
|
}
|
|||
|
},
|
|||
|
message: {
|
|||
|
list: [],
|
|||
|
pagination: {
|
|||
|
page: 1,
|
|||
|
size: 20,
|
|||
|
total: 0
|
|||
|
},
|
|||
|
loading: false,
|
|||
|
value: ""
|
|||
|
},
|
|||
|
session: {
|
|||
|
list: [],
|
|||
|
pagination: {
|
|||
|
page: 1,
|
|||
|
size: 100,
|
|||
|
total: 0
|
|||
|
},
|
|||
|
current: null,
|
|||
|
keyWord: ""
|
|||
|
},
|
|||
|
emoji: {
|
|||
|
visible: false
|
|||
|
},
|
|||
|
socket: null
|
|||
|
};
|
|||
|
},
|
|||
|
|
|||
|
computed: {
|
|||
|
...mapGetters(["userInfo", "token"]),
|
|||
|
|
|||
|
sessionList() {
|
|||
|
return this.session.list
|
|||
|
.map((e) => {
|
|||
|
let { _text } = parseContent(e);
|
|||
|
e.lastMessage = _text;
|
|||
|
return e;
|
|||
|
})
|
|||
|
.sort((a, b) => {
|
|||
|
return a.updateTime < b.updateTime ? 1 : -1;
|
|||
|
});
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
mounted() {
|
|||
|
this.socket = io(`${socketUrl}?isAdmin=true&token=${this.token}`);
|
|||
|
|
|||
|
this.socket.on("connect", () => {
|
|||
|
console.log("socket connect");
|
|||
|
});
|
|||
|
|
|||
|
this.socket.on("admin", (msg) => {
|
|||
|
this.onMessage(msg);
|
|||
|
});
|
|||
|
|
|||
|
this.socket.on("error", (err) => {
|
|||
|
console.log(err);
|
|||
|
});
|
|||
|
|
|||
|
this.socket.on("disconnect", () => {
|
|||
|
console.log("disconnect connect");
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
destroyed() {
|
|||
|
this.socket.close();
|
|||
|
},
|
|||
|
|
|||
|
methods: {
|
|||
|
open() {
|
|||
|
this.visible = true;
|
|||
|
|
|||
|
this.refreshSession().then((res) => {
|
|||
|
this.sessionDetail(res.list[0]);
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
close() {
|
|||
|
this.visible = false;
|
|||
|
},
|
|||
|
|
|||
|
// 上传前
|
|||
|
onBeforeUpload(file, key) {
|
|||
|
const data = {
|
|||
|
content: {
|
|||
|
[`${key}Url`]: ""
|
|||
|
},
|
|||
|
type: 0,
|
|||
|
contentType: MODES.indexOf(key),
|
|||
|
uid: file.uid,
|
|||
|
loading: true,
|
|||
|
progress: "0%"
|
|||
|
};
|
|||
|
|
|||
|
this.append(data);
|
|||
|
},
|
|||
|
|
|||
|
// 上传中
|
|||
|
onUploadProgress(e, file) {
|
|||
|
let item = this.message.list.find((e) => e.uid == file.uid);
|
|||
|
|
|||
|
if (item) {
|
|||
|
item.progress = e.percent + "%";
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 上传成功
|
|||
|
onUploadSuccess(res, file, key) {
|
|||
|
let item = this.message.list.find((e) => e.uid == file.uid);
|
|||
|
|
|||
|
if (item) {
|
|||
|
item.loading = false;
|
|||
|
item.content[`${key}Url`] = res.data;
|
|||
|
|
|||
|
this.sendMessage(item);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 打开会话列表右键菜单
|
|||
|
openSessionCM(e, id, index) {
|
|||
|
this.$crud.openContextMenu(e, {
|
|||
|
list: [
|
|||
|
{
|
|||
|
label: "删除",
|
|||
|
icon: "el-icon-delete",
|
|||
|
callback: (item, done) => {
|
|||
|
this.$service.im.session.delete({
|
|||
|
ids: id
|
|||
|
});
|
|||
|
|
|||
|
this.session.list.splice(index, 1);
|
|||
|
|
|||
|
if (id == this.session.current.id) {
|
|||
|
this.sessionDetail();
|
|||
|
}
|
|||
|
|
|||
|
done();
|
|||
|
}
|
|||
|
}
|
|||
|
]
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 刷新会话列表
|
|||
|
refreshSession(params) {
|
|||
|
return this.$service.im.session
|
|||
|
.page({
|
|||
|
...this.session.pagination,
|
|||
|
keyWord: this.session.keyWord,
|
|||
|
params,
|
|||
|
order: "updateTime",
|
|||
|
sort: "desc"
|
|||
|
})
|
|||
|
.then(async (res) => {
|
|||
|
this.session.list = res.list;
|
|||
|
this.session.pagination = res.pagination;
|
|||
|
|
|||
|
return res;
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 刷新详情
|
|||
|
async sessionDetail(item) {
|
|||
|
if (item) {
|
|||
|
let { id } = this.session.current || {};
|
|||
|
|
|||
|
if (id != item.id) {
|
|||
|
item.serviceUnreadCount = 0;
|
|||
|
|
|||
|
this.conf.title = `与${item.nickname}聊天中`;
|
|||
|
this.message.loading = true;
|
|||
|
this.message.list = [];
|
|||
|
this.session.current = item;
|
|||
|
|
|||
|
await this.refreshMessage({ page: 1 });
|
|||
|
|
|||
|
this.message.loading = false;
|
|||
|
}
|
|||
|
|
|||
|
this.scrollToBottom();
|
|||
|
} else {
|
|||
|
this.conf.title = "聊天对话框";
|
|||
|
this.message.list = [];
|
|||
|
this.session.current = null;
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 刷新消息列表
|
|||
|
refreshMessage(params) {
|
|||
|
return this.$service.im.message
|
|||
|
.page({
|
|||
|
...this.message.pagination,
|
|||
|
...params,
|
|||
|
sessionId: this.session.current.id,
|
|||
|
order: "createTime",
|
|||
|
sort: "desc"
|
|||
|
})
|
|||
|
.then((res) => {
|
|||
|
this.message.pagination = res.pagination;
|
|||
|
this.prepend.apply(this, res.list);
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 更新会话消息
|
|||
|
updateSession(data) {
|
|||
|
Object.assign(this.session.current, data);
|
|||
|
},
|
|||
|
|
|||
|
// 搜索关键字
|
|||
|
onSearch() {
|
|||
|
this.refreshSession({ page: 1 });
|
|||
|
},
|
|||
|
|
|||
|
// 加载更多
|
|||
|
onLoadmore() {
|
|||
|
this.refreshMessage({ page: this.message.pagination.page + 1 });
|
|||
|
},
|
|||
|
|
|||
|
// 滚动到底部
|
|||
|
scrollToBottom: debounce(function () {
|
|||
|
this.$nextTick(() => {
|
|||
|
if (this.$refs["scroller"]) {
|
|||
|
this.$refs["scroller"].scrollTo(0, 999999);
|
|||
|
}
|
|||
|
});
|
|||
|
}, 300),
|
|||
|
|
|||
|
// 发送文本内容
|
|||
|
onTextSend() {
|
|||
|
if (this.message.value) {
|
|||
|
if (this.message.value.replace(/\n/g, "") !== "") {
|
|||
|
const data = {
|
|||
|
type: 0,
|
|||
|
contentType: 0,
|
|||
|
content: {
|
|||
|
text: this.message.value
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
this.append(data);
|
|||
|
this.sendMessage(data);
|
|||
|
|
|||
|
this.$nextTick(() => {
|
|||
|
this.message.value = "";
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 图片选择
|
|||
|
onImageSelect(res) {
|
|||
|
const data = {
|
|||
|
content: {
|
|||
|
imageUrl: res.data
|
|||
|
},
|
|||
|
type: 0,
|
|||
|
contentType: 1
|
|||
|
};
|
|||
|
this.append(data);
|
|||
|
this.sendMessage(data);
|
|||
|
},
|
|||
|
|
|||
|
// 表情选择
|
|||
|
onEmojiSelect(url) {
|
|||
|
this.emoji.visible = false;
|
|||
|
const data = {
|
|||
|
content: {
|
|||
|
imageUrl: url
|
|||
|
},
|
|||
|
type: 0,
|
|||
|
contentType: 2
|
|||
|
};
|
|||
|
this.append(data);
|
|||
|
this.sendMessage(data);
|
|||
|
},
|
|||
|
|
|||
|
// 视频选择
|
|||
|
onVideoSelect(url) {
|
|||
|
const data = {
|
|||
|
content: {
|
|||
|
videoUrl: url
|
|||
|
},
|
|||
|
type: 0,
|
|||
|
contentType: 4
|
|||
|
};
|
|||
|
this.append(data);
|
|||
|
this.sendMessage(data);
|
|||
|
},
|
|||
|
|
|||
|
// 监听消息
|
|||
|
onMessage(msg) {
|
|||
|
// 回调
|
|||
|
this.$emit("message", this.visible);
|
|||
|
|
|||
|
// 消息通知
|
|||
|
this.notification(msg);
|
|||
|
|
|||
|
try {
|
|||
|
const { contentType, fromId, content, msgId } = JSON.parse(msg);
|
|||
|
|
|||
|
// 是否当前
|
|||
|
const same = this.session.current && this.session.current.userId == fromId;
|
|||
|
|
|||
|
if (same) {
|
|||
|
// 更新消息
|
|||
|
this.updateSession({
|
|||
|
contentType,
|
|||
|
content
|
|||
|
});
|
|||
|
|
|||
|
// 追加消息
|
|||
|
this.append({
|
|||
|
contentType,
|
|||
|
content: JSON.parse(content),
|
|||
|
type: 1
|
|||
|
});
|
|||
|
|
|||
|
// 读消息
|
|||
|
this.$service.im.message.read({
|
|||
|
ids: [msgId],
|
|||
|
session: this.session.current.id
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 查找会话
|
|||
|
let item = this.session.list.find((e) => e.userId == fromId);
|
|||
|
|
|||
|
if (item) {
|
|||
|
if (!same) {
|
|||
|
item.serviceUnreadCount += 1;
|
|||
|
}
|
|||
|
// 更新消息
|
|||
|
Object.assign(item, {
|
|||
|
updateTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
|||
|
contentType,
|
|||
|
content
|
|||
|
});
|
|||
|
} else {
|
|||
|
// 刷新会话列表
|
|||
|
this.refreshSession();
|
|||
|
}
|
|||
|
} catch (e) {
|
|||
|
console.error("消息格式异常", e);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 消息通知
|
|||
|
notification(msg) {
|
|||
|
const { _text } = parseContent(JSON.parse(msg));
|
|||
|
|
|||
|
// 播放音乐
|
|||
|
if (this.$refs.sound) {
|
|||
|
this.$refs.sound.play();
|
|||
|
}
|
|||
|
|
|||
|
if (!this.visible) {
|
|||
|
// 页面消息提示
|
|||
|
this.$notify({
|
|||
|
title: "提示",
|
|||
|
message: this.$createElement("span", _text)
|
|||
|
});
|
|||
|
|
|||
|
// 浏览器消息通知
|
|||
|
const NotificationInstance = Notification || window.Notification;
|
|||
|
if (!!NotificationInstance) {
|
|||
|
if (NotificationInstance.permission !== "denied") {
|
|||
|
NotificationInstance.requestPermission((status) => {
|
|||
|
let n = new Notification("COOL-MALL", {
|
|||
|
body: _text,
|
|||
|
icon: "/favicon.ico"
|
|||
|
});
|
|||
|
|
|||
|
setTimeout(() => {
|
|||
|
n.close();
|
|||
|
}, 2000);
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 发送消息
|
|||
|
sendMessage({ contentType, content }) {
|
|||
|
const { id, userId } = this.session.current;
|
|||
|
|
|||
|
// 更新消息
|
|||
|
this.updateSession({
|
|||
|
contentType,
|
|||
|
content
|
|||
|
});
|
|||
|
|
|||
|
this.socket.emit(`user@${userId}`, {
|
|||
|
contentType,
|
|||
|
type: 0,
|
|||
|
content: JSON.stringify(content),
|
|||
|
sessionId: id
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* 处理消息数据
|
|||
|
* mode: 消息模式
|
|||
|
* type: 消息类型 0-回复,1-反馈
|
|||
|
* duration: 时常
|
|||
|
* videoUrl: 视频地址
|
|||
|
* videoCoverUrl: 视频封面
|
|||
|
* imageUrl: 图片地址
|
|||
|
* avatarUrl: 头像地址
|
|||
|
* nickName: 昵称
|
|||
|
*/
|
|||
|
handleMessage(e) {
|
|||
|
if (isString(e)) {
|
|||
|
e = JSON.parse(e);
|
|||
|
}
|
|||
|
|
|||
|
if (isString(e.content)) {
|
|||
|
e.content = JSON.parse(e.content);
|
|||
|
}
|
|||
|
|
|||
|
// 昵称
|
|||
|
const nickName = e.type == 0 ? this.userInfo.nickName : this.session.current.nickname;
|
|||
|
// 头像
|
|||
|
const avatarUrl =
|
|||
|
e.type == 0
|
|||
|
? this.userInfo.avatarUrl || require("../static/images/custom-avatar.png")
|
|||
|
: this.session.current.headimgurl;
|
|||
|
|
|||
|
return {
|
|||
|
...e,
|
|||
|
avatarUrl,
|
|||
|
nickName,
|
|||
|
mode: MODES[e.contentType],
|
|||
|
date: dayjs().format("YYYY-MM-DD HH:mm:ss")
|
|||
|
};
|
|||
|
},
|
|||
|
|
|||
|
// 追加数据到开头
|
|||
|
prepend(...data) {
|
|||
|
data.map(this.handleMessage).forEach((e) => {
|
|||
|
this.message.list.unshift(e);
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 追加数据到结尾
|
|||
|
append(...data) {
|
|||
|
this.message.list.push(...data.map(this.handleMessage));
|
|||
|
this.scrollToBottom();
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style lang="scss">
|
|||
|
.chat-box__wrap {
|
|||
|
height: 650px;
|
|||
|
min-width: 1000px;
|
|||
|
margin-bottom: 0 !important;
|
|||
|
|
|||
|
.el-dialog__body {
|
|||
|
height: calc(100% - 46px);
|
|||
|
padding: 0;
|
|||
|
|
|||
|
.cl-dialog__container {
|
|||
|
height: 100%;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.chat-box {
|
|||
|
display: flex;
|
|||
|
height: 100%;
|
|||
|
background-color: #f7f7f7;
|
|||
|
|
|||
|
&__session {
|
|||
|
height: calc(100% - 10px);
|
|||
|
width: 250px;
|
|||
|
margin: 5px 0 5px 5px;
|
|||
|
border-radius: 5px;
|
|||
|
background-color: #fff;
|
|||
|
|
|||
|
&-search {
|
|||
|
padding: 10px;
|
|||
|
}
|
|||
|
|
|||
|
ul {
|
|||
|
height: calc(100% - 52px);
|
|||
|
overflow: auto;
|
|||
|
|
|||
|
li {
|
|||
|
display: flex;
|
|||
|
list-style: none;
|
|||
|
padding: 10px;
|
|||
|
border-left: 5px solid #fff;
|
|||
|
|
|||
|
.avatar {
|
|||
|
height: 40px;
|
|||
|
width: 40px;
|
|||
|
margin-right: 12px;
|
|||
|
|
|||
|
img {
|
|||
|
display: block;
|
|||
|
height: 100%;
|
|||
|
width: 100%;
|
|||
|
border-radius: 3px;
|
|||
|
background-color: #eee;
|
|||
|
}
|
|||
|
|
|||
|
.el-badge {
|
|||
|
&__content {
|
|||
|
height: 14px;
|
|||
|
line-height: 14px;
|
|||
|
padding: 0 4px;
|
|||
|
background-color: #fa5151;
|
|||
|
border: 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.det {
|
|||
|
flex: 1;
|
|||
|
.name {
|
|||
|
font-size: 13px;
|
|||
|
margin-top: 1px;
|
|||
|
}
|
|||
|
|
|||
|
.content {
|
|||
|
font-size: 12px;
|
|||
|
margin-top: 5px;
|
|||
|
color: #666;
|
|||
|
}
|
|||
|
|
|||
|
.name,
|
|||
|
.content {
|
|||
|
@include text_ellipsis(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
&.is-active {
|
|||
|
background-color: #eee;
|
|||
|
border-color: $color-main;
|
|||
|
}
|
|||
|
|
|||
|
&:hover {
|
|||
|
background-color: #eee;
|
|||
|
cursor: pointer;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
&__detail {
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
flex: 1;
|
|||
|
height: 100%;
|
|||
|
padding: 5px;
|
|||
|
box-sizing: border-box;
|
|||
|
|
|||
|
&-container {
|
|||
|
flex: 1;
|
|||
|
border-radius: 5px;
|
|||
|
padding: 10px;
|
|||
|
overflow: auto;
|
|||
|
margin-bottom: 5px;
|
|||
|
}
|
|||
|
|
|||
|
&-more {
|
|||
|
display: flex;
|
|||
|
justify-content: center;
|
|||
|
margin-bottom: 20px;
|
|||
|
}
|
|||
|
|
|||
|
&-footer {
|
|||
|
background-color: #fff;
|
|||
|
padding: 10px;
|
|||
|
border-radius: 5px;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
&__message {
|
|||
|
flex: 1;
|
|||
|
border-radius: 5px;
|
|||
|
}
|
|||
|
|
|||
|
&__opbar {
|
|||
|
margin-bottom: 5px;
|
|||
|
ul {
|
|||
|
display: flex;
|
|||
|
li {
|
|||
|
list-style: none;
|
|||
|
margin-right: 10px;
|
|||
|
cursor: pointer;
|
|||
|
|
|||
|
&:hover {
|
|||
|
opacity: 0.7;
|
|||
|
}
|
|||
|
|
|||
|
img {
|
|||
|
height: 26px;
|
|||
|
width: 26px;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
&__input {
|
|||
|
position: relative;
|
|||
|
|
|||
|
.el-button {
|
|||
|
position: absolute;
|
|||
|
right: 10px;
|
|||
|
bottom: 10px;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|