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

152 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page msg">
<fy-header :title="unread ? ('消息(' + unread + ')') : '消息'">
<template #right>
<text class="ico sm" @click="goSearch"></text>
<text class="ico" @click="plus"></text>
</template>
</fy-header>
<scroll-view scroll-y class="list">
<view v-if="!rows.length" class="empty">暂无会话</view>
<view
v-for="c in rows"
:key="c.id"
class="item"
:class="{ pin: c.pinned }"
@click="open(c)"
@longpress="act(c)"
>
<view class="av-wrap">
<image v-if="c.avatar" class="av" :src="c.avatar" mode="aspectFill" />
<view v-else class="av txt" :class="avClass(c)">{{ avText(c) }}</view>
<text v-if="c.unread && !c.muted" class="badge abs">{{ c.unread > 99 ? '99+' : c.unread }}</text>
<text v-else-if="c.unread && c.muted" class="dotu" />
</view>
<view class="meta">
<view class="top">
<text class="name">{{ c.name }}</text>
<text class="time">{{ c.lastTime }}</text>
</view>
<view class="bot">
<text class="prev">{{ c.muted && c.unread ? '[' + c.unread + '条]' : '' }}{{ c.lastMessage || ' ' }}</text>
<text v-if="c.muted" class="mute">🔕</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import FyHeader from '../../components/fy-header.vue'
import { store } from '../../utils/store.js'
export default {
components: { FyHeader },
data() {
return { rows: [], unread: 0, off: null }
},
onShow() {
if (this.off) this.off()
this.guard()
store.refreshPeoplePhotos()
this.pull()
this.off = store.on(() => this.pull())
},
onHide() {
if (this.off) this.off()
this.off = null
},
methods: {
guard() {
if (store.phase === 'login') uni.reLaunch({ url: '/pages/login/login' })
if (store.phase === 'forcePassword') uni.redirectTo({ url: '/pages/password/password' })
},
pull() {
this.rows = store.conversations.slice()
this.unread = store.unreadTotal()
},
avText(c) {
if (c.type === 'work') return '工'
if (c.type === 'files') return '文'
if (c.type === 'group') return '群'
return (c.name || '?').slice(0, 1)
},
avClass(c) {
if (c.type === 'work') return 'work'
if (c.type === 'files') return 'file'
return 'plain'
},
open(c) {
if (c.type === 'work') {
uni.navigateTo({ url: '/pages/chat/work?sid=' + encodeURIComponent(c.id) })
return
}
uni.navigateTo({ url: '/pages/chat/chat?sid=' + encodeURIComponent(c.id) })
},
act(c) {
const items = [
c.pinned ? '取消置顶' : '置顶聊天',
c.unread ? '标为已读' : '标为未读',
c.muted ? '关闭免打扰' : '消息免打扰',
'删除该聊天'
]
uni.showActionSheet({
itemList: items,
success: (res) => {
if (res.tapIndex === 0) store.setSession(c, { pinned: !c.pinned })
else if (res.tapIndex === 1) {
if (c.unread) {
c.unread = 0
store.emit()
} else store.markUnread(c)
} else if (res.tapIndex === 2) store.setSession(c, { muted: !c.muted })
else if (res.tapIndex === 3) {
uni.showModal({
title: '删除聊天',
content: '仅从消息列表移除,聊天记录仍保留在服务器。',
success: (r) => { if (r.confirm) store.hideConv(c) }
})
}
}
})
},
plus() {
uni.showActionSheet({
itemList: ['发起群聊', '添加同事'],
success: (res) => {
if (res.tapIndex === 0) uni.navigateTo({ url: '/pages/chat/create' })
else uni.navigateTo({ url: '/pages/search/search' })
}
})
},
goSearch() { uni.navigateTo({ url: '/pages/search/search' }) }
}
}
</script>
<style scoped>
.msg { display: flex; flex-direction: column; height: 100vh; background: #F5F7FA; }
.list { flex: 1; height: 0; background: #fff; }
.ico.sm { font-size: 34rpx; color: #1677FF; }
.item { display: flex; align-items: center; padding: 22rpx 28rpx; background: #fff; }
.item.pin { background: #F7F8FA; }
.av-wrap { position: relative; width: 92rpx; height: 92rpx; flex-shrink: 0; }
.av { width: 92rpx; height: 92rpx; border-radius: 20rpx; }
.av.txt { display: flex; align-items: center; justify-content: center; font-size: 32rpx; font-weight: 600; }
.av.work { background: #1677FF; color: #fff; }
.av.file { background: #F3F6FB; color: #4B5563; }
.av.plain { background: #E8F1FF; color: #1677FF; }
.abs { position: absolute; top: -8rpx; right: -8rpx; }
.dotu { position: absolute; top: -4rpx; right: -4rpx; width: 16rpx; height: 16rpx; border-radius: 50%; background: #DC2626; }
.meta { flex: 1; margin-left: 20rpx; min-width: 0; padding: 8rpx 0 18rpx; border-bottom: 1rpx solid #F0F2F5; }
.top { display: flex; justify-content: space-between; align-items: center; }
.name { font-size: 30rpx; font-weight: 600; max-width: 420rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.time { font-size: 22rpx; color: #9CA3AF; }
.bot { display: flex; align-items: center; margin-top: 8rpx; }
.prev { flex: 1; font-size: 24rpx; color: #6B7280; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mute { font-size: 22rpx; margin-left: 8rpx; color: #9CA3AF; }
.empty { padding: 160rpx 40rpx; text-align: center; color: #6B7280; font-size: 28rpx; background: #fff; }
.ico { font-size: 36rpx; color: #1677FF; padding: 8rpx 12rpx; }
</style>