This commit is contained in:
cansnow
2025-12-08 02:29:46 +08:00
parent 69a61178e1
commit 22ee59cd3d
17 changed files with 895 additions and 244 deletions
+81
View File
@@ -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>
+399
View File
@@ -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>