no message
This commit is contained in:
parent
c6285c57c8
commit
4c51e52a30
@ -17,6 +17,61 @@ use Request;
|
||||
*/
|
||||
class DialogController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* 对话列表
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:100,最大:200
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$list = WebSocketDialog::select(['web_socket_dialogs.*'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('u.userid', $user->userid)
|
||||
->orderByDesc('web_socket_dialogs.last_at')
|
||||
->paginate(Base::getPaginate(200, 100));
|
||||
$list->transform(function (WebSocketDialog $item) use ($user) {
|
||||
return WebSocketDialog::formatData($item, $user->userid);
|
||||
});
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个对话信息
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
*/
|
||||
public function one()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
//
|
||||
$item = WebSocketDialog::select(['web_socket_dialogs.*'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('web_socket_dialogs.id', $dialog_id)
|
||||
->where('u.userid', $user->userid)
|
||||
->first();
|
||||
if ($item) {
|
||||
$item = WebSocketDialog::formatData($item, $user->userid);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息列表
|
||||
*
|
||||
|
@ -375,6 +375,18 @@ class User extends AbstractModel
|
||||
return $_A["__static_userid2basic_" . $userid] = ($userInfo ?: []);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* userid 获取 昵称
|
||||
* @param $userid
|
||||
* @return string
|
||||
*/
|
||||
public static function userid2nickname($userid)
|
||||
{
|
||||
$basic = self::userid2basic($userid);
|
||||
return $basic ? $basic->nickname : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新首字母
|
||||
* @param $userid
|
||||
|
@ -31,6 +31,29 @@ use App\Module\Base;
|
||||
*/
|
||||
class WebSocketDialog extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* 格式化对话
|
||||
* @param WebSocketDialog $dialog
|
||||
* @param int $userid 会员ID
|
||||
* @return WebSocketDialog
|
||||
*/
|
||||
public static function formatData(WebSocketDialog $dialog, $userid)
|
||||
{
|
||||
// 最后消息
|
||||
$last_msg = WebSocketDialogMsg::whereDialogId($dialog->id)->orderByDesc('id')->first();
|
||||
$dialog->last_msg = $last_msg;
|
||||
// 未读信息
|
||||
$dialog->unread = WebSocketDialogMsgRead::whereDialogId($dialog->id)->whereUserid($userid)->whereReadAt(null)->count();
|
||||
// 对方信息
|
||||
$dialog->dialog_user = null;
|
||||
if ($dialog->type === 'user') {
|
||||
$dialog_user = WebSocketDialogUser::whereDialogId($dialog->id)->where('userid', '!=', $userid)->first();
|
||||
$dialog->name = User::userid2nickname($dialog_user->userid);
|
||||
$dialog->dialog_user = $dialog_user;
|
||||
}
|
||||
return $dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建聊天室
|
||||
* @param string $name 聊天室名称
|
||||
|
@ -99,6 +99,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$msgRead = WebSocketDialogMsgRead::whereMsgId($this->id)->whereUserid($userid)->lockForUpdate()->first();
|
||||
if (empty($msgRead)) {
|
||||
$msgRead = WebSocketDialogMsgRead::createInstance([
|
||||
'dialog_id' => $this->dialog_id,
|
||||
'msg_id' => $this->id,
|
||||
'userid' => $userid,
|
||||
]);
|
||||
|
@ -7,12 +7,14 @@ namespace App\Models;
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $dialog_id 对话ID
|
||||
* @property int|null $msg_id 消息ID
|
||||
* @property int|null $userid 发送会员ID
|
||||
* @property string|null $read_at 阅读时间
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereDialogId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMsgId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereReadAt($value)
|
||||
|
@ -35,12 +35,14 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
$userids = is_array($this->userid) ? $this->userid : [$this->userid];
|
||||
$msgId = intval($this->dialogMsgArray['id']);
|
||||
$send = intval($this->dialogMsgArray['send']);
|
||||
$dialogId = intval($this->dialogMsgArray['dialog_id']);
|
||||
if (empty($userids) || empty($msgId)) {
|
||||
return;
|
||||
}
|
||||
$pushIds = [];
|
||||
foreach ($userids AS $userid) {
|
||||
$msgRead = WebSocketDialogMsgRead::createInstance([
|
||||
'dialog_id' => $dialogId,
|
||||
'msg_id' => $msgId,
|
||||
'userid' => $userid,
|
||||
]);
|
||||
|
@ -7,17 +7,21 @@
|
||||
</div>
|
||||
<ul>
|
||||
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
|
||||
<Icon type="md-speedometer" />
|
||||
<Icon type="ios-speedometer-outline" />
|
||||
<div class="menu-title">{{$L('仪表板')}}</div>
|
||||
</li>
|
||||
<li @click="toggleRoute('setting/personal')" :class="classNameRoute('setting')">
|
||||
<Icon type="md-cog" />
|
||||
<div class="menu-title">{{$L('设置')}}</div>
|
||||
</li>
|
||||
<li @click="toggleRoute('calendar')" :class="classNameRoute('calendar')">
|
||||
<Icon type="md-calendar" />
|
||||
<Icon type="ios-calendar-outline" />
|
||||
<div class="menu-title">{{$L('日历')}}</div>
|
||||
</li>
|
||||
<li @click="toggleRoute('dialog')" :class="classNameRoute('dialog')">
|
||||
<Icon type="ios-chatbubbles-outline" />
|
||||
<div class="menu-title">{{$L('消息')}}</div>
|
||||
</li>
|
||||
<li @click="toggleRoute('setting/personal')" :class="classNameRoute('setting')">
|
||||
<Icon type="ios-cog-outline" />
|
||||
<div class="menu-title">{{$L('设置')}}</div>
|
||||
</li>
|
||||
<li class="menu-project">
|
||||
<ul>
|
||||
<li v-for="(item, key) in projectList" :key="key" @click="toggleRoute('project/' + item.id)" :class="classNameRoute('project/' + item.id)">{{item.name}}</li>
|
||||
@ -131,7 +135,7 @@
|
||||
padding: 0 4%;
|
||||
border-radius: 4px;
|
||||
> i {
|
||||
opacity: 0.3;
|
||||
opacity: 0.5;
|
||||
font-size: 22px;
|
||||
margin-right: 10px;
|
||||
margin-top: -1px;
|
||||
|
@ -20,9 +20,9 @@
|
||||
<ScrollerY class="dialog-chat dialog-scroller" @on-scroll="chatScroll">
|
||||
<div ref="manageList" class="dialog-list">
|
||||
<ul>
|
||||
<li v-if="dialogLoad > 0" class="loading"><Loading/></li>
|
||||
<li v-else-if="dialogList.length === 0" class="nothing">{{$L('暂无消息')}}</li>
|
||||
<li v-for="(item, key) in dialogList" :key="key" :class="{self:item.userid == userId}">
|
||||
<li v-if="dialogMsgLoad > 0" class="loading"><Loading/></li>
|
||||
<li v-else-if="dialogMsgList.length === 0" class="nothing">{{$L('暂无消息')}}</li>
|
||||
<li v-for="(item, key) in dialogMsgList" :key="key" :class="{self:item.userid == userId}">
|
||||
<div class="dialog-avatar">
|
||||
<UserAvatar :userid="item.userid" :size="30"/>
|
||||
</div>
|
||||
@ -230,7 +230,7 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['userId', 'projectDetail', 'projectMsgUnread', 'dialogLoad', 'dialogList']),
|
||||
...mapState(['userId', 'projectDetail', 'projectMsgUnread', 'dialogMsgLoad', 'dialogMsgList']),
|
||||
},
|
||||
|
||||
watch: {
|
||||
@ -242,7 +242,7 @@ export default {
|
||||
this.$store.commit('getDialogMsg', id);
|
||||
},
|
||||
|
||||
dialogList(list) {
|
||||
dialogMsgList(list) {
|
||||
if (!this.autoBottom) {
|
||||
let length = list.length - this.msgLength;
|
||||
if (length > 0) {
|
||||
@ -256,7 +256,7 @@ export default {
|
||||
methods: {
|
||||
sendMsg() {
|
||||
let tempId = $A.randomString(16);
|
||||
this.dialogList.push({
|
||||
this.dialogMsgList.push({
|
||||
id: tempId,
|
||||
type: 'text',
|
||||
userid: this.userId,
|
||||
@ -341,7 +341,7 @@ export default {
|
||||
chatFile(type, file) {
|
||||
switch (type) {
|
||||
case 'progress':
|
||||
this.dialogList.push({
|
||||
this.dialogMsgList.push({
|
||||
id: file.tempId,
|
||||
type: 'loading',
|
||||
userid: this.userId,
|
||||
|
@ -1,17 +1,175 @@
|
||||
<template>
|
||||
<div class="calendar">
|
||||
<PageTitle>{{$L('Calendar')}}</PageTitle>
|
||||
<div class="dialog">
|
||||
<PageTitle>{{ $L('消息') }}</PageTitle>
|
||||
<div class="dialog-wrapper">
|
||||
|
||||
<div class="dialog-select">
|
||||
<div class="dialog-search">
|
||||
<Input prefix="ios-search" v-model="dialogKey" :placeholder="$L('搜索...')" clearable />
|
||||
</div>
|
||||
<div class="dialog-list overlay-y">
|
||||
<ul>
|
||||
<li v-for="(dialog, key) in dialogLists" :key="key" :class="{active: dialog.id == dialogId}">
|
||||
<Icon v-if="dialog.type=='group'" class="group-avatar" type="ios-people" />
|
||||
<UserAvatar v-else-if="dialog.dialog_user" :userid="dialog.dialog_user.userid" :size="46"/>
|
||||
<div class="user-msg-box">
|
||||
<div class="user-msg-title">
|
||||
<span>{{dialog.name}}</span>
|
||||
<em v-if="dialog.last_at">{{formatTime(dialog.last_at)}}</em>
|
||||
</div>
|
||||
<div class="user-msg-text">{{formatLastMsg(dialog.last_msg)}}</div>
|
||||
</div>
|
||||
<Badge class="user-msg-num" :count="dialog.unread"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dialog-menu">
|
||||
<Icon class="active" type="ios-chatbubbles" />
|
||||
<Icon type="md-person" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-msg"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:global {
|
||||
.dialog {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
dialogLoad: 0,
|
||||
dialogKey: '',
|
||||
dialogList: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
mounted() {
|
||||
this.getDialogLists();
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['dialogId', 'wsMsg']),
|
||||
|
||||
dialogLists() {
|
||||
const {dialogKey} = this;
|
||||
if (dialogKey == '') {
|
||||
return this.dialogList;
|
||||
}
|
||||
return this.dialogList.filter(({name}) => {
|
||||
return $A.strExists(name, dialogKey);
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
/**
|
||||
* 收到新消息
|
||||
* @param msg
|
||||
*/
|
||||
wsMsg(msg) {
|
||||
const {type, data} = msg;
|
||||
if (type === "dialog") {
|
||||
if (this.dialogId == data.dialog_id) {
|
||||
return;
|
||||
}
|
||||
let dialog = this.dialogList.find(({id}) => id == data.dialog_id);
|
||||
if (dialog) {
|
||||
this.$set(dialog, 'unread', dialog.unread + 1);
|
||||
this.$set(dialog, 'last_msg', data);
|
||||
} else {
|
||||
this.getDialogOne(data.dialog_id)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开对话,标记已读
|
||||
* @param dialog_id
|
||||
*/
|
||||
dialogId(dialog_id) {
|
||||
let dialog = this.dialogList.find(({id}) => id == dialog_id);
|
||||
if (dialog) {
|
||||
this.$set(dialog, 'unread', 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getDialogLists() {
|
||||
this.dialogLoad++;
|
||||
$A.apiAjax({
|
||||
url: 'dialog/lists',
|
||||
complete: () => {
|
||||
this.dialogLoad--;
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
if (ret === 1) {
|
||||
this.dialogList = data.data;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getDialogOne(dialog_id) {
|
||||
$A.apiAjax({
|
||||
url: 'dialog/one',
|
||||
data: {
|
||||
dialog_id,
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
if (ret === 1) {
|
||||
let index = this.dialogList.findIndex(({id}) => id == data.id);
|
||||
if (index > -1) {
|
||||
this.dialogList.splice(index, 1, data);
|
||||
} else {
|
||||
this.dialogList.unshift(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
formatTime(date) {
|
||||
let time = Math.round(new Date(date).getTime() / 1000),
|
||||
string = '';
|
||||
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
|
||||
string = $A.formatDate('H:i', time)
|
||||
} else if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
|
||||
string = $A.formatDate('m-d', time)
|
||||
} else {
|
||||
string = $A.formatDate('Y-m-d', time)
|
||||
}
|
||||
return string || '';
|
||||
},
|
||||
|
||||
formatLastMsg(data) {
|
||||
if ($A.isJson(data)) {
|
||||
switch (data.type) {
|
||||
case 'text':
|
||||
return data.msg.text
|
||||
case 'file':
|
||||
if (data.msg.type == 'img') {
|
||||
return '[' + this.$L('图片') + ']'
|
||||
}
|
||||
return '[' + this.$L('文件') + '] ' + data.msg.name
|
||||
default:
|
||||
return '[' + this.$L('未知的消息') + ']'
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="project-detail">
|
||||
<div class="project">
|
||||
<PageTitle>{{ $L('项目面板') }}</PageTitle>
|
||||
<ProjectList/>
|
||||
<ProjectDialog/>
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:global {
|
||||
.project-detail {
|
||||
.project {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
.project-list {
|
||||
|
5
resources/assets/js/routes.js
vendored
5
resources/assets/js/routes.js
vendored
@ -22,6 +22,11 @@ export default [
|
||||
path: 'calendar',
|
||||
component: () => import('./pages/manage/calendar.vue'),
|
||||
},
|
||||
{
|
||||
name: 'manage-dialog',
|
||||
path: 'dialog',
|
||||
component: () => import('./pages/manage/dialog.vue'),
|
||||
},
|
||||
{
|
||||
name: 'manage-setting',
|
||||
path: 'setting',
|
||||
|
32
resources/assets/js/store/mutations.js
vendored
32
resources/assets/js/store/mutations.js
vendored
@ -202,9 +202,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (state.method.isArray(state.cacheDialog[dialog_id])) {
|
||||
state.dialogList = state.cacheDialog[dialog_id]
|
||||
state.dialogMsgList = state.cacheDialog[dialog_id]
|
||||
} else {
|
||||
state.dialogList = [];
|
||||
state.dialogMsgList = [];
|
||||
}
|
||||
state.dialogId = dialog_id;
|
||||
//
|
||||
@ -213,14 +213,14 @@ export default {
|
||||
}
|
||||
state.cacheDialog[dialog_id + "::load"] = true;
|
||||
//
|
||||
state.dialogLoad++;
|
||||
state.dialogMsgLoad++;
|
||||
$A.apiAjax({
|
||||
url: 'dialog/msg/lists',
|
||||
data: {
|
||||
dialog_id: dialog_id,
|
||||
},
|
||||
complete: () => {
|
||||
state.dialogLoad--;
|
||||
state.dialogMsgLoad--;
|
||||
state.cacheDialog[dialog_id + "::load"] = false;
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
@ -228,11 +228,11 @@ export default {
|
||||
state.cacheDialog[dialog_id] = data.data.reverse();
|
||||
if (state.dialogId === dialog_id) {
|
||||
state.cacheDialog[dialog_id].forEach((item) => {
|
||||
let index = state.dialogList.findIndex(({id}) => id === item.id);
|
||||
let index = state.dialogMsgList.findIndex(({id}) => id === item.id);
|
||||
if (index === -1) {
|
||||
state.dialogList.push(item);
|
||||
state.dialogMsgList.push(item);
|
||||
} else {
|
||||
state.dialogList.splice(index, 1, item);
|
||||
state.dialogMsgList.splice(index, 1, item);
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -252,16 +252,16 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (state.method.isJson(data)) {
|
||||
if (data.id && state.dialogList.find(m => m.id == data.id)) {
|
||||
if (data.id && state.dialogMsgList.find(m => m.id == data.id)) {
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
let index = state.dialogList.findIndex(m => m.id == id);
|
||||
let index = state.dialogMsgList.findIndex(m => m.id == id);
|
||||
if (index > -1) {
|
||||
if (data) {
|
||||
state.dialogList.splice(index, 1, state.method.cloneJSON(data));
|
||||
state.dialogMsgList.splice(index, 1, state.method.cloneJSON(data));
|
||||
} else {
|
||||
state.dialogList.splice(index, 1);
|
||||
state.dialogMsgList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -339,14 +339,14 @@ export default {
|
||||
const msgData = msgDetail.data;
|
||||
const dialog_id = msgData.dialog_id;
|
||||
if (dialog_id == state.dialogId) {
|
||||
let index = state.dialogList.findIndex(({id}) => id === msgData.id);
|
||||
let index = state.dialogMsgList.findIndex(({id}) => id === msgData.id);
|
||||
if (index === -1) {
|
||||
if (state.dialogList.length >= 200) {
|
||||
state.dialogList.splice(0, 1);
|
||||
if (state.dialogMsgList.length >= 200) {
|
||||
state.dialogMsgList.splice(0, 1);
|
||||
}
|
||||
state.dialogList.push(msgData);
|
||||
state.dialogMsgList.push(msgData);
|
||||
} else {
|
||||
state.dialogList.splice(index, 1, msgData);
|
||||
state.dialogMsgList.splice(index, 1, msgData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
resources/assets/js/store/state.js
vendored
4
resources/assets/js/store/state.js
vendored
@ -184,8 +184,8 @@ state.projectMsgUnread = 0;
|
||||
|
||||
// 会话消息
|
||||
state.dialogId = 0;
|
||||
state.dialogLoad = 0;
|
||||
state.dialogList = [];
|
||||
state.dialogMsgLoad = 0;
|
||||
state.dialogMsgList = [];
|
||||
|
||||
// 任务优先级
|
||||
state.taskPriority = [];
|
||||
|
152
resources/assets/sass/main.scss
vendored
152
resources/assets/sass/main.scss
vendored
@ -897,3 +897,155 @@ body {
|
||||
background-color: #F4F4F5;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
.dialog-select {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
min-width: 240px;
|
||||
max-width: 320px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.dialog-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 54px;
|
||||
padding: 0 12px;
|
||||
flex-shrink: 0;
|
||||
.ivu-input {
|
||||
border-color: transparent;
|
||||
&:hover,
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog-list {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
> ul {
|
||||
> li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 86px;
|
||||
padding: 0 12px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
background-color: #F4F5F7;
|
||||
}
|
||||
.common-avatar {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.group-avatar {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
border-radius: 50%;
|
||||
font-size: 26px;
|
||||
background-color: #61B2F9;
|
||||
color: #ffffff;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.user-msg-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 12px;
|
||||
.user-msg-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
line-height: 24px;
|
||||
> span {
|
||||
flex: 1;
|
||||
max-width: 130px;
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
> em {
|
||||
font-style: normal;
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
.user-msg-text {
|
||||
max-width: 170px;
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.user-msg-num {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 38px;
|
||||
font-size: 12px;
|
||||
.ivu-badge-count {
|
||||
height: 18px;
|
||||
min-width: 18px;
|
||||
line-height: 16px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 54px;
|
||||
flex-shrink: 0;
|
||||
border-top: 1px solid #f2f2f2;
|
||||
> i {
|
||||
cursor: pointer;
|
||||
font-size: 28px;
|
||||
margin: 0 24px;
|
||||
color: #aaaaaa;
|
||||
opacity: 0.9;
|
||||
&.active {
|
||||
opacity: 1;
|
||||
color: #2d8cf0;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog-msg {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
2
resources/assets/sass/scrollbar.scss
vendored
2
resources/assets/sass/scrollbar.scss
vendored
@ -27,7 +27,7 @@
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.overlay-x {
|
||||
.overlay {
|
||||
overflow: overlay !important;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user