194 lines
6.5 KiB
Vue
194 lines
6.5 KiB
Vue
<template>
|
||
<view class="page search-page" :class="{ show: shown }">
|
||
<view class="top" :style="{ paddingTop: status + 'px' }">
|
||
<text class="back" @click="back">‹</text>
|
||
<view class="bar">
|
||
<wd-icon name="search" size="30rpx" color="#9CA3AF" />
|
||
<input
|
||
class="field"
|
||
:focus="keyOn"
|
||
v-model="q"
|
||
placeholder="搜索同事、会话或待办"
|
||
confirm-type="search"
|
||
:adjust-position="true"
|
||
hold-keyboard
|
||
@blur="onBlur"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view v-if="!q.trim()">
|
||
<wd-status-tip image="search" tip="搜索同事、会话或待办" />
|
||
</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 v-if="c.type === 'files'" class="av"><fy-file-mark /></view>
|
||
<view v-else 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 v-if="h.conv.type === 'files'" class="av"><fy-file-mark /></view>
|
||
<view v-else 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>
|
||
<wd-status-tip v-if="!people.length && !convs.length && !hits.length && !todos.length" image="search" tip="没有找到相关内容" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import FyFileMark from '../../components/fy-file-mark.vue'
|
||
import { store, previewOf } from '../../utils/store.js'
|
||
|
||
export default {
|
||
components: { FyFileMark },
|
||
data() { return { q: '', shown: false, keyOn: false, status: 20 } },
|
||
created() {
|
||
try { this.status = uni.getSystemInfoSync().statusBarHeight || 20 } catch (_) {}
|
||
},
|
||
onShow() {
|
||
this.shown = false
|
||
this.keyOn = false
|
||
this.$nextTick(() => { this.shown = true })
|
||
clearTimeout(this._key)
|
||
this._key = setTimeout(() => {
|
||
this.keyOn = false
|
||
this.$nextTick(() => { this.keyOn = true })
|
||
}, 320)
|
||
},
|
||
onHide() {
|
||
this.keyOn = false
|
||
clearTimeout(this._key)
|
||
},
|
||
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: {
|
||
onBlur() {
|
||
this.keyOn = false
|
||
},
|
||
back() {
|
||
const pages = getCurrentPages()
|
||
if (pages.length > 1) uni.navigateBack()
|
||
else uni.switchTab({ url: '/pages/tabs/messages' })
|
||
},
|
||
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-page {
|
||
position: fixed;
|
||
left: 0;
|
||
top: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 30;
|
||
background: #F5F7FA;
|
||
transform: translateX(100%);
|
||
overflow: auto;
|
||
}
|
||
.search-page.show {
|
||
transform: translateX(0);
|
||
transition: transform 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||
}
|
||
.top {
|
||
display: flex;
|
||
align-items: center;
|
||
padding-right: 24rpx;
|
||
padding-bottom: 12rpx;
|
||
background: #F5F7FA;
|
||
}
|
||
.back {
|
||
width: 72rpx;
|
||
text-align: center;
|
||
font-size: 52rpx;
|
||
line-height: 1;
|
||
color: #111827;
|
||
font-weight: 300;
|
||
flex-shrink: 0;
|
||
}
|
||
.bar {
|
||
flex: 1;
|
||
height: 64rpx;
|
||
border-radius: 12rpx;
|
||
background: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 16rpx;
|
||
border: 1rpx solid rgba(17, 24, 39, 0.06);
|
||
}
|
||
.field { flex: 1; height: 64rpx; font-size: 28rpx; margin-left: 8rpx; }
|
||
.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>
|