migrate from github

This commit is contained in:
2026-07-15 22:51:56 +08:00
commit a1cccc8068
215 changed files with 38318 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
<template>
<PageLayout
:system-name="systemName"
:current-language="currentLanguage"
:is-dark-mode="isDarkMode"
:user-avatar="userAvatar"
:username="userInfo?.username || t('common.user')"
:active-menu="activeMenu"
:side-menu-items="userMenuItems"
sidebar-scene="user_info"
:title="t('user.notifications.title')"
layout="list"
@change-language="handleLanguageChange"
@toggle-theme="toggleTheme"
@logout="handleLogout"
@menu-select="handleMenuSelect"
>
<el-tabs v-model="activeTab" class="common-tabs">
<el-tab-pane :label="t('user.notifications.tabs.all')" name="all" />
<el-tab-pane :label="t('user.notifications.tabs.unread')" name="unread" />
</el-tabs>
<CommonList
:rows="displayNotifications"
:columns="columns"
:is-dark="isDarkMode"
row-key="external_id"
:empty-text="t('user.notifications.empty')"
:show-title-bar="true"
:title="t('user.notifications.title')"
:show-pagination="true"
:total="totalCount"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[6, 10, 20]"
@page-change="handlePageChange"
@size-change="handleSizeChange"
>
<template #toolbar-actions>
<el-button size="small" type="primary" @click="handleMarkRead">
{{ selectedIds.length ? t('user.notifications.markSelected') : t('user.notifications.markAll') }}
</el-button>
</template>
<template #header-select>
<el-checkbox
:indeterminate="selectIndeterminate"
:model-value="selectAll"
@change="toggleSelectAll"
class="checkbox-only"
/>
</template>
<template #cell-select="{ row }">
<el-checkbox v-model="selectedIds" :label="row.external_id" class="checkbox-only" />
</template>
<template #cell-title="{ row }">
<div class="notice-title" :class="{ 'is-unread': !row.read }">
<span v-if="!row.read" class="notice-dot" />
<div class="notice-text">
<div class="notice-main">{{ buildTitle(row) }}</div>
<div class="notice-sub">{{ row.content || '-' }}</div>
</div>
</div>
</template>
<template #cell-type="{ value }">
<AppTag size="small" :type="tagType(value)">
{{ tagLabel(value) }}
</AppTag>
</template>
<template #cell-created_at="{ value }">
{{ formatDate(value) }}
</template>
<template #cell-actions="{ row }">
<el-button size="small" text type="primary" @click="markRead(row)">
{{ t('user.notifications.markRead') }}
</el-button>
<el-button size="small" text type="primary" @click="openDetail(row)">
{{ t('user.notifications.view') }}
</el-button>
</template>
</CommonList>
</PageLayout>
</template>
<script setup>
import { onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/store/user';
import PageLayout from '@/components/layout/PageLayout.vue';
import CommonList from '@/components/list/CommonList.vue';
import AppTag from '@/components/base/AppTag.vue';
import { useUserShell } from '@/composables/shell/useUserShell';
import { useNotificationCenter } from '@/composables/notification/useNotificationCenter';
import { usePageBoot } from '@/composables/shell/usePageBoot';
import { markNotificationsRead } from '@/services/api';
const router = useRouter();
const userStore = useUserStore();
const { locale, t } = useI18n();
const {
systemName,
activeMenu,
isDarkMode,
userInfo,
userAvatar,
userMenuItems,
currentLanguage,
initLanguage,
fetchSystemInfo,
handleLanguageChange,
toggleTheme,
handleLogout,
handleMenuSelect
} = useUserShell({
locale,
router,
userStore,
defaultActiveMenu: 'user-notifications'
});
const { boot } = usePageBoot({ initLanguage, fetchSystemInfo });
const {
activeTab,
totalCount,
currentPage,
pageSize,
columns,
displayNotifications,
selectedIds,
selectAll,
selectIndeterminate,
toggleSelectAll,
tagType,
tagLabel,
buildTitle,
formatDate,
markRead,
handleMarkRead,
handlePageChange,
handleSizeChange,
init
} = useNotificationCenter({ t });
onMounted(() => {
boot(init);
});
const openDetail = (row) => {
const payload = (() => {
try { return JSON.parse(row.payload || '{}'); } catch (e) { console.warn('Invalid notification payload', e); return {}; }
})();
const docId = payload.document_external_id;
const commentId = payload.comment_external_id;
const kbId = payload.knowledge_base_external_id;
if (!docId) return;
// Fire-and-forget: mark read before navigating away (no need to reload list)
if (!row.read) {
markNotificationsRead([row.external_id]).catch(() => {});
}
const query = commentId ? { comment: commentId } : {};
if (kbId) {
router.push({ name: 'KnowledgeBaseDocumentEditor', params: { kbId, docId }, query });
} else {
router.push({ name: 'MyDocumentEditor', params: { docId }, query });
}
};
</script>
<style scoped>
.notice-title {
display: flex;
align-items: flex-start;
gap: 10px;
}
.notice-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-top: 6px;
background: var(--primary-color);
flex-shrink: 0;
}
.notice-text {
display: flex;
flex-direction: column;
gap: 4px;
}
.notice-main {
font-weight: 600;
color: var(--text-dark);
}
.notice-title.is-unread .notice-main {
color: var(--primary-color);
}
.notice-sub {
font-size: 12px;
color: var(--text-light);
}
.checkbox-only :deep(.el-checkbox__label) {
display: none;
}
:deep(.select-column) {
justify-content: center;
padding-left: 6px;
padding-right: 6px;
}
.dark-mode .notice-main {
color: #e5e7eb;
}
.dark-mode .notice-title.is-unread .notice-main {
color: #8ab4ff;
}
.dark-mode .notice-sub {
color: #9aa4b2;
}
</style>