feat: 新增消息类型筛选

This commit is contained in:
kuaifan 2022-06-30 19:54:48 +08:00
parent a368e00b2f
commit 49f8e23aae
6 changed files with 187 additions and 18 deletions

View File

@ -214,7 +214,7 @@ class DialogController extends AbstractController
* @apiName msg__lists
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {String} [position_id] 定位消息ID填写时page无效
* @apiParam {Number} [position_id] 定位消息ID填写时page无效
*
* @apiParam {Number} [page] 当前页,默认:1
* @apiParam {Number} [pagesize] 每页显示数量,默认:50,最大:100
@ -284,6 +284,13 @@ class DialogController extends AbstractController
* @apiParam {Number} [prev_id] 此消息ID之前的数据
* @apiParam {Number} [next_id] 此消息ID之后的数据
* - position_id、prev_id、next_id 只有一个有效优先循序为position_id > prev_id > next_id
* @apiParam {String} [mtype] 消息类型
* - tag: 标记
* - text: 文本
* - image: 图片
* - file: 文件
* - record: 录音
* - meeting: 会议
*
* @apiParam {Number} [take] 获取条数,默认:50,最大:100
*
@ -301,6 +308,7 @@ class DialogController extends AbstractController
$position_id = intval(Request::input('position_id'));
$prev_id = intval(Request::input('prev_id'));
$next_id = intval(Request::input('next_id'));
$mtype = trim(Request::input('mtype'));
$take = Base::getPaginate(100, 50, 'take');
$data = [];
//
@ -317,6 +325,11 @@ class DialogController extends AbstractController
->on('read.msg_id', '=', 'web_socket_dialog_msgs.id');
})->where('web_socket_dialog_msgs.dialog_id', $dialog_id);
//
if ($mtype === 'tag') {
$builder->where('tag', '>', 0);
} elseif (in_array($mtype, ['text', 'image', 'file', 'record', 'meeting'])) {
$builder->whereMtype($mtype);
}
if ($msg_id > 0) {
$builder->whereReplyId($msg_id);
$reDialog = false;

View File

@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $dialog_type 对话类型
* @property int|null $userid 发送会员ID
* @property string|null $type 消息类型
* @property string|null $mtype 消息类型(用于搜索)
* @property array|mixed $msg 详细消息
* @property array|mixed $emoji emoji回复
* @property string|null $key 搜索关键词
@ -44,6 +45,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereKey($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMsg($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMtype($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereRead($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereReplyId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereReplyNum($value)
@ -519,11 +521,20 @@ class WebSocketDialogMsg extends AbstractModel
*/
public static function sendMsg($dialog_id, $reply_id, $type, $msg, $sender = 0)
{
$mtype = $type;
if ($type === 'text' && str_contains($msg['text'], '<img ')) {
$mtype = 'image';
}
if ($type === 'file' && in_array($msg['ext'], ['jpg', 'jpeg', 'png', 'gif'])) {
$mtype = 'image';
}
//
$dialogMsg = self::createInstance([
'dialog_id' => $dialog_id,
'reply_id' => $reply_id,
'userid' => $sender ?: User::userid(),
'type' => $type,
'mtype' => $mtype,
'msg' => $msg,
'read' => 0,
]);

View File

@ -0,0 +1,56 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogMsgsMtype extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$isAdd = false;
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) use (&$isAdd) {
if (!Schema::hasColumn('web_socket_dialog_msgs', 'mtype')) {
$isAdd = true;
$table->string('mtype', 20)->nullable()->default('')->after('type')->comment('消息类型(用于搜索)');
}
});
if ($isAdd) {
DB::table('web_socket_dialog_msgs')->update([
'mtype' => DB::raw('type')
]);
DB::table('web_socket_dialog_msgs')->where('type', 'text')->where('msg', 'LIKE', '%<img %')->update([
'mtype' => 'image'
]);
DB::table('web_socket_dialog_msgs')->where('type', 'file')->where('msg', 'LIKE', '%.jpg"%')->update([
'mtype' => 'image'
]);
DB::table('web_socket_dialog_msgs')->where('type', 'file')->where('msg', 'LIKE', '%.jpeg"%')->update([
'mtype' => 'image'
]);
DB::table('web_socket_dialog_msgs')->where('type', 'file')->where('msg', 'LIKE', '%.png"%')->update([
'mtype' => 'image'
]);
DB::table('web_socket_dialog_msgs')->where('type', 'file')->where('msg', 'LIKE', '%.gif"%')->update([
'mtype' => 'image'
]);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
$table->dropColumn("mtype");
});
}
}

View File

@ -11,7 +11,7 @@
<!--顶部导航-->
<div class="dialog-nav" :style="navStyle">
<slot name="head">
<div class="nav-wrapper" :class="{completed:$A.dialogCompleted(dialogData)}">
<div class="nav-wrapper" :class="{completed: $A.dialogCompleted(dialogData)}">
<div class="dialog-back" @click="onBack">
<i class="taskfont">&#xe72d;</i>
<div v-if="msgUnreadOnly" class="back-num">{{msgUnreadOnly}}</div>
@ -64,6 +64,16 @@
<i class="taskfont dialog-create" @click="openCreateGroup">&#xe646;</i>
</ETooltip>
</div>
<ul class="nav-tags">
<li
v-for="item in msgTags"
:key="item.type"
:class="{active: msgType === item.type}"
@click="msgType=item.type">
<i class="taskfont" v-html="item.icon"></i>
<span>{{$L(item.label)}}</span>
</li>
</ul>
</slot>
</div>
@ -78,6 +88,7 @@
:item-class-add="itemClassAdd"
:extra-props="{dialogData, operateVisible, operateItem, hidePercentage: isMyDialog, hideReply: msgId > 0}"
:estimate-size="78"
:top-threshold="120"
:keeps="70"
@scroll="onScroll"
@range="onRange"
@ -330,6 +341,14 @@ export default {
msgText: '',
msgNew: 0,
msgType: '',
msgTags: [
{icon: '&#xe6eb;', type: '', label: '消息'},
{icon: '&#xe61e;', type: 'tag', label: '标注'},
{icon: '&#xe7bc;', type: 'image', label: '图片'},
{icon: '&#xe7c0;', type: 'file', label: '文件'},
],
allMsgs: [],
tempMsgs: [],
@ -408,12 +427,7 @@ export default {
if (!this.isReady) {
return [];
}
return this.dialogMsgs.filter(item => {
if (this.msgId) {
return item.reply_id == this.msgId;
}
return item.dialog_id == this.dialogId;
}).sort((a, b) => {
return this.dialogMsgs.filter(item => this.msgFilter(item)).sort((a, b) => {
return a.id - b.id;
});
},
@ -422,12 +436,7 @@ export default {
if (!this.isReady) {
return [];
}
return this.tempMsgs.filter(item => {
if (this.msgId) {
return item.reply_id == this.msgId;
}
return item.dialog_id == this.dialogId;
});
return this.tempMsgs.filter(item => this.msgFilter(item));
},
allMsgList() {
@ -442,7 +451,7 @@ export default {
},
loadMsg() {
return this.isLoad(`msg::${this.dialogId}-${this.msgId}`)
return this.isLoad(`msg::${this.dialogId}-${this.msgId}-${this.msgType}`)
},
prevId() {
@ -534,6 +543,7 @@ export default {
this.allMsgs = this.allMsgList;
requestAnimationFrame(this.onToBottom);
}
this.msgType = '';
this.$store.dispatch("getDialogMsgs", {
dialog_id,
msg_id: this.msgId
@ -555,6 +565,15 @@ export default {
immediate: true
},
msgType(type) {
if (!type) return
this.$store.dispatch("getDialogMsgs", {
dialog_id: this.dialogId,
msg_id: this.msgId,
msg_type: this.msgType,
}).catch(_ => {});
},
dialogSearchMsgId() {
this.onSearchMsgId();
},
@ -582,6 +601,7 @@ export default {
this.$store.dispatch("getDialogMsgs", {
dialog_id: this.dialogId,
msg_id: this.msgId,
msg_type: this.msgType,
}).catch(_ => {});
},
@ -644,6 +664,7 @@ export default {
}
msgText = msgText.replace(/<\/span> <\/p>$/, "</span></p>")
//
this.msgType = '';
this.onToBottom();
this.onActive();
//
@ -687,6 +708,7 @@ export default {
* @param msg {base64, duration}
*/
sendRecord(msg) {
this.msgType = '';
this.onToBottom();
this.onActive();
//
@ -724,6 +746,7 @@ export default {
sendFileMsg(row) {
const files = $A.isArray(row) ? row : [row];
if (files.length > 0) {
this.msgType = '';
this.pasteFile = [];
this.pasteItem = [];
files.some(file => {
@ -751,6 +774,24 @@ export default {
}
},
msgFilter(item) {
if (this.msgType) {
if (this.msgType === 'tag') {
if (!item.tag) {
return false
}
} else if (this.msgType !== item.mtype) {
return false
}
}
if (this.msgId) {
if (item.reply_id != this.msgId) {
return false
}
}
return item.dialog_id == this.dialogId;
},
onSearchMsgId() {
if (this.dialogSearchMsgId > 0 && this.openId === this.dialogId) {
this.onPositionId(this.dialogSearchMsgId)
@ -772,6 +813,7 @@ export default {
delay: 600
})
}
this.msgType = '';
this.preventToBottom = true;
this.$store.dispatch("getDialogMsgs", {
dialog_id: this.dialogId,
@ -1008,6 +1050,7 @@ export default {
this.$store.dispatch('getDialogMsgs', {
dialog_id: this.dialogId,
msg_id: this.msgId,
msg_type: this.msgType,
prev_id: this.prevId
}).then(({data}) => {
const ids = data.list.map(item => item.id)
@ -1109,6 +1152,7 @@ export default {
this.$store.dispatch("getDialogMsgs", {
dialog_id: this.dialogId,
msg_id: this.msgId,
msg_type: this.msgType,
[key]: rangeValue,
}).finally(_ => {
this.preventMoreLoad = false

View File

@ -2170,7 +2170,7 @@ export default {
* @param state
* @param dispatch
* @param getters
* @param data {dialog_id, msg_id, ?position_id, ?prev_id, ?next_id}
* @param data {dialog_id, msg_id, ?msg_type, ?position_id, ?prev_id, ?next_id}
* @returns {Promise<unknown>}
*/
getDialogMsgs({state, dispatch, getters}, data) {
@ -2181,7 +2181,7 @@ export default {
return;
}
//
const loadKey = `msg::${data.dialog_id}-${data.msg_id}`
const loadKey = `msg::${data.dialog_id}-${data.msg_id}-${data.msg_type || ''}`
if (getters.isLoad(loadKey)) {
reject({msg: 'Loading'});
return

View File

@ -207,12 +207,54 @@
display: none;
}
}
.nav-tags {
position: absolute;
top: 78px;
left: 0;
width: 100%;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
> li {
list-style: none;
display: flex;
align-items: center;
margin: 0 8px;
padding: 3px 8px;
background-color: #c0c4cc;
color: #fff;
border-radius: 6px;
font-size: 12px;
cursor: pointer;
box-shadow: 0 1px 6px rgba(255, 255, 255, 0.2);
.taskfont {
font-size: 13px;
padding-right: 6px;
}
> span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&.active {
background-color: $primary-color;
}
}
}
}
.dialog-scroller {
flex: 1;
position: relative;
padding: 16px 32px 0;
padding: 48px 32px 0;
.dialog-shake {
animation: ani-dialog-shake 600ms ease-in-out;
@ -1232,6 +1274,9 @@
}
}
}
.nav-tags {
top: 60px;
}
}
.dialog-scroller {
padding-right: 14px;