11
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view class="scan_page">
|
||||
<uni-nav-bar left-icon="back" fixed backgroundColor="transparent" statusBar></uni-nav-bar>
|
||||
<barcode id='1' class="barcode" autostart="true" ref="barcode" background="rgb(0,0,0)" frameColor="#1C86EE" scanbarColor="#1C86EE" :filters="fil" @marked="success1" @error="fail1"></barcode>
|
||||
<view class="overlay">
|
||||
<button class="btn" @click="toStart">开始扫码识别</button>
|
||||
<button class="btn" @click="tocancel">取消扫码识别</button>
|
||||
<button class="btn" @click="toFlash">开启闪光灯</button>
|
||||
<button class="btn" @click="toscan">预览</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
onLoad() {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fil: [0, 2, 1]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
success1(e) {
|
||||
console.log("success1:" + JSON.stringify(e));
|
||||
},
|
||||
fail1(e) {
|
||||
console.log("fail1:" + JSON.stringify(e));
|
||||
},
|
||||
toStart: function() {
|
||||
this.$refs.barcode.start({
|
||||
conserve: true,
|
||||
filename: '_doc/barcode/'
|
||||
});
|
||||
},
|
||||
tocancel:function(){
|
||||
this.$refs.barcode.cancel();
|
||||
},
|
||||
toFlash: function() {
|
||||
this.$refs.barcode.setFlash(true);
|
||||
},
|
||||
|
||||
toscan: function() {
|
||||
console.log("scan:");
|
||||
const barcodeModule = uni.requireNativePlugin('barcodeScan');
|
||||
barcodeModule.scan("/static/barcode1.png"
|
||||
,(e)=>{
|
||||
console.log("scan_error:"+JSON.stringify(e));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scan_page{
|
||||
width: 750rpx;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background: #800000;
|
||||
.barcode {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.btn {
|
||||
top: 20rpx;
|
||||
width: 730rpx;
|
||||
margin-left: 10rpx;
|
||||
margin-top: 10rpx;
|
||||
background-color: #458B00;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
.overlay{
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<view class="message-search-item" @click="handleClick">
|
||||
<view class="message-content">
|
||||
<view class="message-meta">
|
||||
<text class="sender-name" v-if="!isSelf && !isGroup">{{ message.senderNickname }}</text>
|
||||
<text class="message-time">{{ formattedTime }}</text>
|
||||
</view>
|
||||
<view class="message-preview">
|
||||
<text class="message-text">{{ messagePreview }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from "vuex";
|
||||
import MyAvatar from "@/components/MyAvatar/index.vue";
|
||||
import {MessageType, SessionType} from "openim-uniapp-polyfill";
|
||||
import {formatMessageTime, parseMessageByType} from "@/util/imCommon";
|
||||
|
||||
export default {
|
||||
name: "MessageSearchItem",
|
||||
components: {
|
||||
MyAvatar
|
||||
},
|
||||
props: {
|
||||
message: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
conversation: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
"storeSelfInfo"
|
||||
]),
|
||||
isGroup() {
|
||||
if(this.conversation){
|
||||
return this.conversation.conversationType === SessionType.WorkingGroup || this.conversation.conversationType === 2;
|
||||
}
|
||||
return this.message.sessionType === SessionType.WorkingGroup || this.message.sessionType === 2;
|
||||
},
|
||||
isSelf() {
|
||||
return this.message.sendID === this.storeSelfInfo?.userID;
|
||||
},
|
||||
formattedTime() {
|
||||
return formatMessageTime(this.message.sendTime);
|
||||
},
|
||||
messagePreview() {
|
||||
// 根据消息类型显示预览
|
||||
switch(this.message.contentType) {
|
||||
case MessageType.TextMessage:
|
||||
return this.message.textElem?.content || '[文本消息]';
|
||||
case MessageType.FileMessage:
|
||||
return `[文件] ${this.message.fileElem?.fileName || '未知文件'}`;
|
||||
case MessageType.LocationMessage:
|
||||
return `[位置] ${this.message.locationElem?.description || '位置信息'}`;
|
||||
case MessageType.PictureMessage:
|
||||
return '[图片]';
|
||||
case MessageType.VideoMessage:
|
||||
return '[视频]';
|
||||
case MessageType.SoundMessage:
|
||||
return '[语音]';
|
||||
default:
|
||||
return parseMessageByType(this.message) || '[消息]';
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.$emit('click', {
|
||||
message: this.message,
|
||||
conversation: this.conversation
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.message-search-item {
|
||||
background-color: #fff;
|
||||
padding: 20rpx 44rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
.message-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.sender-name {
|
||||
font-size: 26rpx;
|
||||
color: #1d6bed;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.message-preview {
|
||||
.message-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
@include ellipsisWithLine(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<view style="display: flex;flex-direction: column;background-color: #ececec;">
|
||||
<u-status-bar bgColor="transparent"></u-status-bar>
|
||||
<uni-search-bar class="search_bar"
|
||||
:focus="true"
|
||||
bgColor="#fff"
|
||||
cancelButton="always"
|
||||
:placeholder="getPlaceholder"
|
||||
v-model="searchValue"
|
||||
@confirm="search"
|
||||
@input="input"
|
||||
@cancel="cancel"
|
||||
@clear="clear">
|
||||
</uni-search-bar>
|
||||
<u-tabs :list="searchTypeList" :current="currentTabIndex" @change="changeSearchType"></u-tabs>
|
||||
<scroll-view scroll-y="true" class="scroll-view">
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="status === 'loading'" class="empty-container">
|
||||
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
|
||||
<text class="empty-text">搜索中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else-if="status === 'done' && isEmpty" class="empty-container">
|
||||
<text class="empty-text">暂无搜索结果</text>
|
||||
</view>
|
||||
|
||||
<!-- 等待搜索状态 -->
|
||||
<view v-else-if="status === 'wait'" class="empty-container">
|
||||
<text class="empty-text">请输入关键词进行搜索</text>
|
||||
</view>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<view v-else>
|
||||
<!-- 会话搜索结果 -->
|
||||
<ConversationItem
|
||||
v-if="currentSearchType === 'conversation'"
|
||||
:source="conversation"
|
||||
:key="index"
|
||||
v-for="(conversation,index) in conversationList"
|
||||
></ConversationItem>
|
||||
|
||||
<!-- 好友搜索结果 -->
|
||||
<user-item
|
||||
v-if="currentSearchType === 'friend'"
|
||||
@itemClick="userClick"
|
||||
@updateCheck="()=>{}"
|
||||
:checked="false"
|
||||
:disabled="false"
|
||||
:checkVisible="false"
|
||||
v-for="cell in friendList"
|
||||
:item="cell"
|
||||
:key="cell.userID"
|
||||
/>
|
||||
|
||||
<!-- 消息搜索结果 -->
|
||||
<view v-if="['message', 'file', 'location'].includes(currentSearchType)">
|
||||
<view
|
||||
v-for="(resultItem, resultIndex) in messageList"
|
||||
:key="resultIndex"
|
||||
class="conversation-group"
|
||||
>
|
||||
<view class="conversation-header" @click="goToConversation(resultItem)">
|
||||
<my-avatar
|
||||
:src="resultItem.faceURL"
|
||||
:desc="resultItem.showName"
|
||||
:isGroup="resultItem.conversationType === 2"
|
||||
size="36"
|
||||
/>
|
||||
<text class="conversation-title">{{ resultItem.showName }}</text>
|
||||
<text class="message-count">{{ resultItem.messageCount }}条相关消息</text>
|
||||
</view>
|
||||
<message-search-item
|
||||
v-for="(message, msgIndex) in resultItem.messageList"
|
||||
:key="msgIndex"
|
||||
:message="message"
|
||||
:conversation="resultItem"
|
||||
@click="messageClick"
|
||||
></message-search-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from "vuex";
|
||||
import ConversationItem from "@/pages/conversation/conversationList/components/ConversationItem.vue";
|
||||
import UserItem from "@/components/UserItem/index.vue";
|
||||
import MessageSearchItem from "./components/MessageSearchItem.vue";
|
||||
import MyAvatar from "@/components/MyAvatar/index.vue";
|
||||
import IMSDK,{MessageType, SessionType} from "openim-uniapp-polyfill";
|
||||
import {prepareConversationState, navigateToDesignatedConversation} from "@/util/imCommon";
|
||||
|
||||
export default {
|
||||
components:{
|
||||
ConversationItem,
|
||||
UserItem,
|
||||
MessageSearchItem,
|
||||
MyAvatar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchValue:"",
|
||||
currentSearchType:"message", // conversation, friend, message, file, location
|
||||
searchTypeList:[
|
||||
{
|
||||
name: '会话',
|
||||
value: 'conversation'
|
||||
},
|
||||
{
|
||||
name: '好友',
|
||||
value: 'friend'
|
||||
},
|
||||
{
|
||||
name: '消息',
|
||||
value: 'message'
|
||||
},
|
||||
{
|
||||
name: '文件',
|
||||
value: 'file'
|
||||
},
|
||||
{
|
||||
name: '位置',
|
||||
value: 'location'
|
||||
}
|
||||
],
|
||||
conversationList:[],
|
||||
friendList:[],
|
||||
messageList:[],
|
||||
status:"wait", // wait, loading, done
|
||||
conversationID:"",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
"storeConversationList",
|
||||
"storeFriendList",
|
||||
]),
|
||||
currentTabIndex() {
|
||||
const index = this.searchTypeList.findIndex(item => item.value === this.currentSearchType);
|
||||
return index >= 0 ? index : 2;
|
||||
},
|
||||
getPlaceholder() {
|
||||
const placeholders = {
|
||||
conversation: "搜索会话名称",
|
||||
friend: "搜索好友昵称或备注",
|
||||
message: "搜索消息内容",
|
||||
file: "搜索文件",
|
||||
location: "搜索位置"
|
||||
};
|
||||
return placeholders[this.currentSearchType] || "请输入您要搜索的内容";
|
||||
},
|
||||
isEmpty() {
|
||||
if (this.currentSearchType === 'conversation') {
|
||||
return this.conversationList.length === 0;
|
||||
} else if (this.currentSearchType === 'friend') {
|
||||
return this.friendList.length === 0;
|
||||
} else if (['message', 'file', 'location'].includes(this.currentSearchType)) {
|
||||
return this.messageList.length === 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
onLoad(opt) {
|
||||
if(opt.type){
|
||||
this.currentSearchType = opt.type;
|
||||
}
|
||||
if(opt.conversationID){
|
||||
this.conversationID = opt.conversationID;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clear(){
|
||||
this.searchValue = "";
|
||||
this.status = "wait";
|
||||
this.conversationList = [];
|
||||
this.friendList = [];
|
||||
this.messageList = [];
|
||||
},
|
||||
cancel(){
|
||||
uni.navigateBack();
|
||||
},
|
||||
input(){
|
||||
// 可以在这里实现实时搜索
|
||||
},
|
||||
async search(){
|
||||
if(!this.searchValue || this.searchValue.trim() === ''){
|
||||
uni.showToast({
|
||||
title: '请输入搜索关键词',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.status = 'loading';
|
||||
try {
|
||||
if(this.currentSearchType === 'conversation'){
|
||||
await this.searchConversation();
|
||||
} else if(this.currentSearchType === 'friend'){
|
||||
await this.searchFriend();
|
||||
} else if(['message', 'file', 'location'].includes(this.currentSearchType)){
|
||||
await this.searchMessage();
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('搜索失败:', e);
|
||||
uni.showToast({
|
||||
title: '搜索失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
this.status = 'done';
|
||||
}
|
||||
},
|
||||
changeSearchType(e){
|
||||
//console.log(e,this.searchTypeList[e]);
|
||||
this.currentSearchType = e.value;//this.searchTypeList[index].value;
|
||||
this.search();
|
||||
// 切换类型时清空之前的搜索结果
|
||||
//this.clear();
|
||||
},
|
||||
userClick(item){
|
||||
// 跳转到用户详情或聊天页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/common/userCard/index?sourceID=${item.userID}`
|
||||
});
|
||||
},
|
||||
messageClick(item){
|
||||
// 跳转到对应的聊天页面
|
||||
const conversation = item.conversation;
|
||||
if(conversation){
|
||||
prepareConversationState(conversation);
|
||||
}
|
||||
},
|
||||
goToConversation(resultItem){
|
||||
// 根据会话类型跳转
|
||||
const sessionType = resultItem.conversationType === 2 ? SessionType.WorkingGroup : SessionType.Single;
|
||||
|
||||
// 从 conversationID 中解析 sourceID,或者从 store 中获取
|
||||
let sourceID = resultItem.groupID || resultItem.userID;
|
||||
|
||||
if(!sourceID){
|
||||
// 从 conversationID 解析:si_userID1_userID2 或 g_groupID
|
||||
const convID = resultItem.conversationID || '';
|
||||
if(convID.startsWith('si_')) {
|
||||
// 单聊:si_userID1_userID2,取第二个 userID(对方)
|
||||
const parts = convID.split('_');
|
||||
if(parts.length >= 3){
|
||||
const selfID = this.$store.getters.storeCurrentUserID;
|
||||
sourceID = parts[1] === selfID ? parts[2] : parts[1];
|
||||
}
|
||||
} else if(convID.startsWith('g_')) {
|
||||
// 群聊:g_groupID
|
||||
sourceID = convID.substring(2);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还是没有,尝试从 store 中获取
|
||||
if(!sourceID){
|
||||
const allConversations = this.storeConversationList || [];
|
||||
const conv = allConversations.find(c => c.conversationID === resultItem.conversationID);
|
||||
if(conv){
|
||||
sourceID = conv.groupID || conv.userID;
|
||||
}
|
||||
}
|
||||
|
||||
if(sourceID){
|
||||
navigateToDesignatedConversation(sourceID, sessionType).catch(() => {
|
||||
uni.showToast({
|
||||
title: '跳转失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '无法跳转,会话信息不完整',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
// 搜索会话
|
||||
searchConversation(){
|
||||
const keyword = this.searchValue.toLowerCase();
|
||||
const allConversations = this.storeConversationList || [];
|
||||
this.conversationList = allConversations.filter(conversation => {
|
||||
const showName = (conversation.showName || '').toLowerCase();
|
||||
return showName.includes(keyword);
|
||||
});
|
||||
},
|
||||
// 搜索好友
|
||||
searchFriend(){
|
||||
const keyword = this.searchValue.toLowerCase();
|
||||
const allFriends = this.storeFriendList || [];
|
||||
this.friendList = allFriends.filter(friend => {
|
||||
const nickname = (friend.nickname || '').toLowerCase();
|
||||
const remark = (friend.remark || '').toLowerCase();
|
||||
const userID = (friend.userID || '').toLowerCase();
|
||||
return nickname.includes(keyword) || remark.includes(keyword) || userID.includes(keyword);
|
||||
});
|
||||
},
|
||||
// 搜索消息(包括文件、位置)
|
||||
async searchMessage(){
|
||||
const _this = this;
|
||||
|
||||
// 根据搜索类型确定消息类型
|
||||
let messageTypeList = [];
|
||||
if(this.currentSearchType === 'file'){
|
||||
messageTypeList = [MessageType.FileMessage];
|
||||
} else if(this.currentSearchType === 'location'){
|
||||
messageTypeList = [MessageType.LocationMessage];
|
||||
} else {
|
||||
// 消息搜索:文本消息
|
||||
messageTypeList = [MessageType.TextMessage];
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await IMSDK.asyncApi('searchLocalMessages', IMSDK.uuid(), {
|
||||
conversationID: _this.conversationID || '',
|
||||
keywordList: [_this.searchValue],
|
||||
keywordListMatchType: 0,
|
||||
senderUserIDList: [],
|
||||
messageTypeList: messageTypeList,
|
||||
searchTimePosition: 0,
|
||||
searchTimePeriod: 0,
|
||||
pageIndex: 1,
|
||||
count: 50,
|
||||
});
|
||||
|
||||
if(result && result.errCode === 0 && result.data){
|
||||
// 直接使用返回的 searchResultItems,它们已经包含了会话信息和消息列表
|
||||
this.messageList = result.data.searchResultItems || [];
|
||||
|
||||
// 如果需要补充更多会话信息,可以从store中获取
|
||||
const allConversations = this.storeConversationList || [];
|
||||
this.messageList.forEach(item => {
|
||||
const conv = allConversations.find(c => c.conversationID === item.conversationID);
|
||||
if(conv){
|
||||
// 补充会话信息(如果返回的数据不完整)
|
||||
item.userID = item.userID || conv.userID;
|
||||
item.groupID = item.groupID || conv.groupID;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.messageList = [];
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('搜索消息失败:', e);
|
||||
this.messageList = [];
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-view{
|
||||
flex: 1;
|
||||
height: 0;
|
||||
::v-deep .uni-scroll-view-content{
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 200rpx 0;
|
||||
.empty-text {
|
||||
margin-top: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.conversation-group {
|
||||
background-color: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
.conversation-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 44rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 1px solid #eee;
|
||||
.conversation-title {
|
||||
flex: 1;
|
||||
margin-left: 24rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
.message-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user