版本预发布

This commit is contained in:
孟帅
2023-02-08 20:29:34 +08:00
parent f11c7c5bf2
commit 2068d05c93
269 changed files with 16122 additions and 12075 deletions

View File

@@ -0,0 +1,74 @@
<template>
<n-scrollbar style="max-height: 360px">
<n-list>
<n-list-item v-for="(item, index) in list" :key="item.id" @click="handleRead(index)">
<n-thing class="px-15px" :class="{ 'opacity-30': item.isRead }">
<template #avatar>
<n-avatar round v-if="item.senderAvatar" :size="28" :src="item.senderAvatar" />
<n-icon-wrapper v-else :size="28" :border-radius="10">
<n-icon :size="20" :component="getIcon(item)" />
</n-icon-wrapper>
</template>
<template #header>
<n-ellipsis :line-clamp="1">
{{ item.title }}
<template #tooltip>
{{ item.title }}
</template>
</n-ellipsis>
</template>
<template v-if="item.tagTitle" #header-extra>
<n-tag v-bind="item.tagProps" size="small">{{ item.tagTitle }}</n-tag>
</template>
<template #description>
<div v-if="item.content" class="description-box">
<span v-html="item.content" class="description-html"> </span>
</div>
<p>{{ item.createdAt }}</p>
</template>
</n-thing>
</n-list-item>
</n-list>
</n-scrollbar>
</template>
<script lang="ts" setup>
import { MessageRow, getIcon } from '@/enums/systemMessageEnum';
interface Props {
list?: MessageRow[];
}
withDefaults(defineProps<Props>(), {
list: () => [],
});
interface Emits {
(e: 'read', val: number): void;
}
const emit = defineEmits<Emits>();
function handleRead(index: number) {
emit('read', index);
}
</script>
<style lang="less" scoped>
:deep(.description-box) {
height: 100%;
display: flex;
align-items: center;
margin-right: 10px;
}
:deep(.description-html) {
height: 100%;
}
:deep(.px-15px) {
padding-left: 15px;
padding-right: 15px;
}
:deep(.text-34px) {
font-size: 34px;
}
</style>

View File

@@ -1,65 +0,0 @@
<template>
<n-card
:content-style="{ padding: '0px' }"
:footer-style="{ padding: '0px' }"
:bordered="false"
:segmented="true"
>
<div v-if="notificationStore.messages.length > 0">
<div
class="flex items-center max-w-sm p-1 mx-auto space-x-2 rounded-xl"
v-for="(item, index) of notificationStore.messages"
:key="index"
>
<div class="flex-shrink-0">
<n-icon size="40" color="#f00">
<NotificationsCircle />
</n-icon>
</div>
<div>
<div class="text-sm font-medium">{{ item.title }}</div>
<n-ellipsis :line-clamp="1" class="text-gray-500">{{ item.content }}</n-ellipsis>
</div>
</div>
</div>
<n-empty v-else description="暂无消息哦~" class="pt-20 pb-20" />
<template #footer>
<div class="flex justify-evenly">
<n-button type="text" @click="onClearMessage">清空提醒</n-button>
<n-button type="text" @click="onAllMessage">查看更多</n-button>
</div>
</template>
</n-card>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { NotificationsCircle } from '@vicons/ionicons5';
import { notificationStoreWidthOut } from '@/store/modules/notification';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'PopoverMessage',
components: { NotificationsCircle },
emits: ['clear'],
setup(_props, { emit }) {
const notificationStore = notificationStoreWidthOut();
const router = useRouter();
function onClearMessage() {
notificationStore.setMessages([]);
emit('clear');
}
function onAllMessage() {
router.push({ name: 'apply_notice' });
}
return {
onClearMessage,
notificationStore,
onAllMessage,
};
},
});
</script>

View File

