Files
oa_app/pages/search/search.vue
T
2026-09-23 17:38:24 +08:00

111 lines
4.4 KiB
Vue

<template>
<view class="page">
<fy-header title="搜索" back />
<view class="search">
<input v-model="q" placeholder="搜索同事 / 会话 / 聊天记录" confirm-type="search" focus />
</view>
<view v-if="!q.trim()" class="empty">搜索同事会话或待办</view>
<view v-else>
<view v-if="people.length" class="sec">同事</view>
<view v-for="p in people" :key="'p' + p.id" class="item" @click="openPerson(p)">
<image v-if="p.avatar" class="av img" :src="p.avatar" mode="aspectFill" />
<view v-else class="av">{{ (p.name || '?').slice(0, 1) }}</view>
<view class="meta">
<text class="n">{{ p.name }}</text>
<text class="s">{{ p.department }} {{ p.title }}</text>
</view>
</view>
<view v-if="convs.length" class="sec">会话</view>
<view v-for="c in convs" :key="'c' + c.id" class="item" @click="openConv(c)">
<view class="av">{{ (c.name || '?').slice(0, 1) }}</view>
<view class="meta">
<text class="n">{{ c.name }}</text>
<text class="s">{{ c.lastMessage }}</text>
</view>
</view>
<view v-if="hits.length" class="sec">聊天记录</view>
<view v-for="h in hits" :key="h.id" class="item" @click="openConv(h.conv)">
<view class="av">{{ (h.conv.name || '?').slice(0, 1) }}</view>
<view class="meta">
<text class="n">{{ h.conv.name }}</text>
<text class="s">{{ h.text }}</text>
</view>
</view>
<view v-if="todos.length" class="sec">待办</view>
<view v-for="t in todos" :key="t.no || t.id" class="item" @click="openTodo(t)">
<view class="av work"></view>
<view class="meta">
<text class="n">{{ t.title || t.no }}</text>
</view>
</view>
<view v-if="!people.length && !convs.length && !hits.length && !todos.length" class="empty">没有找到相关内容</view>
</view>
</view>
</template>
<script>
import FyHeader from '../../components/fy-header.vue'
import { store, previewOf } from '../../utils/store.js'
export default {
components: { FyHeader },
data() { return { q: '' } },
computed: {
people() {
const q = this.q.trim()
if (!q) return []
return Object.values(store.people).filter((p) => (p.name + p.phone + p.department).includes(q)).slice(0, 8)
},
convs() {
const q = this.q.trim()
if (!q) return []
return store.conversations.filter((c) => (c.name + (c.lastMessage || '')).includes(q)).slice(0, 8)
},
hits() {
const q = this.q.trim()
if (!q) return []
const out = []
store.conversations.forEach((c) => {
(c.messages || []).forEach((m) => {
if (m.recalled) return
const text = previewOf(m)
if (text.indexOf(q) >= 0) out.push({ id: c.id + ':' + m.id, conv: c, text })
})
})
return out.slice(0, 12)
},
todos() {
const q = this.q.trim()
if (!q) return []
return (store.todos || []).filter((t) => String(t.title || t.no || '').includes(q)).slice(0, 8)
}
},
methods: {
openPerson(p) {
uni.navigateTo({ url: '/pages/contact/detail?id=' + encodeURIComponent(p.id) })
},
openConv(c) {
if (c.type === 'work') uni.navigateTo({ url: '/pages/chat/work?sid=' + encodeURIComponent(c.id) })
else uni.navigateTo({ url: '/pages/chat/chat?sid=' + encodeURIComponent(c.id) })
},
openTodo(t) {
uni.navigateTo({ url: '/pages/todo/detail?no=' + encodeURIComponent(t.no || t.id) })
}
}
}
</script>
<style scoped>
.search { padding: 12rpx 28rpx 16rpx; }
.search input { height: 80rpx; background: #fff; border-radius: 20rpx; padding: 0 28rpx; font-size: 28rpx; border: 1rpx solid rgba(17,24,39,0.06); }
.sec { padding: 24rpx 28rpx 8rpx; font-size: 24rpx; color: #6B7280; }
.item { display: flex; align-items: center; padding: 20rpx 24rpx; background: #fff; }
.av { width: 72rpx; height: 72rpx; border-radius: 10rpx; background: #E8F1FF; color: #1677FF; display: flex; align-items: center; justify-content: center; font-weight: 700; overflow: hidden; flex-shrink: 0; }
.av.img { width: 72rpx; height: 72rpx; }
.av.work { background: #1677FF; color: #fff; }
.meta { margin-left: 16rpx; min-width: 0; }
.n { display: block; font-size: 28rpx; }
.s { font-size: 22rpx; color: #888; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.empty { text-align: center; color: #6B7280; padding: 120rpx 40rpx; font-size: 28rpx; }
</style>