3 changed files with 211 additions and 248 deletions
@ -1,266 +1,32 @@ |
|||
<script lang="ts" setup> |
|||
import type { |
|||
WorkbenchProjectItem, |
|||
WorkbenchQuickNavItem, |
|||
WorkbenchTodoItem, |
|||
WorkbenchTrendItem, |
|||
} from '@vben/common-ui'; |
|||
<script setup lang="ts"> |
|||
import type { FavoriteMenu } from '@abp/platform'; |
|||
|
|||
import { ref } from 'vue'; |
|||
import { useRouter } from 'vue-router'; |
|||
|
|||
import { |
|||
AnalysisChartCard, |
|||
WorkbenchHeader, |
|||
WorkbenchProject, |
|||
WorkbenchQuickNav, |
|||
WorkbenchTodo, |
|||
WorkbenchTrends, |
|||
} from '@vben/common-ui'; |
|||
import { preferences } from '@vben/preferences'; |
|||
import { useUserStore } from '@vben/stores'; |
|||
import { openWindow } from '@vben/utils'; |
|||
|
|||
import AnalyticsVisitsSource from '../analytics/analytics-visits-source.vue'; |
|||
|
|||
const userStore = useUserStore(); |
|||
|
|||
// 这是一个示例数据,实际项目中需要根据实际情况进行调整 |
|||
// url 也可以是内部路由,在 navTo 方法中识别处理,进行内部跳转 |
|||
// 例如:url: /dashboard/workspace |
|||
const projectItems: WorkbenchProjectItem[] = [ |
|||
{ |
|||
color: '', |
|||
content: '不要等待机会,而要创造机会。', |
|||
date: '2021-04-01', |
|||
group: '开源组', |
|||
icon: 'carbon:logo-github', |
|||
title: 'Github', |
|||
url: 'https://github.com', |
|||
}, |
|||
{ |
|||
color: '#3fb27f', |
|||
content: '现在的你决定将来的你。', |
|||
date: '2021-04-01', |
|||
group: '算法组', |
|||
icon: 'ion:logo-vue', |
|||
title: 'Vue', |
|||
url: 'https://vuejs.org', |
|||
}, |
|||
{ |
|||
color: '#e18525', |
|||
content: '没有什么才能比努力更重要。', |
|||
date: '2021-04-01', |
|||
group: '上班摸鱼', |
|||
icon: 'ion:logo-html5', |
|||
title: 'Html5', |
|||
url: 'https://developer.mozilla.org/zh-CN/docs/Web/HTML', |
|||
}, |
|||
{ |
|||
color: '#bf0c2c', |
|||
content: '热情和欲望可以突破一切难关。', |
|||
date: '2021-04-01', |
|||
group: 'UI', |
|||
icon: 'ion:logo-angular', |
|||
title: 'Angular', |
|||
url: 'https://angular.io', |
|||
}, |
|||
{ |
|||
color: '#00d8ff', |
|||
content: '健康的身体是实现目标的基石。', |
|||
date: '2021-04-01', |
|||
group: '技术牛', |
|||
icon: 'bx:bxl-react', |
|||
title: 'React', |
|||
url: 'https://reactjs.org', |
|||
}, |
|||
{ |
|||
color: '#EBD94E', |
|||
content: '路是走出来的,而不是空想出来的。', |
|||
date: '2021-04-01', |
|||
group: '架构组', |
|||
icon: 'ion:logo-javascript', |
|||
title: 'Js', |
|||
url: 'https://developer.mozilla.org/zh-CN/docs/Web/JavaScript', |
|||
}, |
|||
]; |
|||
|
|||
// 同样,这里的 url 也可以使用以 http 开头的外部链接 |
|||
const quickNavItems: WorkbenchQuickNavItem[] = [ |
|||
{ |
|||
color: '#1fdaca', |
|||
icon: 'ion:home-outline', |
|||
title: '首页', |
|||
url: '/', |
|||
}, |
|||
{ |
|||
color: '#bf0c2c', |
|||
icon: 'ion:grid-outline', |
|||
title: '仪表盘', |
|||
url: '/dashboard', |
|||
}, |
|||
{ |
|||
color: '#e18525', |
|||
icon: 'ion:layers-outline', |
|||
title: '组件', |
|||
url: '/demos/features/icons', |
|||
}, |
|||
{ |
|||
color: '#3fb27f', |
|||
icon: 'ion:settings-outline', |
|||
title: '系统管理', |
|||
url: '/demos/features/login-expired', // 这里的 URL 是示例,实际项目中需要根据实际情况进行调整 |
|||
}, |
|||
{ |
|||
color: '#4daf1bc9', |
|||
icon: 'ion:key-outline', |
|||
title: '权限管理', |
|||
url: '/demos/access/page-control', |
|||
}, |
|||
{ |
|||
color: '#00d8ff', |
|||
icon: 'ion:bar-chart-outline', |
|||
title: '图表', |
|||
url: '/analytics', |
|||
}, |
|||
]; |
|||
|
|||
const todoItems = ref<WorkbenchTodoItem[]>([ |
|||
{ |
|||
completed: false, |
|||
content: `审查最近提交到Git仓库的前端代码,确保代码质量和规范。`, |
|||
date: '2024-07-30 11:00:00', |
|||
title: '审查前端代码提交', |
|||
}, |
|||
{ |
|||
completed: true, |
|||
content: `检查并优化系统性能,降低CPU使用率。`, |
|||
date: '2024-07-30 11:00:00', |
|||
title: '系统性能优化', |
|||
}, |
|||
{ |
|||
completed: false, |
|||
content: `进行系统安全检查,确保没有安全漏洞或未授权的访问。 `, |
|||
date: '2024-07-30 11:00:00', |
|||
title: '安全检查', |
|||
}, |
|||
{ |
|||
completed: false, |
|||
content: `更新项目中的所有npm依赖包,确保使用最新版本。`, |
|||
date: '2024-07-30 11:00:00', |
|||
title: '更新项目依赖', |
|||
}, |
|||
{ |
|||
completed: false, |
|||
content: `修复用户报告的页面UI显示问题,确保在不同浏览器中显示一致。 `, |
|||
date: '2024-07-30 11:00:00', |
|||
title: '修复UI显示问题', |
|||
}, |
|||
]); |
|||
const trendItems: WorkbenchTrendItem[] = [ |
|||
{ |
|||
avatar: 'svg:avatar-1', |
|||
content: `在 <a>开源组</a> 创建了项目 <a>Vue</a>`, |
|||
date: '刚刚', |
|||
title: '威廉', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-2', |
|||
content: `关注了 <a>威廉</a> `, |
|||
date: '1个小时前', |
|||
title: '艾文', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-3', |
|||
content: `发布了 <a>个人动态</a> `, |
|||
date: '1天前', |
|||
title: '克里斯', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-4', |
|||
content: `发表文章 <a>如何编写一个Vite插件</a> `, |
|||
date: '2天前', |
|||
title: 'Vben', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-1', |
|||
content: `回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>`, |
|||
date: '3天前', |
|||
title: '皮特', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-2', |
|||
content: `关闭了问题 <a>如何运行项目</a> `, |
|||
date: '1周前', |
|||
title: '杰克', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-3', |
|||
content: `发布了 <a>个人动态</a> `, |
|||
date: '1周前', |
|||
title: '威廉', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-4', |
|||
content: `推送了代码到 <a>Github</a>`, |
|||
date: '2021-04-01 20:00', |
|||
title: '威廉', |
|||
}, |
|||
{ |
|||
avatar: 'svg:avatar-4', |
|||
content: `发表文章 <a>如何编写使用 Admin Vben</a> `, |
|||
date: '2021-03-01 20:00', |
|||
title: 'Vben', |
|||
}, |
|||
]; |
|||
import { Workbench } from '@abp/platform'; |
|||
|
|||
const router = useRouter(); |
|||
|
|||
// 这是一个示例方法,实际项目中需要根据实际情况进行调整 |
|||
// This is a sample method, adjust according to the actual project requirements |
|||
function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) { |
|||
if (nav.url?.startsWith('http')) { |
|||
openWindow(nav.url); |
|||
function navTo(menu: FavoriteMenu) { |
|||
if (menu.path?.startsWith('http')) { |
|||
openWindow(menu.path); |
|||
return; |
|||
} |
|||
if (nav.url?.startsWith('/')) { |
|||
router.push(nav.url).catch((error) => { |
|||
if (menu.path?.startsWith('/')) { |
|||
router.push(menu.path).catch((error) => { |
|||
console.error('Navigation failed:', error); |
|||
}); |
|||
} else { |
|||
console.warn(`Unknown URL for navigation item: ${nav.title} -> ${nav.url}`); |
|||
console.warn( |
|||
`Unknown URL for navigation item: ${menu.displayName} -> ${menu.path}`, |
|||
); |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="p-5"> |
|||
<WorkbenchHeader |
|||
:avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar" |
|||
> |
|||
<template #title> |
|||
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧! |
|||
<Workbench @nav-to="navTo" /> |
|||
</template> |
|||
<template #description> 今日晴,20℃ - 32℃! </template> |
|||
</WorkbenchHeader> |
|||
|
|||
<div class="mt-5 flex flex-col lg:flex-row"> |
|||
<div class="mr-4 w-full lg:w-3/5"> |
|||
<WorkbenchProject :items="projectItems" title="项目" @click="navTo" /> |
|||
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" /> |
|||
</div> |
|||
<div class="w-full lg:w-2/5"> |
|||
<WorkbenchQuickNav |
|||
:items="quickNavItems" |
|||
class="mt-5 lg:mt-0" |
|||
title="快捷导航" |
|||
@click="navTo" |
|||
/> |
|||
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> |
|||
<AnalysisChartCard class="mt-5" title="访问来源"> |
|||
<AnalyticsVisitsSource /> |
|||
</AnalysisChartCard> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<style scoped></style> |
|||
|
|||
@ -0,0 +1,195 @@ |
|||
<script setup lang="ts"> |
|||
import type { WorkbenchTodoItem, WorkbenchTrendItem } from '@vben/common-ui'; |
|||
|
|||
import type { FavoriteMenu } from './types'; |
|||
|
|||
import { computed, onMounted, ref } from 'vue'; |
|||
|
|||
import { useAppConfig } from '@vben/hooks'; |
|||
import { $t } from '@vben/locales'; |
|||
import { preferences } from '@vben/preferences'; |
|||
import { useUserStore } from '@vben/stores'; |
|||
|
|||
import { formatToDateTime } from '@abp/core'; |
|||
import { |
|||
NotificationReadState, |
|||
useMyNotifilersApi, |
|||
useNotificationSerializer, |
|||
} from '@abp/notifications'; |
|||
import { Empty } from 'ant-design-vue'; |
|||
|
|||
import { useMyFavoriteMenusApi } from '../../api/useMyFavoriteMenusApi'; |
|||
import WorkbenchHeader from './components/WorkbenchHeader.vue'; |
|||
import WorkbenchQuickNav from './components/WorkbenchQuickNav.vue'; |
|||
import WorkbenchTodo from './components/WorkbenchTodo.vue'; |
|||
import WorkbenchTrends from './components/WorkbenchTrends.vue'; |
|||
|
|||
defineEmits<{ |
|||
(event: 'navTo', menu: FavoriteMenu): void; |
|||
}>(); |
|||
|
|||
const userStore = useUserStore(); |
|||
const { getMyNotifilersApi } = useMyNotifilersApi(); |
|||
const { getListApi: getFavoriteMenusApi } = useMyFavoriteMenusApi(); |
|||
const { deserialize } = useNotificationSerializer(); |
|||
const { uiFramework } = useAppConfig(import.meta.env, import.meta.env.PROD); |
|||
|
|||
const defaultMenus: FavoriteMenu[] = [ |
|||
{ |
|||
id: '1', |
|||
color: '#1fdaca', |
|||
icon: 'ion:home-outline', |
|||
displayName: $t('workbench.content.favoriteMenu.home'), |
|||
path: '/', |
|||
isDefault: true, |
|||
}, |
|||
{ |
|||
id: '2', |
|||
color: '#bf0c2c', |
|||
icon: 'ion:grid-outline', |
|||
displayName: $t('workbench.content.favoriteMenu.dashboard'), |
|||
path: '/', |
|||
isDefault: true, |
|||
}, |
|||
{ |
|||
id: '3', |
|||
color: '#00d8ff', |
|||
icon: 'ant-design:notification-outlined', |
|||
displayName: $t('workbench.content.favoriteMenu.notifiers'), |
|||
path: '/manage/notifications/my-notifilers', |
|||
isDefault: true, |
|||
}, |
|||
{ |
|||
id: '4', |
|||
color: '#4daf1bc9', |
|||
icon: 'tdesign:user-setting', |
|||
displayName: $t('workbench.content.favoriteMenu.settings'), |
|||
path: '/account/my-settings', |
|||
isDefault: true, |
|||
}, |
|||
{ |
|||
id: '5', |
|||
color: '#3fb27f', |
|||
icon: 'hugeicons:profile-02', |
|||
displayName: $t('workbench.content.favoriteMenu.profile'), |
|||
path: '/account/profile', |
|||
isDefault: true, |
|||
}, |
|||
]; |
|||
const unReadNotifilerCount = ref(0); |
|||
const unReadNotifilers = ref<WorkbenchTrendItem[]>([]); |
|||
const favoriteMenus = ref<FavoriteMenu[]>([]); |
|||
const todoList = ref<WorkbenchTodoItem[]>([]); |
|||
|
|||
const getFavoriteMenus = computed(() => { |
|||
return [...defaultMenus, ...favoriteMenus.value]; |
|||
}); |
|||
const getWelcomeTitle = computed(() => { |
|||
const now = new Date(); |
|||
const hour = now.getHours(); |
|||
if (hour < 12) { |
|||
return $t('workbench.header.welcome.morning', [ |
|||
userStore.userInfo?.realName, |
|||
]); |
|||
} |
|||
if (hour < 14) { |
|||
return $t('workbench.header.welcome.atoon', [userStore.userInfo?.realName]); |
|||
} |
|||
if (hour < 17) { |
|||
return $t('workbench.header.welcome.afternoon', [ |
|||
userStore.userInfo?.realName, |
|||
]); |
|||
} |
|||
if (hour < 24) { |
|||
return $t('workbench.header.welcome.evening', [ |
|||
userStore.userInfo?.realName, |
|||
]); |
|||
} |
|||
return ''; |
|||
}); |
|||
async function onInit() { |
|||
await Promise.all([ |
|||
onInitFavoriteMenus(), |
|||
onInitNotifiers(), |
|||
onInitTodoList(), |
|||
]); |
|||
} |
|||
async function onInitFavoriteMenus() { |
|||
const { items } = await getFavoriteMenusApi(uiFramework); |
|||
favoriteMenus.value = items.map((item) => { |
|||
return { |
|||
...item, |
|||
isDefault: false, |
|||
}; |
|||
}); |
|||
} |
|||
async function onInitNotifiers() { |
|||
const { items, totalCount } = await getMyNotifilersApi({ |
|||
maxResultCount: 10, |
|||
readState: NotificationReadState.UnRead, |
|||
}); |
|||
unReadNotifilers.value = items.map((item) => { |
|||
const notifier = deserialize(item); |
|||
return { |
|||
avatar: '', |
|||
date: formatToDateTime(item.creationTime), |
|||
title: notifier.title, |
|||
content: notifier.message, |
|||
}; |
|||
}); |
|||
unReadNotifilerCount.value = totalCount; |
|||
} |
|||
async function onInitTodoList() { |
|||
// TODO: 实现待办事项列表 |
|||
todoList.value = []; |
|||
} |
|||
|
|||
onMounted(onInit); |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="p-5"> |
|||
<WorkbenchHeader |
|||
:avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar" |
|||
:text="userStore.userInfo?.realName" |
|||
:notifier-count="unReadNotifilerCount" |
|||
> |
|||
<template #title> |
|||
{{ getWelcomeTitle }} |
|||
</template> |
|||
<template #description> 今日晴,20℃ - 32℃! </template> |
|||
</WorkbenchHeader> |
|||
|
|||
<div class="mt-5 flex flex-col lg:flex-row"> |
|||
<div class="mr-4 w-full lg:w-3/5"> |
|||
<WorkbenchQuickNav |
|||
:items="getFavoriteMenus" |
|||
class="mt-5 lg:mt-0" |
|||
:title="$t('workbench.content.favoriteMenu.title')" |
|||
@click="(menu: FavoriteMenu) => $emit('navTo', menu)" |
|||
/> |
|||
<WorkbenchTodo |
|||
:items="todoList" |
|||
class="mt-5" |
|||
:title="$t('workbench.content.todo.title')" |
|||
> |
|||
<template #empty> |
|||
<Empty /> |
|||
</template> |
|||
</WorkbenchTodo> |
|||
</div> |
|||
<div class="w-full lg:w-2/5"> |
|||
<WorkbenchTrends |
|||
:items="unReadNotifilers" |
|||
:title="$t('workbench.content.trends.title')" |
|||
> |
|||
<template #empty> |
|||
<Empty /> |
|||
</template> |
|||
</WorkbenchTrends> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
Loading…
Reference in new issue