3 changed files with 211 additions and 248 deletions
@ -1,266 +1,32 @@ |
|||||
<script lang="ts" setup> |
<script setup lang="ts"> |
||||
import type { |
import type { FavoriteMenu } from '@abp/platform'; |
||||
WorkbenchProjectItem, |
|
||||
WorkbenchQuickNavItem, |
|
||||
WorkbenchTodoItem, |
|
||||
WorkbenchTrendItem, |
|
||||
} from '@vben/common-ui'; |
|
||||
|
|
||||
import { ref } from 'vue'; |
|
||||
import { useRouter } from 'vue-router'; |
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 { openWindow } from '@vben/utils'; |
||||
|
|
||||
import AnalyticsVisitsSource from '../analytics/analytics-visits-source.vue'; |
import { Workbench } from '@abp/platform'; |
||||
|
|
||||
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', |
|
||||
}, |
|
||||
]; |
|
||||
|
|
||||
const router = useRouter(); |
const router = useRouter(); |
||||
|
function navTo(menu: FavoriteMenu) { |
||||
// 这是一个示例方法,实际项目中需要根据实际情况进行调整 |
if (menu.path?.startsWith('http')) { |
||||
// This is a sample method, adjust according to the actual project requirements |
openWindow(menu.path); |
||||
function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) { |
|
||||
if (nav.url?.startsWith('http')) { |
|
||||
openWindow(nav.url); |
|
||||
return; |
return; |
||||
} |
} |
||||
if (nav.url?.startsWith('/')) { |
if (menu.path?.startsWith('/')) { |
||||
router.push(nav.url).catch((error) => { |
router.push(menu.path).catch((error) => { |
||||
console.error('Navigation failed:', error); |
console.error('Navigation failed:', error); |
||||
}); |
}); |
||||
} else { |
} else { |
||||
console.warn(`Unknown URL for navigation item: ${nav.title} -> ${nav.url}`); |
console.warn( |
||||
|
`Unknown URL for navigation item: ${menu.displayName} -> ${menu.path}`, |
||||
|
); |
||||
} |
} |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div class="p-5"> |
<Workbench @nav-to="navTo" /> |
||||
<WorkbenchHeader |
|
||||
:avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar" |
|
||||
> |
|
||||
<template #title> |
|
||||
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧! |
|
||||
</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> |
</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