@@ -0,0 +1,129 @@
<template>
<n-tabs v-model:value="currentTab" type="line" justify-content="space-evenly">
<n-tab-pane
v-for="(item, index) in notificationStore.getMessages"
:key="item.key"
:name="index"
>
<template #tab>
<div>
<span>{{ item.name }}</span>
<n-badge
v-bind="item.badgeProps"
:value="item.list.filter((message) => !message.isRead).length"
:max="99"
show-zero
/>
</div>
</template>
<n-spin :show="loading">
<n-empty v-show="item.list.length === 0" description="无数据" :show-icon="false">
<template #extra>
<n-button size="small" @click="handleLoadMore"> 查看更多</n-button>
</template>
</n-empty>
<message-list :list="item.list" @read="handleRead" />
</n-spin>
</n-tab-pane>
</n-tabs>
<n-space v-if="showAction" justify="center" size="large" class="flex border-t">
<n-button class="act-btn" size="small" @click="handleClear">清空</n-button>
<n-button class="act-btn" size="small" @click="handleAllRead">全部已读</n-button>
<n-button class="act-btn" size="small" @click="handleLoadMore">查看更多</n-button>
</n-space>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import MessageList from './MessageList.vue';
import { notificationStoreWidthOut } from '@/store/modules/notification';
import { ReadAll, UpRead } from '@/api/apply/notice';
import { useRouter } from 'vue-router';
const router = useRouter();
const notificationStore = notificationStoreWidthOut();
const loading = ref(false);
const currentTab = ref(0);
const showAction = computed(
() => notificationStore.getMessages[currentTab.value].list.length > 0
);
function handleRead(index: number) {
loading.value = true;
const message = notificationStore.getMessages[currentTab.value].list[index];
UpRead({ id: message.id })
.then(() => {
message.isRead = true;
if (!message.isRead) {
switch (message.type) {
case 1:
notificationStore.notifyUnread--;
break;
case 2:
notificationStore.noticeUnread--;
break;
case 3:
notificationStore.letterUnread--;
break;
}
}
})
.finally(() => {
loading.value = false;
});
}
function handleAllRead() {
loading.value = true;
ReadAll({ type: notificationStore.getMessages[currentTab.value].key })
.then(() => {
notificationStore.getMessages[currentTab.value].list.forEach((item) =>
Object.assign(item, { isRead: true })
);
switch (notificationStore.getMessages[currentTab.value].key) {
case 1:
notificationStore.notifyUnread = 0;
break;
case 2:
notificationStore.noticeUnread = 0;
break;
case 3:
notificationStore.letterUnread = 0;
break;
}
})
.finally(() => {
loading.value = false;
});
}
function handleClear() {
notificationStore.getMessages[currentTab.value].list = [];
switch (notificationStore.getMessages[currentTab.value].key) {
case 1:
notificationStore.notifyUnread = 0;
break;
case 2:
notificationStore.noticeUnread = 0;
break;
case 3:
notificationStore.letterUnread = 0;
break;
}
}
function handleLoadMore() {
router.push({
name: 'home_message',
query: {
type: notificationStore.getMessages[currentTab.value].key,
},
});
}
</script>
<style scoped>
.act-btn {
margin-top: 8px;
}
</style>

View File

