feat: init
This commit is contained in:
116
src/components/message-box/index.vue
Normal file
116
src/components/message-box/index.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<a-spin style="display: block" :loading="loading">
|
||||
<a-tabs v-model:activeKey="messageType" type="rounded" destroy-on-hide>
|
||||
<a-tab-pane v-for="item in tabList" :key="item.key">
|
||||
<template #title>
|
||||
<span>{{ item.title }}{{ formatUnreadLength(item.key) }}</span>
|
||||
</template>
|
||||
<a-result v-if="!renderList.length" status="404">
|
||||
<template #subtitle>{{ $t('messageBox.noContent') }}</template>
|
||||
</a-result>
|
||||
<List :render-list="renderList" :unread-count="unreadCount" @item-click="handleItemClick" />
|
||||
</a-tab-pane>
|
||||
<template #extra>
|
||||
<a-button type="text" @click="emptyList">
|
||||
{{ $t('messageBox.tab.button') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MessageListType, MessageRecord, queryMessageList, setMessageStatus } from '@/api/message'
|
||||
import useLoading from '@/hooks/loading'
|
||||
import { computed, reactive, ref, toRefs } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import List from './list.vue'
|
||||
|
||||
interface TabItem {
|
||||
key: string
|
||||
title: string
|
||||
avatar?: string
|
||||
}
|
||||
const { loading, setLoading } = useLoading(true)
|
||||
const messageType = ref('message')
|
||||
const { t } = useI18n()
|
||||
const messageData = reactive<{
|
||||
renderList: MessageRecord[]
|
||||
messageList: MessageRecord[]
|
||||
}>({
|
||||
renderList: [],
|
||||
messageList: [],
|
||||
})
|
||||
toRefs(messageData)
|
||||
const tabList: TabItem[] = [
|
||||
{
|
||||
key: 'message',
|
||||
title: t('messageBox.tab.title.message'),
|
||||
},
|
||||
{
|
||||
key: 'notice',
|
||||
title: t('messageBox.tab.title.notice'),
|
||||
},
|
||||
{
|
||||
key: 'todo',
|
||||
title: t('messageBox.tab.title.todo'),
|
||||
},
|
||||
]
|
||||
async function fetchSourceData() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const { data } = await queryMessageList()
|
||||
messageData.messageList = data
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
async function readMessage(data: MessageListType) {
|
||||
const ids = data.map((item) => item.id)
|
||||
await setMessageStatus({ ids })
|
||||
fetchSourceData()
|
||||
}
|
||||
const renderList = computed(() => {
|
||||
return messageData.messageList.filter((item) => messageType.value === item.type)
|
||||
})
|
||||
const unreadCount = computed(() => {
|
||||
return renderList.value.filter((item) => !item.status).length
|
||||
})
|
||||
const getUnreadList = (type: string) => {
|
||||
const list = messageData.messageList.filter((item) => item.type === type && !item.status)
|
||||
return list
|
||||
}
|
||||
const formatUnreadLength = (type: string) => {
|
||||
const list = getUnreadList(type)
|
||||
return list.length ? `(${list.length})` : ``
|
||||
}
|
||||
const handleItemClick = (items: MessageListType) => {
|
||||
if (renderList.value.length) readMessage([...items])
|
||||
}
|
||||
const emptyList = () => {
|
||||
messageData.messageList = []
|
||||
}
|
||||
fetchSourceData()
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.arco-popover-popup-content) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.arco-list-item-meta) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
:deep(.arco-tabs-nav) {
|
||||
padding: 14px 0 12px 16px;
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
}
|
||||
:deep(.arco-tabs-content) {
|
||||
padding-top: 0;
|
||||
.arco-result-subtitle {
|
||||
color: rgb(var(--gray-6));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
140
src/components/message-box/list.vue
Normal file
140
src/components/message-box/list.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<a-list :bordered="false">
|
||||
<a-list-item
|
||||
v-for="item in renderList"
|
||||
:key="item.id"
|
||||
action-layout="vertical"
|
||||
:style="{
|
||||
opacity: item.status ? 0.5 : 1,
|
||||
}"
|
||||
>
|
||||
<template #extra>
|
||||
<a-tag v-if="item.messageType === 0" color="gray">未开始</a-tag>
|
||||
<a-tag v-else-if="item.messageType === 1" color="green">已开通</a-tag>
|
||||
<a-tag v-else-if="item.messageType === 2" color="blue">进行中</a-tag>
|
||||
<a-tag v-else-if="item.messageType === 3" color="red">即将到期</a-tag>
|
||||
</template>
|
||||
<div class="item-wrap" @click="onItemClick(item)">
|
||||
<a-list-item-meta>
|
||||
<template v-if="item.avatar" #avatar>
|
||||
<a-avatar shape="circle">
|
||||
<img v-if="item.avatar" :src="item.avatar" />
|
||||
<icon-desktop v-else />
|
||||
</a-avatar>
|
||||
</template>
|
||||
<template #title>
|
||||
<a-space :size="4">
|
||||
<span>{{ item.title }}</span>
|
||||
<a-typography-text type="secondary">
|
||||
{{ item.subTitle }}
|
||||
</a-typography-text>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #description>
|
||||
<div>
|
||||
<a-typography-paragraph
|
||||
:ellipsis="{
|
||||
rows: 1,
|
||||
}"
|
||||
>
|
||||
{{ item.content }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-text v-if="item.type === 'message'" class="time-text">
|
||||
{{ item.time }}
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<template #footer>
|
||||
<a-space fill :size="0" :class="{ 'add-border-top': renderList.length < showMax }">
|
||||
<div class="footer-wrap">
|
||||
<a-link @click="allRead">{{ $t('messageBox.allRead') }}</a-link>
|
||||
</div>
|
||||
<div class="footer-wrap">
|
||||
<a-link>{{ $t('messageBox.viewMore') }}</a-link>
|
||||
</div>
|
||||
</a-space>
|
||||
</template>
|
||||
<div v-if="renderList.length && renderList.length < 3" :style="{ height: (showMax - renderList.length) * 86 + 'px' }"></div>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MessageListType, MessageRecord } from '@/api/message'
|
||||
import { PropType } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
renderList: {
|
||||
type: Array as PropType<MessageListType>,
|
||||
required: true,
|
||||
},
|
||||
unreadCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['itemClick'])
|
||||
const allRead = () => {
|
||||
emit('itemClick', [...props.renderList])
|
||||
}
|
||||
|
||||
const onItemClick = (item: MessageRecord) => {
|
||||
if (!item.status) {
|
||||
emit('itemClick', [item])
|
||||
}
|
||||
}
|
||||
const showMax = 3
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.arco-list) {
|
||||
.arco-list-item {
|
||||
min-height: 86px;
|
||||
border-bottom: 1px solid rgb(var(--gray-3));
|
||||
}
|
||||
.arco-list-item-extra {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
.arco-list-item-meta-content {
|
||||
flex: 1;
|
||||
}
|
||||
.item-wrap {
|
||||
cursor: pointer;
|
||||
}
|
||||
.time-text {
|
||||
font-size: 12px;
|
||||
color: rgb(var(--gray-6));
|
||||
}
|
||||
.arco-empty {
|
||||
display: none;
|
||||
}
|
||||
.arco-list-footer {
|
||||
padding: 0;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-top: none;
|
||||
.arco-space-item {
|
||||
width: 100%;
|
||||
border-right: 1px solid rgb(var(--gray-3));
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
.add-border-top {
|
||||
border-top: 1px solid rgb(var(--gray-3));
|
||||
}
|
||||
}
|
||||
.footer-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
.arco-typography {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.add-border {
|
||||
border-top: 1px solid rgb(var(--gray-3));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
13
src/components/message-box/locale/en-US.ts
Normal file
13
src/components/message-box/locale/en-US.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
'messageBox.tab.title.message': 'Message',
|
||||
'messageBox.tab.title.notice': 'Notice',
|
||||
'messageBox.tab.title.todo': 'Todo',
|
||||
'messageBox.tab.button': 'empty',
|
||||
'messageBox.allRead': 'All Read',
|
||||
'messageBox.viewMore': 'View More',
|
||||
'messageBox.noContent': 'No Content',
|
||||
'messageBox.switchRoles': 'Switch Roles',
|
||||
'messageBox.userCenter': 'User Center',
|
||||
'messageBox.userSettings': 'User Settings',
|
||||
'messageBox.logout': 'Logout',
|
||||
}
|
||||
13
src/components/message-box/locale/zh-CN.ts
Normal file
13
src/components/message-box/locale/zh-CN.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
'messageBox.tab.title.message': '消息',
|
||||
'messageBox.tab.title.notice': '通知',
|
||||
'messageBox.tab.title.todo': '待办',
|
||||
'messageBox.tab.button': '清空',
|
||||
'messageBox.allRead': '全部已读',
|
||||
'messageBox.viewMore': '查看更多',
|
||||
'messageBox.noContent': '暂无内容',
|
||||
'messageBox.switchRoles': '切换角色',
|
||||
'messageBox.userCenter': '用户中心',
|
||||
'messageBox.userSettings': '用户设置',
|
||||
'messageBox.logout': '登出登录',
|
||||
}
|
||||
Reference in New Issue
Block a user