feat: 搜索会话消息

This commit is contained in:
kuaifan 2023-03-09 03:05:42 +08:00
parent f34a391aec
commit 032b02c1ec
3 changed files with 209 additions and 3 deletions

View File

@ -370,7 +370,7 @@ class DialogController extends AbstractController
* @apiName msg__list * @apiName msg__list
* *
* @apiParam {Number} dialog_id 对话ID * @apiParam {Number} dialog_id 对话ID
* @apiParam {Number} msg_id 消息ID * @apiParam {Number} [msg_id] 消息ID
* @apiParam {Number} [position_id] 此消息ID前后的数据 * @apiParam {Number} [position_id] 此消息ID前后的数据
* @apiParam {Number} [prev_id] 此消息ID之前的数据 * @apiParam {Number} [prev_id] 此消息ID之前的数据
* @apiParam {Number} [next_id] 此消息ID之后的数据 * @apiParam {Number} [next_id] 此消息ID之后的数据
@ -492,6 +492,43 @@ class DialogController extends AbstractController
return Base::retSuccess('success', $data); return Base::retSuccess('success', $data);
} }
/**
* @api {get} api/dialog/msg/search 10. 搜索消息位置
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__search
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {String} key 搜索关键词
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__search()
{
User::auth();
//
$dialog_id = intval(Request::input('dialog_id'));
$key = trim(Request::input('key'));
//
if (empty($key)) {
return Base::retError('关键词不能为空');
}
//
WebSocketDialog::checkDialog($dialog_id);
//
$data = WebSocketDialogMsg::whereDialogId($dialog_id)
->where('key', 'LIKE', "%{$key}%")
->take(200)
->pluck('id');
return Base::retSuccess('success', [
'data' => $data
]);
}
/** /**
* @api {get} api/dialog/msg/one 10. 获取单条消息 * @api {get} api/dialog/msg/one 10. 获取单条消息
* *

View File

@ -74,6 +74,9 @@
@command="onDialogMenu"> @command="onDialogMenu">
<i class="taskfont dialog-menu-icon">&#xe6e9;</i> <i class="taskfont dialog-menu-icon">&#xe6e9;</i>
<EDropdownMenu v-slot="dropdown"> <EDropdownMenu v-slot="dropdown">
<EDropdownItem command="searchMsg">
<div>{{$L('搜索消息')}}</div>
</EDropdownItem>
<EDropdownItem v-if="dialogData.type === 'user'" command="openCreate"> <EDropdownItem v-if="dialogData.type === 'user'" command="openCreate">
<div>{{$L('创建群组')}}</div> <div>{{$L('创建群组')}}</div>
</EDropdownItem> </EDropdownItem>
@ -103,11 +106,29 @@
</template> </template>
</EDropdownMenu> </EDropdownMenu>
</EDropdown> </EDropdown>
<!--搜索框-->
<div v-if="searchShow" class="dialog-search">
<div class="search-location">
<i class="taskfont" @click="onSearchSwitch('prev')">&#xe702;</i>
<i class="taskfont" @click="onSearchSwitch('next')">&#xe705;</i>
</div>
<div class="search-input">
<Input ref="searchInput" v-model="searchKey" :placeholder="$L('搜索消息')" @on-keyup="onSearchKeyup" clearable>
<div class="search-pre" slot="prefix">
<Loading v-if="searchLoad > 0"/>
<Icon v-else type="ios-search" />
</div>
</Input>
<div v-if="searchLoad === 0 && searchResult.length > 0" class="search-total" slot="append">{{searchLocation}}/{{searchResult.length}}</div>
</div>
<div class="search-cancel" @click="onSearchKeyup(null)">{{$L('取消')}}</div>
</div>
</div> </div>
</slot> </slot>
</div> </div>
<!--顶部提示--> <!--跳转提示-->
<div v-if="positionMsg" class="dialog-position" :class="{'down': tagShow}"> <div v-if="positionMsg" class="dialog-position" :class="{'down': tagShow}">
<div class="position-label" @click="onPositionMark"> <div class="position-label" @click="onPositionMark">
<Icon v-if="positionLoad > 0" type="ios-loading" class="icon-loading"></Icon> <Icon v-if="positionLoad > 0" type="ios-loading" class="icon-loading"></Icon>
@ -523,6 +544,12 @@ export default {
pasteFile: [], pasteFile: [],
pasteItem: [], pasteItem: [],
searchShow: false,
searchKey: '',
searchLoad: 0,
searchLocation: 1,
searchResult: [],
createGroupShow: false, createGroupShow: false,
createGroupData: {}, createGroupData: {},
createGroupLoad: 0, createGroupLoad: 0,
@ -759,7 +786,7 @@ export default {
}, },
tagShow() { tagShow() {
return this.msgTags.length > 1 && this.windowScrollY === 0 return this.msgTags.length > 1 && this.windowScrollY === 0 && !this.searchShow
}, },
scrollerClass() { scrollerClass() {
@ -900,6 +927,46 @@ export default {
}).catch(_ => {}) }).catch(_ => {})
}, },
searchKey(key) {
if (!key) {
return
}
this.searchLoad++
setTimeout(_ => {
if (this.searchKey === key) {
this.searchLoad++
this.searchResult = []
this.searchLocation = 0
this.$store.dispatch("call", {
url: 'dialog/msg/search',
data: {
dialog_id: this.dialogId,
key,
},
}).then(({data}) => {
if (this.searchKey !== key) {
return
}
this.searchResult = data.data
this.searchLocation = this.searchResult.length
}).finally(_ => {
this.searchLoad--
});
}
this.searchLoad--
}, 600)
},
searchLocation(position) {
if (position === 0) {
return
}
const id = this.searchResult[position - 1]
if (id) {
this.onPositionId(id)
}
},
dialogSearchMsgId() { dialogSearchMsgId() {
this.onSearchMsgId(); this.onSearchMsgId();
}, },
@ -1645,6 +1712,13 @@ export default {
onDialogMenu(cmd) { onDialogMenu(cmd) {
switch (cmd) { switch (cmd) {
case "searchMsg":
this.searchShow = true
this.$nextTick(_ => {
this.$refs.searchInput.focus()
})
break;
case "openCreate": case "openCreate":
const userids = [this.userId] const userids = [this.userId]
if (this.dialogData.dialog_user && this.userId != this.dialogData.dialog_user.userid) { if (this.dialogData.dialog_user && this.userId != this.dialogData.dialog_user.userid) {
@ -2423,6 +2497,37 @@ export default {
} }
}, },
onSearchSwitch(type) {
if (this.searchResult.length === 0) {
return
}
if (this.searchLocation === 1 && this.searchResult.length === 1) {
this.onPositionId(this.searchResult[0])
return
}
if (type === 'prev') {
if (this.searchLocation <= 1) {
this.searchLocation = this.searchResult.length
} else {
this.searchLocation--
}
} else {
if (this.searchLocation >= this.searchResult.length) {
this.searchLocation = 1
} else {
this.searchLocation++
}
}
},
onSearchKeyup(e) {
if (e === null || e.keyCode === 27) {
this.searchShow = false
this.searchKey = ''
this.searchResult = []
}
},
onPositionMark() { onPositionMark() {
if (this.positionLoad > 0) { if (this.positionLoad > 0) {
return; return;

View File

@ -346,6 +346,70 @@
display: none; display: none;
cursor: pointer; cursor: pointer;
} }
.dialog-search {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 1px;
z-index: 1;
background-color: #ffffff;
display: flex;
align-items: center;
.search-location {
margin-left: 14px;
display: flex;
align-items: center;
> i {
cursor: pointer;
font-size: 18px;
padding: 0 6px;
}
}
.search-input {
flex: 1;
margin-left: 12px;
border-radius: 12px;
background-color: #F7F7F7;
overflow: hidden;
position: relative;
display: flex;
align-items: center;
.search-pre {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.common-loading {
width: 14px;
height: 14px;
margin: 0;
}
}
.ivu-input {
border-color: transparent;
background-color: transparent;
&:hover,
&:focus {
box-shadow: none;
}
}
.search-total {
padding-right: 12px;
}
}
.search-cancel {
cursor: pointer;
padding: 0 18px;
color: $primary-color;
}
}
} }
} }