perf: 优化撤回消息
This commit is contained in:
parent
3d04bd4444
commit
9999548bc2
@ -160,16 +160,28 @@ class DialogController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} api/dialog/msg/sendtext 05. 未读消息
|
* @api {get} api/dialog/msg/unread 05. 获取未读消息数量
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份
|
* @apiDescription 需要token身份
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
* @apiGroup dialog
|
* @apiGroup dialog
|
||||||
* @apiName msg__sendtext
|
* @apiName msg__unread
|
||||||
|
*
|
||||||
|
* @apiParam {Number} [dialog_id] 对话ID,留空获取总未读消息数量
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
*/
|
*/
|
||||||
public function msg__unread()
|
public function msg__unread()
|
||||||
{
|
{
|
||||||
$unread = WebSocketDialogMsgRead::whereUserid(User::userid())->whereReadAt(null)->count();
|
$dialog_id = intval(Request::input('dialog_id'));
|
||||||
|
//
|
||||||
|
$builder = WebSocketDialogMsgRead::whereUserid(User::userid())->whereReadAt(null);
|
||||||
|
if ($dialog_id > 0) {
|
||||||
|
$builder->whereDialogId($dialog_id);
|
||||||
|
}
|
||||||
|
$unread = $builder->count();
|
||||||
return Base::retSuccess('success', [
|
return Base::retSuccess('success', [
|
||||||
'unread' => $unread,
|
'unread' => $unread,
|
||||||
]);
|
]);
|
||||||
@ -417,7 +429,7 @@ class DialogController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* @api {get} api/dialog/msg/withdraw 11. 聊天消息撤回
|
* @api {get} api/dialog/msg/withdraw 11. 聊天消息撤回
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份
|
* @apiDescription 消息撤回限制24小时内,需要token身份
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
* @apiGroup dialog
|
* @apiGroup dialog
|
||||||
* @apiName msg__withdraw
|
* @apiName msg__withdraw
|
||||||
|
@ -8,6 +8,7 @@ use App\Tasks\PushTask;
|
|||||||
use App\Tasks\WebSocketDialogMsgTask;
|
use App\Tasks\WebSocketDialogMsgTask;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App\Models\WebSocketDialogMsg
|
* App\Models\WebSocketDialogMsg
|
||||||
@ -21,11 +22,15 @@ use Hhxsv5\LaravelS\Swoole\Task\Task;
|
|||||||
* @property int|null $send 发送数量
|
* @property int|null $send 发送数量
|
||||||
* @property \Illuminate\Support\Carbon|null $created_at
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||||
* @property-read int|mixed $percentage
|
* @property-read int|mixed $percentage
|
||||||
|
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
|
||||||
|
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg onlyTrashed()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereCreatedAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDeletedAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDialogId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDialogId($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMsg($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMsg($value)
|
||||||
@ -34,10 +39,14 @@ use Hhxsv5\LaravelS\Swoole\Task\Task;
|
|||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUpdatedAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUpdatedAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUserid($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUserid($value)
|
||||||
|
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg withTrashed()
|
||||||
|
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg withoutTrashed()
|
||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class WebSocketDialogMsg extends AbstractModel
|
class WebSocketDialogMsg extends AbstractModel
|
||||||
{
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
protected $appends = [
|
protected $appends = [
|
||||||
'percentage',
|
'percentage',
|
||||||
];
|
];
|
||||||
@ -46,6 +55,14 @@ class WebSocketDialogMsg extends AbstractModel
|
|||||||
'updated_at',
|
'updated_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||||
|
*/
|
||||||
|
public function webSocketDialog(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(WebSocketDialog::class, 'id', 'dialog_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阅读占比
|
* 阅读占比
|
||||||
* @return int|mixed
|
* @return int|mixed
|
||||||
@ -130,27 +147,39 @@ class WebSocketDialogMsg extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function deleteMsg()
|
public function deleteMsg()
|
||||||
{
|
{
|
||||||
$send_dt = Carbon::parse($this->created_at)->addMinutes(5);
|
$send_dt = Carbon::parse($this->created_at)->addDay();
|
||||||
if ($send_dt->lt(Carbon::now())) {
|
if ($send_dt->lt(Carbon::now())) {
|
||||||
throw new ApiException('已超过5分钟,此消息不能撤回');
|
throw new ApiException('已超过24小时,此消息不能撤回');
|
||||||
}
|
|
||||||
$this->delete();
|
|
||||||
//
|
|
||||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
|
||||||
if ($dialog) {
|
|
||||||
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
|
||||||
PushTask::push([
|
|
||||||
'userid' => $userids,
|
|
||||||
'msg' => [
|
|
||||||
'type' => 'dialog',
|
|
||||||
'mode' => 'delete',
|
|
||||||
'data' => [
|
|
||||||
'id' => $this->id,
|
|
||||||
'dialog_id' => $this->dialog_id
|
|
||||||
],
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
AbstractModel::transaction(function() {
|
||||||
|
$deleteRead = WebSocketDialogMsgRead::whereMsgId($this->id)->whereNull('read_at')->delete(); // 未阅读记录不需要软删除,直接删除即可
|
||||||
|
$this->delete();
|
||||||
|
//
|
||||||
|
$last_msg = null;
|
||||||
|
if ($this->webSocketDialog) {
|
||||||
|
$last_msg = WebSocketDialogMsg::whereDialogId($this->dialog_id)->orderByDesc('id')->first();
|
||||||
|
$this->webSocketDialog->last_at = $last_msg->created_at;
|
||||||
|
$this->webSocketDialog->save();
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||||
|
if ($dialog) {
|
||||||
|
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
||||||
|
PushTask::push([
|
||||||
|
'userid' => $userids,
|
||||||
|
'msg' => [
|
||||||
|
'type' => 'dialog',
|
||||||
|
'mode' => 'delete',
|
||||||
|
'data' => [
|
||||||
|
'id' => $this->id,
|
||||||
|
'dialog_id' => $this->dialog_id,
|
||||||
|
'last_msg' => $last_msg,
|
||||||
|
'update_read' => $deleteRead ? 1 : 0
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class WebSocketDialogMsgsAddDeletes extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('web_socket_dialog_msgs', 'deleted_at')) {
|
||||||
|
$table->softDeletes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
|
||||||
|
$table->dropSoftDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="`dialog-view ${msgData.type}`" :data-id="msgData.id">
|
<div :class="`dialog-view ${msgData.type}`" :data-id="msgData.id">
|
||||||
|
|
||||||
<!--文本-->
|
<div class="dialog-head">
|
||||||
<div v-if="msgData.type === 'text'" class="dialog-content">
|
<!--详情-->
|
||||||
<pre class="no-dark-mode">{{textMsg(msgData.msg.text)}}</pre>
|
<div class="dialog-content">
|
||||||
</div>
|
<!--文本-->
|
||||||
|
<div v-if="msgData.type === 'text'" class="content-text">
|
||||||
<!--等待-->
|
<pre class="no-dark-mode">{{textMsg(msgData.msg.text)}}</pre>
|
||||||
<div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div>
|
</div>
|
||||||
|
<!--文件-->
|
||||||
<!--文件-->
|
<div v-else-if="msgData.type === 'file'" :class="`content-file ${msgData.msg.type}`">
|
||||||
<div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]">
|
<div class="dialog-file">
|
||||||
<div class="dialog-file">
|
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb" @click="viewFile"/>
|
||||||
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb" @click="viewFile"/>
|
<div v-else class="file-box">
|
||||||
<div v-else class="file-box">
|
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
||||||
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
<div class="file-info">
|
||||||
<div class="file-info">
|
<div class="file-name">{{msgData.msg.name}}</div>
|
||||||
<div class="file-name">{{msgData.msg.name}}</div>
|
<div class="file-size">{{$A.bytesToSize(msgData.msg.size)}}</div>
|
||||||
<div class="file-size">{{$A.bytesToSize(msgData.msg.size)}}</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--等待-->
|
||||||
|
<div v-else-if="msgData.type === 'loading'" class="content-loading">
|
||||||
|
<Loading/>
|
||||||
|
</div>
|
||||||
|
<!--未知-->
|
||||||
|
<div v-else class="content-unknown">{{$L("未知的消息类型")}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-file-menu">
|
|
||||||
<div class="file-menu-warp"></div>
|
<!--菜单-->
|
||||||
<div class="file-menu-icon">
|
<div v-if="showMenu" class="dialog-menu">
|
||||||
<Icon @click="viewFile" type="md-eye" />
|
<div class="menu-icon">
|
||||||
<Icon @click="downFile" type="md-arrow-round-down" />
|
<Icon v-if="msgData.userid == userId" @click="withdraw" type="md-undo" :title="$L('撤回')"/>
|
||||||
|
<template v-if="msgData.type === 'file'">
|
||||||
|
<Icon @click="viewFile" type="md-eye" :title="$L('查看')"/>
|
||||||
|
<Icon @click="downFile" type="md-arrow-round-down" :title="$L('下载')"/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--未知-->
|
|
||||||
<div v-else class="dialog-content unknown">{{$L("未知的消息类型")}}</div>
|
|
||||||
|
|
||||||
<!--时间/阅读-->
|
<!--时间/阅读-->
|
||||||
<div v-if="msgData.created_at" class="dialog-foot">
|
<div v-if="msgData.created_at" class="dialog-foot">
|
||||||
<div class="time">{{$A.formatTime(msgData.created_at)}}</div>
|
<div class="time" :title="msgData.created_at">{{$A.formatTime(msgData.created_at)}}</div>
|
||||||
<Poptip
|
<Poptip
|
||||||
v-if="msgData.send > 1 || dialogType == 'group'"
|
v-if="msgData.send > 1 || dialogType == 'group'"
|
||||||
class="percent"
|
class="percent"
|
||||||
@ -95,7 +103,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['userToken']),
|
...mapState(['userToken', 'userId']),
|
||||||
|
|
||||||
readList() {
|
readList() {
|
||||||
return this.read_list.filter(({read_at}) => read_at)
|
return this.read_list.filter(({read_at}) => read_at)
|
||||||
@ -103,6 +111,10 @@ export default {
|
|||||||
|
|
||||||
unreadList() {
|
unreadList() {
|
||||||
return this.read_list.filter(({read_at}) => !read_at)
|
return this.read_list.filter(({read_at}) => !read_at)
|
||||||
|
},
|
||||||
|
|
||||||
|
showMenu() {
|
||||||
|
return this.msgData.userid == this.userId || this.msgData.type === 'file'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -176,6 +188,29 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
withdraw() {
|
||||||
|
$A.modalConfirm({
|
||||||
|
content: `确定撤回此信息吗?`,
|
||||||
|
okText: '撤回',
|
||||||
|
loading: true,
|
||||||
|
onOk: () => {
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'dialog/msg/withdraw',
|
||||||
|
data: {
|
||||||
|
msg_id: this.msgData.id
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
$A.messageSuccess("消息已撤回");
|
||||||
|
this.$store.dispatch("forgetDialogMsg", this.msgData.id);
|
||||||
|
this.$Modal.remove();
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
$A.messageError(msg, 301);
|
||||||
|
this.$Modal.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
viewFile() {
|
viewFile() {
|
||||||
if (this.$Electron) {
|
if (this.$Electron) {
|
||||||
this.$Electron.ipcRenderer.send('windowRouter', {
|
this.$Electron.ipcRenderer.send('windowRouter', {
|
||||||
|
16
resources/assets/js/store/actions.js
vendored
16
resources/assets/js/store/actions.js
vendored
@ -2053,6 +2053,22 @@ export default {
|
|||||||
case 'delete':
|
case 'delete':
|
||||||
// 删除消息
|
// 删除消息
|
||||||
dispatch("forgetDialogMsg", data.id)
|
dispatch("forgetDialogMsg", data.id)
|
||||||
|
//
|
||||||
|
let dialog = state.cacheDialogs.find(({id}) => id == data.dialog_id);
|
||||||
|
if (dialog) {
|
||||||
|
// 更新最后消息
|
||||||
|
dialog.last_at = data.last_msg && data.last_msg.created_at;
|
||||||
|
dialog.last_msg = data.last_msg;
|
||||||
|
if (data.update_read) {
|
||||||
|
// 更新未读数量
|
||||||
|
dispatch("call", {
|
||||||
|
url: 'dialog/msg/unread',
|
||||||
|
dialog_id: data.dialog_id
|
||||||
|
}).then(result => {
|
||||||
|
dialog.unread = result.data.unread
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'add':
|
case 'add':
|
||||||
case 'chat':
|
case 'chat':
|
||||||
|
4
resources/assets/sass/dark.scss
vendored
4
resources/assets/sass/dark.scss
vendored
@ -140,8 +140,10 @@ body.dark-mode-reverse {
|
|||||||
> li {
|
> li {
|
||||||
.dialog-view {
|
.dialog-view {
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
color: #ffffff;
|
|
||||||
background-color: #e1e1e1;
|
background-color: #e1e1e1;
|
||||||
|
.content-text {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.self {
|
&.self {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
.dialog-title {
|
.dialog-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -15,6 +16,7 @@
|
|||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
height: 68px;
|
height: 68px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -24,6 +26,7 @@
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: #f4f5f5;
|
background-color: #f4f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.completed {
|
&.completed {
|
||||||
&:after {
|
&:after {
|
||||||
content: "\f373";
|
content: "\f373";
|
||||||
@ -39,19 +42,23 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-title {
|
.main-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
.ivu-tag {
|
.ivu-tag {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 0 6px 0 0;
|
margin: 0 6px 0 0;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
|
|
||||||
&.ivu-tag-success {
|
&.ivu-tag-success {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ivu-icon {
|
.ivu-icon {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
@ -60,6 +67,7 @@
|
|||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> h2 {
|
> h2 {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -67,6 +75,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
> em {
|
> em {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
@ -74,24 +83,29 @@
|
|||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-title {
|
.sub-title {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: #aaaaaa;
|
color: #aaaaaa;
|
||||||
|
|
||||||
&.pointer {
|
&.pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #888888;
|
color: #888888;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-scroller {
|
.dialog-scroller {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.dialog-list {
|
.dialog-list {
|
||||||
> ul {
|
> ul {
|
||||||
> li {
|
> li {
|
||||||
@ -100,9 +114,11 @@
|
|||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-avatar {
|
.dialog-avatar {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@ -110,36 +126,137 @@
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-view {
|
.dialog-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin: 0 0 0 8px;
|
margin: 0 0 0 8px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.text {
|
&.text {
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
}
|
}
|
||||||
.dialog-content {
|
|
||||||
color: #333333;
|
&:hover {
|
||||||
background-color: #F4F5F7;
|
.dialog-head {
|
||||||
padding: 8px;
|
.dialog-menu {
|
||||||
min-width: 32px;
|
opacity: 1;
|
||||||
border-radius: 6px 6px 6px 0;
|
|
||||||
.dialog-file-menu {
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.3s;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: -8px;
|
|
||||||
.file-menu-warp {
|
|
||||||
width: 100%;
|
|
||||||
height: 12px;
|
|
||||||
}
|
}
|
||||||
.file-menu-icon {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
background-color: #F4F5F7;
|
||||||
|
padding: 8px;
|
||||||
|
min-width: 32px;
|
||||||
|
border-radius: 6px 6px 6px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
color: #333333;
|
||||||
|
> pre {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-file {
|
||||||
|
&.file {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
.file-box {
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 220px;
|
||||||
|
|
||||||
|
.file-thumb {
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
margin-left: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
color: #333333;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
word-break: break-all;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-size {
|
||||||
|
padding-top: 4px;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.img {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
max-width: 220px;
|
||||||
|
max-height: 220px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: transparent;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.file-img {
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-loading {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.common-loading {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-unknown {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-menu {
|
||||||
|
opacity: 0;
|
||||||
|
margin-left: 6px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
|
background-color: #ffffff;
|
||||||
|
|
||||||
> i {
|
> i {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -147,105 +264,37 @@
|
|||||||
color: #999;
|
color: #999;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
& + i {
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
&+i {
|
|
||||||
border-left: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> pre {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.dialog-file-menu {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.loading {
|
|
||||||
display: flex;
|
|
||||||
.common-loading {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.file {
|
|
||||||
display: inline-block;
|
|
||||||
.file-box {
|
|
||||||
background-color: #ffffff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 220px;
|
|
||||||
.file-thumb {
|
|
||||||
width: 36px;
|
|
||||||
}
|
|
||||||
.file-info {
|
|
||||||
margin-left: 12px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
.file-name {
|
|
||||||
color: #333333;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 18px;
|
|
||||||
word-break: break-all;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
.file-size {
|
|
||||||
padding-top: 4px;
|
|
||||||
color: #666666;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.img {
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
max-width: 220px;
|
|
||||||
max-height: 220px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: transparent;
|
|
||||||
overflow: hidden;
|
|
||||||
.file-img {
|
|
||||||
display: flex;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.unknown {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-foot {
|
.dialog-foot {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
height: 21px;
|
height: 21px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
||||||
.common-loading {
|
.common-loading {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
color: #bbbbbb;
|
color: #bbbbbb;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.done {
|
.done {
|
||||||
display: none;
|
display: none;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
@ -253,6 +302,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.percent {
|
.percent {
|
||||||
display: none;
|
display: none;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
@ -260,6 +310,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-action {
|
.dialog-action {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -271,6 +322,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.history {
|
&.history {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -279,13 +331,16 @@
|
|||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.history-tip {
|
&.history-tip {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 60px;
|
padding-top: 60px;
|
||||||
|
|
||||||
.history-text {
|
.history-text {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -301,15 +356,18 @@
|
|||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.loading {
|
&.loading {
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
.common-loading {
|
.common-loading {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.nothing {
|
&.nothing {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@ -317,32 +375,54 @@
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.bottom {
|
&.bottom {
|
||||||
height: 0;
|
height: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.self {
|
&.self {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
.dialog-view {
|
.dialog-view {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
margin: 0 8px 0 0;
|
margin: 0 8px 0 0;
|
||||||
.dialog-content {
|
|
||||||
color: #ffffff;
|
.dialog-head {
|
||||||
background-color: $primary-color;
|
flex-direction: row-reverse;
|
||||||
border-radius: 6px 6px 0 6px;
|
|
||||||
&.file {
|
.dialog-content {
|
||||||
background-color: #F4F5F7;
|
background-color: $primary-color;
|
||||||
|
border-radius: 6px 6px 0 6px;
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-file {
|
||||||
|
&.file {
|
||||||
|
background-color: #F4F5F7;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.img {
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.img {
|
|
||||||
border-radius: 6px;
|
.dialog-menu {
|
||||||
background-color: transparent;
|
margin-left: 0;
|
||||||
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-foot {
|
.dialog-foot {
|
||||||
.done {
|
.done {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.percent {
|
.percent {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@ -353,6 +433,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -360,6 +441,7 @@
|
|||||||
padding: 0 28px;
|
padding: 0 28px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.dialog-newmsg {
|
.dialog-newmsg {
|
||||||
display: none;
|
display: none;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
@ -374,19 +456,23 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 2;;
|
z-index: 2;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-input {
|
.dialog-input {
|
||||||
background-color: #F4F5F7;
|
background-color: #F4F5F7;
|
||||||
padding: 10px 52px 10px 12px;
|
padding: 10px 52px 10px 12px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
||||||
.ivu-input {
|
.ivu-input {
|
||||||
border: 0;
|
border: 0;
|
||||||
resize: none;
|
resize: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-send {
|
.dialog-send {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -398,19 +484,23 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-upload {
|
.chat-upload {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.newmsg {
|
&.newmsg {
|
||||||
margin-top: -50px;
|
margin-top: -50px;
|
||||||
|
|
||||||
.dialog-newmsg {
|
.dialog-newmsg {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.drag-over {
|
.drag-over {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -422,6 +512,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -432,6 +523,7 @@
|
|||||||
border: 2px dashed #7b7b7b;
|
border: 2px dashed #7b7b7b;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drag-text {
|
.drag-text {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -439,23 +531,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-wrapper-read-poptip-content {
|
.dialog-wrapper-read-poptip-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.read,
|
.read,
|
||||||
.unread {
|
.unread {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
> li {
|
> li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
|
||||||
.common-avatar {
|
.common-avatar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.read-title {
|
&.read-title {
|
||||||
> em {
|
> em {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -466,11 +564,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.unread {
|
.unread {
|
||||||
> li {
|
> li {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -484,10 +584,12 @@
|
|||||||
|
|
||||||
.dialog-wrapper-paste {
|
.dialog-wrapper-paste {
|
||||||
margin-top: -4px;
|
margin-top: -4px;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 1000px;
|
max-height: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> div,
|
> div,
|
||||||
> img {
|
> img {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -502,6 +604,7 @@
|
|||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
.dialog-send {
|
.dialog-send {
|
||||||
right: 20px;
|
right: 20px;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
min-height: 120px;
|
min-height: 120px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
.task-info {
|
.task-info {
|
||||||
flex: 1;
|
flex: 3;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -459,6 +459,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task-dialog {
|
.task-dialog {
|
||||||
|
flex: 2;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user