Some checks are pending
Check / lint (push) Waiting to run
Check / typecheck (push) Waiting to run
Check / build (build, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build, 18.x, windows-latest) (push) Waiting to run
Check / build (build:app, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:app, 18.x, windows-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Waiting to run
282 lines
6.6 KiB
Vue
282 lines
6.6 KiB
Vue
<script lang="ts" setup>
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import Loading from '@/components/base/Loading.vue'
|
|
import { ServeFindTalkRecords } from '@/api/chat'
|
|
import { Down, Calendar } from '@icon-park/vue-next'
|
|
import * as message from '@/constant/message'
|
|
import { ITalkRecord } from '@/types/chat'
|
|
import { useInject } from '@/hooks'
|
|
|
|
const emit = defineEmits(['close'])
|
|
const props = defineProps({
|
|
talkType: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
receiverId: {
|
|
type: Number,
|
|
default: 0
|
|
}
|
|
})
|
|
const { showUserInfoModal } = useInject()
|
|
const model = reactive({
|
|
cursor: 0,
|
|
limit: 30,
|
|
msgType: 0,
|
|
loading: false,
|
|
loadMore: false,
|
|
isLoadMore: true
|
|
})
|
|
|
|
const isShow = ref(true)
|
|
const items = ref<ITalkRecord[]>([])
|
|
|
|
const tabs = [
|
|
{ name: '全部', type: 0, show: true },
|
|
{ name: '图片', type: message.ChatMsgTypeImage, show: true },
|
|
{ name: '音频', type: message.ChatMsgTypeAudio, show: true },
|
|
{ name: '视频', type: message.ChatMsgTypeVideo, show: true },
|
|
{ name: '文件', type: message.ChatMsgTypeFile, show: true },
|
|
{ name: '会话', type: message.ChatMsgTypeForward, show: true },
|
|
{ name: '代码', type: message.ChatMsgTypeCode, show: true },
|
|
{ name: '位置', type: message.ChatMsgTypeLocation, show: true },
|
|
{ name: '群投票', type: message.ChatMsgTypeVote, show: props.talkType == 2 }
|
|
]
|
|
|
|
const onMaskClick = () => {
|
|
emit('close')
|
|
}
|
|
|
|
const loadChatRecord = () => {
|
|
let data = {
|
|
talk_type: props.talkType,
|
|
receiver_id: props.receiverId,
|
|
msg_type: model.msgType,
|
|
cursor: model.cursor,
|
|
limit: model.limit
|
|
}
|
|
|
|
if (model.cursor === 0) {
|
|
model.loading = true
|
|
} else {
|
|
model.loadMore = true
|
|
}
|
|
|
|
ServeFindTalkRecords(data).then((res) => {
|
|
if (res.code != 200) return
|
|
|
|
if (data.cursor === 0) {
|
|
items.value = []
|
|
}
|
|
|
|
let list = res.data.items || []
|
|
if (list.length) {
|
|
model.cursor = res.data.cursor
|
|
}
|
|
|
|
model.loading = false
|
|
model.loadMore = false
|
|
model.isLoadMore = list.length >= model.limit
|
|
items.value.push(...list)
|
|
})
|
|
}
|
|
|
|
const triggerType = (type: number) => {
|
|
model.msgType = type
|
|
model.cursor = 0
|
|
loadChatRecord()
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadChatRecord()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<n-modal
|
|
v-model:show="isShow"
|
|
preset="card"
|
|
title="消息管理"
|
|
style="max-width: 750px"
|
|
class="modal-radius"
|
|
:on-after-leave="onMaskClick"
|
|
:segmented="{
|
|
content: true
|
|
}"
|
|
:header-style="{
|
|
padding: '20px 15px'
|
|
}"
|
|
:content-style="{
|
|
padding: 0
|
|
}"
|
|
>
|
|
<section class="main-box el-container is-vertical o-hidden">
|
|
<header class="el-header bdr-b search" style="height: 50px">
|
|
<div class="type-items">
|
|
<span
|
|
v-for="tab in tabs"
|
|
:key="tab.name"
|
|
class="pointer"
|
|
:class="{ active: model.msgType == tab.type }"
|
|
@click="triggerType(tab.type)"
|
|
v-show="tab.show"
|
|
>
|
|
{{ tab.name }}
|
|
</span>
|
|
</div>
|
|
<div style="display: flex; align-items: center">
|
|
<!-- <n-popover placement="bottom-end" trigger="click" :show-arrow="false">
|
|
<template #trigger>
|
|
<n-icon
|
|
:size="20"
|
|
class="pointer"
|
|
:component="Calendar"
|
|
/>
|
|
</template>
|
|
<n-date-picker
|
|
panel
|
|
type="date"
|
|
:is-date-disabled="disablePreviousDate"
|
|
:on-update:value="datefunc"
|
|
/>
|
|
</n-popover> -->
|
|
|
|
<n-icon :size="20" class="pointer" :component="Calendar" />
|
|
</div>
|
|
</header>
|
|
|
|
<main v-if="model.loading" class="el-main flex-center">
|
|
<Loading />
|
|
</main>
|
|
|
|
<main v-else-if="items.length === 0" class="el-main flex-center">
|
|
<n-empty size="200" description="暂无相关数据">
|
|
<template #icon>
|
|
<img src="@/assets/image/no-data.svg" alt="" />
|
|
</template>
|
|
</n-empty>
|
|
</main>
|
|
|
|
<main v-else class="el-main me-scrollbar me-scrollbar-thumb">
|
|
<div v-for="item in items" :key="item.id" class="message-item">
|
|
<div class="left-box">
|
|
<im-avatar
|
|
:src="item.avatar"
|
|
:size="30"
|
|
:username="item.nickname"
|
|
@click="showUserInfoModal(item.user_id)"
|
|
/>
|
|
</div>
|
|
|
|
<div class="right-box me-scrollbar">
|
|
<div class="msg-header">
|
|
<span class="name">{{ item.nickname }}</span>
|
|
<span class="time"> {{ item.created_at }}</span>
|
|
</div>
|
|
|
|
<template v-if="item.is_revoke == 1">
|
|
<div class="msg-content">此消息已被撤回</div>
|
|
</template>
|
|
|
|
<component
|
|
v-if="item.is_revoke == 0"
|
|
:is="message.MessageComponents[item.msg_type] || 'unknown-message'"
|
|
:extra="item.extra"
|
|
:data="item"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="more pointer flex-center" @click="loadChatRecord" v-show="model.isLoadMore">
|
|
<n-icon v-show="!model.loadMore" :size="20" class="icon" :component="Down" />
|
|
<span> {{ model.loadMore ? '数据加载中...' : '加载更多' }} </span>
|
|
</div>
|
|
</main>
|
|
</section>
|
|
</n-modal>
|
|
</template>
|
|
|
|
<style lang="less" scoped>
|
|
.main-box {
|
|
height: 550px;
|
|
|
|
.search {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0 15px 0 5px;
|
|
|
|
.type-items {
|
|
line-height: 40px;
|
|
user-select: none;
|
|
|
|
.active {
|
|
color: #03a9f4;
|
|
font-weight: 500;
|
|
}
|
|
|
|
span {
|
|
height: 40px;
|
|
width: 45px;
|
|
margin: 0 10px;
|
|
font-size: 13px;
|
|
font-weight: 400;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.message-item {
|
|
min-height: 30px;
|
|
display: flex;
|
|
margin-bottom: 5px;
|
|
flex-direction: row;
|
|
padding: 5px 15px;
|
|
|
|
&:first-child {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.left-box {
|
|
width: 30px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
justify-content: center;
|
|
user-select: none;
|
|
padding-top: 8px;
|
|
margin-right: 10px;
|
|
|
|
img {
|
|
height: 30px;
|
|
width: 30px;
|
|
border-radius: 3px;
|
|
}
|
|
}
|
|
|
|
.right-box {
|
|
width: 100%;
|
|
overflow-x: auto;
|
|
padding: 0px 5px 15px 5px;
|
|
box-sizing: border-box;
|
|
height: fit-content;
|
|
|
|
.msg-header {
|
|
height: 30px;
|
|
line-height: 30px;
|
|
font-size: 12px;
|
|
position: relative;
|
|
user-select: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
}
|
|
}
|
|
|
|
.more {
|
|
margin: 10px auto 20px;
|
|
width: 150px;
|
|
height: 30px;
|
|
}
|
|
</style>
|