@@ -93,16 +93,22 @@
placement="bottom"
v-if="item.icon === 'BellOutlined'"
trigger="click"
:width="300"
:width="getIsMobile ? 276 : 420"
>
<template #trigger>
<n-badge :value="notificationStore.messages.length" :max="99" processing>
<n-icon size="18">
<BellOutlined />
</n-icon>
</n-badge>
<n-tooltip placement="bottom">
<template #trigger>
<n-badge :value="notificationStore.getUnreadCount()" :max="99" processing>
<n-icon size="18">
<BellOutlined />
</n-icon>
</n-badge>
</template>
<span>{{ item.tips }}</span>
</n-tooltip>
</template>
<PopoverMessage />
<SystemMessage />
</n-popover>
<div v-else>
@@ -131,12 +137,8 @@
<div class="layout-header-trigger layout-header-trigger-min">
<n-dropdown trigger="hover" @select="avatarSelect" :options="avatarOptions">
<div class="avatar">
<n-avatar round>
{{ username }}
<template #icon>
<UserOutlined />
</template>
</n-avatar>
<n-avatar v-if="userStore.avatar" round :size="30" :src="userStore.avatar" />
<n-avatar v-else round :size="30">{{ userStore.realName }}</n-avatar>
</div>
</n-dropdown>
</div>
@@ -162,7 +164,17 @@
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, computed, unref, watch, h } from 'vue';
import {
defineComponent,
reactive,
toRefs,
ref,
computed,
unref,
watch,
h,
onMounted,
} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import components from './components';
import {
@@ -170,8 +182,11 @@
useDialog,
useMessage,
NAvatar,
NTag,
NIcon,
useNotification,
NotificationReactive,
NButton,
} from 'naive-ui';
import { TABS_ROUTES } from '@/store/mutation-types';
import { useUserStore } from '@/store/modules/user';
@@ -180,13 +195,19 @@
import { AsideMenu } from '@/layout/components/Menu';
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
import { NotificationsOutline as NotificationsIcon } from '@vicons/ionicons5';
import PopoverMessage from './PopoverMessage.vue';
import SystemMessage from './SystemMessage.vue';
import { notificationStoreWidthOut } from '@/store/modules/notification';
import notificationImg from '@/assets/images/notification.png';
import { getIcon } from '@/enums/systemMessageEnum';
export default defineComponent({
name: 'PageHeader',
components: { ...components, NDialogProvider, ProjectSetting, AsideMenu, PopoverMessage },
components: {
...components,
NDialogProvider,
ProjectSetting,
AsideMenu,
SystemMessage,
},
props: {
collapsed: {
type: Boolean,
@@ -201,15 +222,21 @@
const useLockscreen = useLockscreenStore();
const message = useMessage();
const dialog = useDialog();
const { getNavMode, getNavTheme, getHeaderSetting, getMenuSetting, getCrumbsSetting } =
useProjectSetting();
const { username } = userStore?.info || {};
const {
getNavMode,
getNavTheme,
getHeaderSetting,
getMenuSetting,
getCrumbsSetting,
getIsMobile,
} = useProjectSetting();
// const { username, avatar } = userStore?.info || {};
const drawerSetting = ref();
const state = reactive({
username: username || '',
// username: username || '',
// avatar: avatar || '',
fullscreenIcon: 'FullscreenOutlined',
navMode: getNavMode,
navTheme: getNavTheme,
@@ -334,7 +361,7 @@
},
{
icon: 'BellOutlined',
tips: '系统消息',
tips: '我的消息',
},
{
icon: 'LockOutlined',
@@ -359,7 +386,7 @@
const avatarSelect = (key) => {
switch (key) {
case 1:
router.push({ name: 'setting_account' });
router.push({ name: 'home_account' });
break;
case 2:
doLogout();
@@ -373,38 +400,84 @@
}
const notification = useNotification();
const getMessages = computed(() => {
return notificationStore.messages;
return notificationStore.newMessage;
});
const nRef = ref<NotificationReactive | null>(null);
// 监听新消息,推送通知
watch(
getMessages,
(newVal, _oldVal) => {
if (newVal[0] !== undefined) {
let message = newVal[0];
nRef.value = notification.create({
title: message.title,
description: message.description,
content: message.content,
meta: message.meta,
duration: 5000,
avatar: () =>
h(NAvatar, {
size: 'small',
round: true,
src: notificationImg,
}),
onClose: () => {
nRef.value = null;
},
});
if (newVal === null || newVal === undefined) {
return;
}
nRef.value = notification.create({
title: newVal.title,
description:
newVal.tagTitle === '' || newVal.tagTitle === undefined
? undefined
: () =>
h(
NTag,
{
style: {
marginRight: '6px',
},
type: newVal.tagProps?.type,
bordered: false,
},
{
default: () => newVal.tagTitle,
}
),
content: () =>
newVal.content === '' || newVal.content === undefined
? undefined
: h('div', { innerHTML: '<div>' + newVal.content + '</div>' }),
meta: newVal.createdAt,
avatar: () =>
newVal.senderAvatar !== '' || newVal.senderAvatar === undefined
? h(NAvatar, {
size: 'small',
round: true,
src: newVal.senderAvatar,
})
: h(NIcon, null, { default: () => h(getIcon(newVal)) }),
action: () =>
h(
NButton,
{
text: true,
type: 'info',
onClick: () => {
(nRef.value as NotificationReactive).destroy();
router.push({
name: 'home_message',
query: {
type: newVal.type,
},
});
},
},
{
default: () => '查看详情',
}
),
onClose: () => {
nRef.value = null;
},
});
},
{ immediate: true, deep: true }
);
onMounted(() => {
if (notificationStore.getUnreadCount() === 0) {
notificationStore.pullMessages();
}
});
return {
...toRefs(state),
iconList,
@@ -423,8 +496,10 @@
getMenuLocation,
mixMenu,
NotificationsIcon,
PopoverMessage,
SystemMessage,
notificationStore,
getIsMobile,
userStore,
};
},
});