zhang_li
12 months ago
2 changed files with 620 additions and 330 deletions
@ -0,0 +1,381 @@ |
|||
<template> |
|||
<div> |
|||
<el-card shadow="never"> |
|||
<el-skeleton :loading="loading" animated> |
|||
<el-row :gutter="20" justify="space-between"> |
|||
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> |
|||
<div class="flex items-center"> |
|||
<img :src="avatar" alt="" class="mr-20px h-70px w-70px rounded-[50%]" /> |
|||
<div> |
|||
<div class="text-20px"> |
|||
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }} |
|||
</div> |
|||
<div class="mt-10px text-14px text-gray-500"> |
|||
{{ t('workplace.toady') }},20℃ - 32℃! |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</el-col> |
|||
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> |
|||
<div class="h-70px flex items-center justify-end lt-sm:mt-10px"> |
|||
<div class="px-8px text-right"> |
|||
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.project') }}</div> |
|||
<CountTo |
|||
class="text-20px" |
|||
:start-val="0" |
|||
:end-val="totalSate.project" |
|||
:duration="2600" |
|||
/> |
|||
</div> |
|||
<el-divider direction="vertical" /> |
|||
<div class="px-8px text-right"> |
|||
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.toDo') }}</div> |
|||
<CountTo |
|||
class="text-20px" |
|||
:start-val="0" |
|||
:end-val="totalSate.todo" |
|||
:duration="2600" |
|||
/> |
|||
</div> |
|||
<el-divider direction="vertical" border-style="dashed" /> |
|||
<div class="px-8px text-right"> |
|||
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.access') }}</div> |
|||
<CountTo |
|||
class="text-20px" |
|||
:start-val="0" |
|||
:end-val="totalSate.access" |
|||
:duration="2600" |
|||
/> |
|||
</div> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-skeleton> |
|||
</el-card> |
|||
</div> |
|||
|
|||
<el-row class="mt-5px" :gutter="20" justify="space-between"> |
|||
<el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-10px"> |
|||
<el-card shadow="never"> |
|||
<template #header> |
|||
<div class="h-3 flex justify-between"> |
|||
<span>{{ t('workplace.project') }}</span> |
|||
<el-link type="primary" :underline="false">{{ t('action.more') }}</el-link> |
|||
</div> |
|||
</template> |
|||
<el-skeleton :loading="loading" animated> |
|||
<el-row> |
|||
<el-col |
|||
v-for="(item, index) in projects" |
|||
:key="`card-${index}`" |
|||
:xl="8" |
|||
:lg="8" |
|||
:md="8" |
|||
:sm="24" |
|||
:xs="24" |
|||
> |
|||
<el-card shadow="hover"> |
|||
<div class="flex items-center"> |
|||
<Icon :icon="item.icon" :size="25" class="mr-10px" /> |
|||
<span class="text-16px">{{ item.name }}</span> |
|||
</div> |
|||
<div class="mt-15px text-14px text-gray-400">{{ t(item.message) }}</div> |
|||
<div class="mt-20px flex justify-between text-12px text-gray-400"> |
|||
<span>{{ item.personal }}</span> |
|||
<span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</el-skeleton> |
|||
</el-card> |
|||
|
|||
<el-card shadow="never" class="mt-5px"> |
|||
<el-skeleton :loading="loading" animated> |
|||
<el-row :gutter="20" justify="space-between"> |
|||
<el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> |
|||
<el-card shadow="hover" class="mb-10px"> |
|||
<el-skeleton :loading="loading" animated> |
|||
<Echart :options="pieOptionsData" :height="280" /> |
|||
</el-skeleton> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24"> |
|||
<el-card shadow="hover" class="mb-10px"> |
|||
<el-skeleton :loading="loading" animated> |
|||
<Echart :options="barOptionsData" :height="280" /> |
|||
</el-skeleton> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</el-skeleton> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-10px"> |
|||
<el-card shadow="never"> |
|||
<template #header> |
|||
<div class="h-3 flex justify-between"> |
|||
<span>{{ t('workplace.shortcutOperation') }}</span> |
|||
</div> |
|||
</template> |
|||
<el-skeleton :loading="loading" animated> |
|||
<el-row> |
|||
<el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-10px"> |
|||
<div class="flex items-center"> |
|||
<Icon :icon="item.icon" class="mr-10px" /> |
|||
<el-link type="default" :underline="false" @click="setWatermark(item.name)"> |
|||
{{ item.name }} |
|||
</el-link> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-skeleton> |
|||
</el-card> |
|||
<el-card shadow="never" class="mt-10px"> |
|||
<template #header> |
|||
<div class="h-3 flex justify-between"> |
|||
<span>{{ t('workplace.notice') }}</span> |
|||
<el-link type="primary" :underline="false">{{ t('action.more') }}</el-link> |
|||
</div> |
|||
</template> |
|||
<el-skeleton :loading="loading" animated> |
|||
<div v-for="(item, index) in notice" :key="`dynamics-${index}`"> |
|||
<div class="flex items-center"> |
|||
<img :src="avatar" alt="" class="mr-20px h-35px w-35px rounded-[50%]" /> |
|||
<div> |
|||
<div class="text-14px"> |
|||
<Highlight :keys="item.keys.map((v) => t(v))"> |
|||
{{ item.type }} : {{ item.title }} |
|||
</Highlight> |
|||
</div> |
|||
<div class="mt-15px text-12px text-gray-400"> |
|||
{{ formatTime(item.date, 'yyyy-MM-dd') }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<el-divider /> |
|||
</div> |
|||
</el-skeleton> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</template> |
|||
<script lang="ts" setup> |
|||
import { set } from 'lodash-es' |
|||
import { EChartsOption } from 'echarts' |
|||
import { formatTime } from '@/utils' |
|||
|
|||
import { useUserStore } from '@/store/modules/user' |
|||
import { useWatermark } from '@/hooks/web/useWatermark' |
|||
import avatarImg from '@/assets/imgs/avatar.gif' |
|||
import type { WorkplaceTotal, Project, Notice, Shortcut } from './types' |
|||
import { pieOptions, barOptions } from './echarts-data' |
|||
|
|||
defineOptions({ name: 'Home' }) |
|||
|
|||
const { t } = useI18n() |
|||
const userStore = useUserStore() |
|||
const { setWatermark } = useWatermark() |
|||
const loading = ref(true) |
|||
const avatar = userStore.getUser.avatar ? userStore.getUser.avatar : avatarImg |
|||
const username = userStore.getUser.nickname |
|||
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption |
|||
// 获取统计数 |
|||
let totalSate = reactive<WorkplaceTotal>({ |
|||
project: 0, |
|||
access: 0, |
|||
todo: 0 |
|||
}) |
|||
|
|||
const getCount = async () => { |
|||
const data = { |
|||
project: 40, |
|||
access: 2340, |
|||
todo: 10 |
|||
} |
|||
totalSate = Object.assign(totalSate, data) |
|||
} |
|||
|
|||
// 获取项目数 |
|||
let projects = reactive<Project[]>([]) |
|||
const getProject = async () => { |
|||
const data = [ |
|||
{ |
|||
name: 'Github', |
|||
icon: 'akar-icons:github-fill', |
|||
message: 'workplace.introduction', |
|||
personal: 'Archer', |
|||
time: new Date() |
|||
}, |
|||
{ |
|||
name: 'Vue', |
|||
icon: 'logos:vue', |
|||
message: 'workplace.introduction', |
|||
personal: 'Archer', |
|||
time: new Date() |
|||
}, |
|||
{ |
|||
name: 'Angular', |
|||
icon: 'logos:angular-icon', |
|||
message: 'workplace.introduction', |
|||
personal: 'Archer', |
|||
time: new Date() |
|||
}, |
|||
{ |
|||
name: 'React', |
|||
icon: 'logos:react', |
|||
message: 'workplace.introduction', |
|||
personal: 'Archer', |
|||
time: new Date() |
|||
}, |
|||
{ |
|||
name: 'Webpack', |
|||
icon: 'logos:webpack', |
|||
message: 'workplace.introduction', |
|||
personal: 'Archer', |
|||
time: new Date() |
|||
}, |
|||
{ |
|||
name: 'Vite', |
|||
icon: 'vscode-icons:file-type-vite', |
|||
message: 'workplace.introduction', |
|||
personal: 'Archer', |
|||
time: new Date() |
|||
} |
|||
] |
|||
projects = Object.assign(projects, data) |
|||
} |
|||
|
|||
// 获取通知公告 |
|||
let notice = reactive<Notice[]>([]) |
|||
const getNotice = async () => { |
|||
const data = [ |
|||
{ |
|||
title: '系统升级版本', |
|||
type: '通知', |
|||
keys: ['通知', '升级'], |
|||
date: new Date() |
|||
}, |
|||
{ |
|||
title: '系统凌晨维护', |
|||
type: '公告', |
|||
keys: ['公告', '维护'], |
|||
date: new Date() |
|||
}, |
|||
{ |
|||
title: '系统升级版本', |
|||
type: '通知', |
|||
keys: ['通知', '升级'], |
|||
date: new Date() |
|||
}, |
|||
{ |
|||
title: '系统凌晨维护', |
|||
type: '公告', |
|||
keys: ['公告', '维护'], |
|||
date: new Date() |
|||
} |
|||
] |
|||
notice = Object.assign(notice, data) |
|||
} |
|||
|
|||
// 获取快捷入口 |
|||
let shortcut = reactive<Shortcut[]>([]) |
|||
|
|||
const getShortcut = async () => { |
|||
const data = [ |
|||
{ |
|||
name: 'Github', |
|||
icon: 'akar-icons:github-fill', |
|||
url: 'github.io' |
|||
}, |
|||
{ |
|||
name: 'Vue', |
|||
icon: 'logos:vue', |
|||
url: 'vuejs.org' |
|||
}, |
|||
{ |
|||
name: 'Vite', |
|||
icon: 'vscode-icons:file-type-vite', |
|||
url: 'https://vitejs.dev/' |
|||
}, |
|||
{ |
|||
name: 'Angular', |
|||
icon: 'logos:angular-icon', |
|||
url: 'github.io' |
|||
}, |
|||
{ |
|||
name: 'React', |
|||
icon: 'logos:react', |
|||
url: 'github.io' |
|||
}, |
|||
{ |
|||
name: 'Webpack', |
|||
icon: 'logos:webpack', |
|||
url: 'github.io' |
|||
} |
|||
] |
|||
shortcut = Object.assign(shortcut, data) |
|||
} |
|||
|
|||
// 用户来源 |
|||
const getUserAccessSource = async () => { |
|||
const data = [ |
|||
{ value: 335, name: 'analysis.directAccess' }, |
|||
{ value: 310, name: 'analysis.mailMarketing' }, |
|||
{ value: 234, name: 'analysis.allianceAdvertising' }, |
|||
{ value: 135, name: 'analysis.videoAdvertising' }, |
|||
{ value: 1548, name: 'analysis.searchEngines' } |
|||
] |
|||
set( |
|||
pieOptionsData, |
|||
'legend.data', |
|||
data.map((v) => t(v.name)) |
|||
) |
|||
pieOptionsData!.series![0].data = data.map((v) => { |
|||
return { |
|||
name: t(v.name), |
|||
value: v.value |
|||
} |
|||
}) |
|||
} |
|||
const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption |
|||
|
|||
// 周活跃量 |
|||
const getWeeklyUserActivity = async () => { |
|||
const data = [ |
|||
{ value: 13253, name: 'analysis.monday' }, |
|||
{ value: 34235, name: 'analysis.tuesday' }, |
|||
{ value: 26321, name: 'analysis.wednesday' }, |
|||
{ value: 12340, name: 'analysis.thursday' }, |
|||
{ value: 24643, name: 'analysis.friday' }, |
|||
{ value: 1322, name: 'analysis.saturday' }, |
|||
{ value: 1324, name: 'analysis.sunday' } |
|||
] |
|||
set( |
|||
barOptionsData, |
|||
'xAxis.data', |
|||
data.map((v) => t(v.name)) |
|||
) |
|||
set(barOptionsData, 'series', [ |
|||
{ |
|||
name: t('analysis.activeQuantity'), |
|||
data: data.map((v) => v.value), |
|||
type: 'bar' |
|||
} |
|||
]) |
|||
} |
|||
|
|||
const getAllApi = async () => { |
|||
await Promise.all([ |
|||
getCount(), |
|||
getProject(), |
|||
getNotice(), |
|||
getShortcut(), |
|||
getUserAccessSource(), |
|||
getWeeklyUserActivity() |
|||
]) |
|||
loading.value = false |
|||
} |
|||
|
|||
getAllApi() |
|||
</script> |
Loading…
Reference in new issue