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
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {Number} msg_id 消息ID
* @apiParam {Number} [msg_id] 消息ID
* @apiParam {Number} [position_id] 此消息ID前后的数据
* @apiParam {Number} [prev_id] 此消息ID之前的数据
* @apiParam {Number} [next_id] 此消息ID之后的数据
@ -492,6 +492,43 @@ class DialogController extends AbstractController
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. 获取单条消息
*

View File

@ -74,6 +74,9 @@
@command="onDialogMenu">
<i class="taskfont dialog-menu-icon">&#xe6e9;</i>
<EDropdownMenu v-slot="dropdown">
<EDropdownItem command="searchMsg">
<div>{{$L('搜索消息')}}</div>
</EDropdownItem>
<EDropdownItem v-if="dialogData.type === 'user'" command="openCreate">
<div>{{$L('创建群组')}}</div>
</EDropdownItem>
@ -103,11 +106,29 @@
</template>
</EDropdownMenu>
</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>
</slot>
</div>
<!--顶部提示-->
<!--跳转提示-->
<div v-if="positionMsg" class="dialog-position" :class="{'down': tagShow}">
<div class="position-label" @click="onPositionMark">
<Icon v-if="positionLoad > 0" type="ios-loading" class="icon-loading"></Icon>
@ -523,6 +544,12 @@ export default {
pasteFile: [],
pasteItem: [],
searchShow: false,
searchKey: '',
searchLoad: 0,
searchLocation: 1,
searchResult: [],
createGroupShow: false,
createGroupData: {},
createGroupLoad: 0,
@ -759,7 +786,7 @@ export default {
},
tagShow() {
return this.msgTags.length > 1 && this.windowScrollY === 0
return this.msgTags.length > 1 && this.windowScrollY === 0 && !this.searchShow
},
scrollerClass() {
@ -900,6 +927,46 @@ export default {
}).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() {
this.onSearchMsgId();
},
@ -1645,6 +1712,13 @@ export default {
onDialogMenu(cmd) {
switch (cmd) {
case "searchMsg":
this.searchShow = true
this.$nextTick(_ => {
this.$refs.searchInput.focus()
})
break;
case "openCreate":
const userids = [this.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() {
if (this.positionLoad > 0) {
return;

View File

@ -346,6 +346,70 @@
display: none;
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;
}
}
}
}