feat(performance、notify、weekly): 添加绩效考核功能、通知跳转链接、优化工作日志分割逻辑。
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useDebounceFn, useInfiniteScroll } from '@vueuse/core';
|
||||
import { OBJECT_CONTEXT_QUERY_KEY } from '@/constants/object-context';
|
||||
import { NOTIFY_MESSAGE_LEVEL_DICT_CODE } from '@/constants/dict';
|
||||
import {
|
||||
fetchGetMyNotifyMessagePage,
|
||||
@@ -14,6 +16,7 @@ import { formatDateTime, formatRelativeTime } from '@/utils/datetime';
|
||||
defineOptions({ name: 'NotificationBell' });
|
||||
|
||||
const dictStore = useDictStore();
|
||||
const router = useRouter();
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
const UNREAD_COUNT_POLL_INTERVAL = 15 * 1000;
|
||||
@@ -50,6 +53,12 @@ const searchKeyword = ref('');
|
||||
const detailVisible = ref(false);
|
||||
const detailMessage = ref<Api.NotifyMessage.NotifyMessage | null>(null);
|
||||
|
||||
interface NotifyRenderParts {
|
||||
prefix: string;
|
||||
clickable: string;
|
||||
suffix: string;
|
||||
}
|
||||
|
||||
function keywordParam() {
|
||||
return searchKeyword.value.trim() || undefined;
|
||||
}
|
||||
@@ -202,6 +211,89 @@ function openDetail(row: Api.NotifyMessage.NotifyMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
function getClickableText(message: Api.NotifyMessage.NotifyMessage) {
|
||||
const params = message.templateParams;
|
||||
switch (message.templateCode) {
|
||||
case 'execution_assigned':
|
||||
return params?.executionName?.trim() || '';
|
||||
case 'task_assigned':
|
||||
return params?.taskName?.trim() || '';
|
||||
case 'project_created':
|
||||
return params?.projectName?.trim() || '';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function canJump(message: Api.NotifyMessage.NotifyMessage) {
|
||||
const params = message.templateParams;
|
||||
return Boolean(params?.jumpType && params.projectId && getClickableText(message));
|
||||
}
|
||||
|
||||
function renderNotifyParts(message: Api.NotifyMessage.NotifyMessage): NotifyRenderParts {
|
||||
const content = message.templateContent || '';
|
||||
const clickable = getClickableText(message);
|
||||
if (!content || !clickable) {
|
||||
return { prefix: content, clickable: '', suffix: '' };
|
||||
}
|
||||
|
||||
const startIndex = content.indexOf(clickable);
|
||||
if (startIndex < 0) {
|
||||
return { prefix: content, clickable: '', suffix: '' };
|
||||
}
|
||||
|
||||
return {
|
||||
prefix: content.slice(0, startIndex),
|
||||
clickable,
|
||||
suffix: content.slice(startIndex + clickable.length)
|
||||
};
|
||||
}
|
||||
|
||||
async function handleNotifyJump(message: Api.NotifyMessage.NotifyMessage) {
|
||||
const params = message.templateParams;
|
||||
if (!params?.jumpType || !params.projectId) return;
|
||||
|
||||
const query: Record<string, string> = {
|
||||
[OBJECT_CONTEXT_QUERY_KEY]: params.projectId
|
||||
};
|
||||
|
||||
let path = '';
|
||||
|
||||
switch (params.jumpType) {
|
||||
case 'project':
|
||||
path = '/project/project/overview';
|
||||
break;
|
||||
case 'execution':
|
||||
path = '/project/project/execution';
|
||||
if (params.executionName) {
|
||||
query.executionName = params.executionName;
|
||||
}
|
||||
break;
|
||||
case 'task':
|
||||
path = '/project/project/execution';
|
||||
if (params.taskName) {
|
||||
query.taskName = params.taskName;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
detailVisible.value = false;
|
||||
drawerOpen.value = false;
|
||||
await router.push({ path, query });
|
||||
}
|
||||
|
||||
async function onNotifyLinkClick(row: Api.NotifyMessage.NotifyMessage) {
|
||||
if (!canJump(row)) return;
|
||||
|
||||
if (!row.readStatus) {
|
||||
await markRead(row);
|
||||
}
|
||||
|
||||
await handleNotifyJump(row);
|
||||
}
|
||||
|
||||
async function markAllRead() {
|
||||
const { error } = await fetchUpdateAllNotifyMessageRead();
|
||||
if (error) return;
|
||||
@@ -281,7 +373,25 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<span class="notification-bell__row-dot" :style="{ backgroundColor: levelDotColor(row.level) }" />
|
||||
<div class="notification-bell__row-body">
|
||||
<div class="notification-bell__row-title">{{ row.templateContent }}</div>
|
||||
<div class="notification-bell__row-title">
|
||||
<template v-if="canJump(row)">
|
||||
<template v-for="(part, index) in [renderNotifyParts(row)]" :key="`${row.id}-${index}`">
|
||||
<span>{{ part.prefix }}</span>
|
||||
<button
|
||||
v-if="part.clickable"
|
||||
type="button"
|
||||
class="notification-bell__inline-link"
|
||||
@click.stop="onNotifyLinkClick(row)"
|
||||
>
|
||||
{{ part.clickable }}
|
||||
</button>
|
||||
<span>{{ part.suffix }}</span>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ row.templateContent }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="notification-bell__row-meta">
|
||||
<DictTag :dict-code="NOTIFY_MESSAGE_LEVEL_DICT_CODE" :value="row.level" size="small" round />
|
||||
<span class="notification-bell__row-time">{{ formatRelativeTime(row.createTime) }}</span>
|
||||
@@ -312,7 +422,25 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<span class="notification-bell__row-dot" :style="{ backgroundColor: levelDotColor(row.level) }" />
|
||||
<div class="notification-bell__row-body">
|
||||
<div class="notification-bell__row-title">{{ row.templateContent }}</div>
|
||||
<div class="notification-bell__row-title">
|
||||
<template v-if="canJump(row)">
|
||||
<template v-for="(part, index) in [renderNotifyParts(row)]" :key="`${row.id}-${index}`">
|
||||
<span>{{ part.prefix }}</span>
|
||||
<button
|
||||
v-if="part.clickable"
|
||||
type="button"
|
||||
class="notification-bell__inline-link"
|
||||
@click.stop="onNotifyLinkClick(row)"
|
||||
>
|
||||
{{ part.clickable }}
|
||||
</button>
|
||||
<span>{{ part.suffix }}</span>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ row.templateContent }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="notification-bell__row-meta">
|
||||
<DictTag :dict-code="NOTIFY_MESSAGE_LEVEL_DICT_CODE" :value="row.level" size="small" round />
|
||||
<span class="notification-bell__row-time">{{ formatRelativeTime(row.createTime) }}</span>
|
||||
@@ -350,7 +478,25 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="detailMessage" class="notification-bell__detail-body">
|
||||
<div class="notification-bell__detail-content">{{ detailMessage.templateContent }}</div>
|
||||
<div class="notification-bell__detail-content">
|
||||
<template v-if="canJump(detailMessage)">
|
||||
<template v-for="(part, index) in [renderNotifyParts(detailMessage)]" :key="`detail-${index}`">
|
||||
<span>{{ part.prefix }}</span>
|
||||
<button
|
||||
v-if="part.clickable"
|
||||
type="button"
|
||||
class="notification-bell__inline-link"
|
||||
@click.stop="onNotifyLinkClick(detailMessage)"
|
||||
>
|
||||
{{ part.clickable }}
|
||||
</button>
|
||||
<span>{{ part.suffix }}</span>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ detailMessage.templateContent }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="notification-bell__detail-time">收到于 {{ formatDateTime(detailMessage.createTime) }}</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -578,6 +724,19 @@ onBeforeUnmount(() => {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.notification-bell__inline-link {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.notification-bell__inline-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification-bell__row-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user