oa_app仓库初始化

This commit is contained in:
2026-09-23 16:52:48 +08:00
commit 65c5e9f66f
82 changed files with 6490 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
<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: #fff; }
.list { flex: 1; height: 0; }
.ico.sm { font-size: 36rpx; }
.item { display: flex; align-items: center; padding: 20rpx 24rpx; background: #fff; }
.item.pin { background: #f7f7f7; }
.av-wrap { position: relative; width: 96rpx; height: 96rpx; flex-shrink: 0; }
.av { width: 96rpx; height: 96rpx; border-radius: 12rpx; }
.av.txt { display: flex; align-items: center; justify-content: center; font-size: 34rpx; font-weight: 600; }
.av.work { background: #2563eb; color: #fff; }
.av.file { background: #e2e7ff; color: #434655; }
.av.plain { background: #e2e7ff; color: #004ac6; }
.abs { position: absolute; top: -8rpx; right: -8rpx; }
.dotu { position: absolute; top: -4rpx; right: -4rpx; width: 16rpx; height: 16rpx; border-radius: 50%; background: #fa5151; }
.meta { flex: 1; margin-left: 20rpx; min-width: 0; padding: 4rpx 0; border-bottom: 1rpx solid #f0f0f0; }
.top { display: flex; justify-content: space-between; align-items: center; }
.name { font-size: 32rpx; font-weight: 500; max-width: 420rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.time { font-size: 22rpx; color: #b2b2b2; }
.bot { display: flex; align-items: center; margin-top: 8rpx; }
.prev { flex: 1; font-size: 24rpx; color: #888; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mute { font-size: 22rpx; margin-left: 8rpx; }
.empty { padding: 120rpx; text-align: center; color: #888; font-size: 26rpx; }
.ico { font-size: 40rpx; color: #111; padding: 8rpx 16rpx; }
</style>