| | |
| | | <template> |
| | | <div> |
| | | <el-card shadow="never"> |
| | | <el-skeleton :loading="loading" animated> |
| | | <el-row :gutter="16" justify="space-between"> |
| | | <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> |
| | | <div class="flex items-center"> |
| | | <el-avatar :src="avatar" :size="70" class="mr-16px"> |
| | | <img src="@/assets/imgs/avatar.gif" alt="" /> |
| | | </el-avatar> |
| | | <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-16px 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-16px 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-16px 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 id="title"> |
| | | <span>工业互联网平台</span> |
| | | </div> |
| | | |
| | | <el-row class="mt-8px" :gutter="8" justify="space-between"> |
| | | <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px"> |
| | | <el-card shadow="never"> |
| | | <template #header> |
| | | <div class="h-3 flex justify-between"> |
| | | <span>{{ t('workplace.project') }}</span> |
| | | <el-link |
| | | type="primary" |
| | | :underline="false" |
| | | href="https://xxxx" |
| | | target="_blank" |
| | | > |
| | | {{ 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" class="mr-5px mt-5px"> |
| | | <div class="flex items-center"> |
| | | <Icon :icon="item.icon" :size="25" class="mr-8px" /> |
| | | <span class="text-16px">{{ item.name }}</span> |
| | | </div> |
| | | <div class="mt-12px text-9px text-gray-400">{{ t(item.message) }}</div> |
| | | <div class="mt-12px 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-8px"> |
| | | <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-8px"> |
| | | <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-8px"> |
| | | <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-8px"> |
| | | <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-8px"> |
| | | <div class="flex items-center"> |
| | | <Icon :icon="item.icon" class="mr-8px" /> |
| | | <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-8px"> |
| | | <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"> |
| | | <el-avatar :src="avatar" :size="35" class="mr-16px"> |
| | | <img src="@/assets/imgs/avatar.gif" alt="" /> |
| | | </el-avatar> |
| | | <div> |
| | | <div class="text-14px"> |
| | | <Highlight :keys="item.keys.map((v) => t(v))"> |
| | | {{ item.type }} : {{ item.title }} |
| | | </Highlight> |
| | | </div> |
| | | <div class="mt-16px text-12px text-gray-400"> |
| | | {{ formatTime(item.date, 'yyyy-MM-dd') }} |
| | | </div> |
| | | </div> |
| | | <el-skeleton :loading="loading" animated> |
| | | <div id="app"> |
| | | <div class="card" v-for="(item, index) in appList" :key="`dynamics-${index}`"> |
| | | <div> |
| | | <img class="card-left" :src="item.icon"/> |
| | | <div class="card-right"> |
| | | <div class="app-title"> |
| | | {{ item.appName }} |
| | | </div> |
| | | <el-divider /> |
| | | <div class="goto-app" @click="gotoApp(item)"> |
| | | <div>进入应用</div> |
| | | </div> |
| | | </div> |
| | | </el-skeleton> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-skeleton> |
| | | |
| | | </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 type { WorkplaceTotal, Project, Notice, Shortcut } from './types' |
| | | import { pieOptions, barOptions } from './echarts-data' |
| | | import * as AppApi from '@/api/system/app' |
| | | import {Apps} from "@/views/Home/types"; |
| | | import {CACHE_KEY, useCache, useSessionCache} from "@/hooks/web/useCache"; |
| | | |
| | | defineOptions({ name: 'Home' }) |
| | | |
| | | const { t } = useI18n() |
| | | const userStore = useUserStore() |
| | | const { setWatermark } = useWatermark() |
| | | defineOptions({name: 'Home'}) |
| | | |
| | | const {wsCache} = useCache() |
| | | const {wsSessionCache} = useSessionCache() |
| | | |
| | | const loading = ref(true) |
| | | const avatar = userStore.getUser.avatar |
| | | 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 appList = reactive<Apps[]>([]) |
| | | |
| | | const getAppList = async () => { |
| | | const data = await AppApi.getAppList() |
| | | appList = Object.assign(appList, data) |
| | | } |
| | | |
| | | // 获取项目数 |
| | | let projects = reactive<Project[]>([]) |
| | | const getProject = async () => { |
| | | const data = [ |
| | | { |
| | | name: 'iailab-plat', |
| | | icon: 'akar-icons:github-fill', |
| | | message: 'https://xxxx/iailab-plat', |
| | | personal: 'Spring Boot 单体架构', |
| | | time: new Date() |
| | | }, |
| | | { |
| | | name: 'iailab-plat-ui-vue3', |
| | | icon: 'logos:vue', |
| | | message: 'https://xxxx/iailab-plat-ui-vue3', |
| | | personal: 'Vue3 + element-plus', |
| | | time: new Date() |
| | | }, |
| | | { |
| | | name: 'iailab-plat-ui-vue2', |
| | | icon: 'logos:vue', |
| | | message: 'https://xxxx/iailab-plat-ui-vue2', |
| | | personal: 'Vue2 + element-ui', |
| | | time: new Date() |
| | | } |
| | | ] |
| | | projects = Object.assign(projects, data) |
| | | } |
| | | |
| | | // 获取通知公告 |
| | | let notice = reactive<Notice[]>([]) |
| | | const getNotice = async () => { |
| | | const data = [ |
| | | { |
| | | title: '系统支持 JDK 8/17/21,Vue 2/3', |
| | | type: '通知', |
| | | keys: ['通知', '8', '17', '21', '2', '3'], |
| | | date: new Date() |
| | | }, |
| | | { |
| | | title: '后端提供 Spring Boot 2.7/3.2 + Cloud 双架构', |
| | | type: '公告', |
| | | keys: ['公告', 'Boot', 'Cloud'], |
| | | date: new Date() |
| | | }, |
| | | { |
| | | title: '全部开源,个人与企业可 100% 直接使用,无需授权', |
| | | type: '通知', |
| | | keys: ['通知', '无需授权'], |
| | | date: new Date() |
| | | }, |
| | | { |
| | | title: '国内使用最广泛的快速开发平台,超 300+ 人贡献', |
| | | 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 getAppMenuList = async (id, appCode) => { |
| | | const data = await AppApi.getAppMenuList(id) |
| | | let userInfo = wsCache.get(CACHE_KEY.USER) |
| | | // userInfo.menus = data |
| | | wsCache.set(CACHE_KEY.USER, userInfo) |
| | | wsSessionCache.set(CACHE_KEY.ROLE_ROUTERS, data) |
| | | window.location.href = import.meta.env.VITE_BASE_PATH + 'index' |
| | | } |
| | | |
| | | const getAllApi = async () => { |
| | | await Promise.all([ |
| | | getCount(), |
| | | getProject(), |
| | | getNotice(), |
| | | getShortcut(), |
| | | getUserAccessSource(), |
| | | getWeeklyUserActivity() |
| | | getAppList() |
| | | ]) |
| | | loading.value = false |
| | | } |
| | | |
| | | getAllApi() |
| | | |
| | | // 进入应用 |
| | | const gotoApp = async (item) => { |
| | | let path = window.location.pathname |
| | | let appName = path.split("/")[0] |
| | | let id = item.id |
| | | let type = item.type |
| | | let appCode = item.appCode |
| | | if (type === 0) { |
| | | await getAppMenuList(id, appCode) |
| | | } else { |
| | | // const data = await AppApi.getAppMenuList(id) |
| | | // let userInfo = wsCache.get(CACHE_KEY.USER) |
| | | // userInfo.menus = data |
| | | // wsCache.set(CACHE_KEY.USER, userInfo) |
| | | // wsSessionCache.set(CACHE_KEY.ROLE_ROUTERS, data) |
| | | localStorage.setItem(appCode, id) |
| | | window.open(item.appDomain + '/index', '_blank') |
| | | // window.open('/plat/shasteel', '_blank') |
| | | // window.location.href = '/plat/shasteel' |
| | | // window.location.href = `/plat/shasteel?key=energy&url=http://localhost:9000&energy=/energy/demo` |
| | | } |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | #title { |
| | | width: 280px; |
| | | height: 51px; |
| | | margin: 30px auto; |
| | | font-family: Microsoft YaHei UI, Microsoft YaHei UI; |
| | | font-weight: bold; |
| | | font-size: 40px; |
| | | color: #282F3D; |
| | | } |
| | | |
| | | #app { |
| | | margin: 0 96px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .card { |
| | | width: 354px; |
| | | height: 200px; |
| | | margin: 0 24px 24px 0; |
| | | background: linear-gradient(180deg, #E9F0FA 0%, #FFFFFF 100%); |
| | | border-radius: 12px 12px 12px 12px; |
| | | border: 2px solid; |
| | | border-image: linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 1)) 2 2; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .card-left { |
| | | height: 100px; |
| | | width: 100px; |
| | | float: left; |
| | | margin: 50px 30px; |
| | | } |
| | | |
| | | .card-right { |
| | | float: right; |
| | | margin: 61px 10px; |
| | | } |
| | | |
| | | .app-title { |
| | | width: 162px; |
| | | font-family: Microsoft YaHei, Microsoft YaHei; |
| | | font-weight: bold; |
| | | font-size: 24px; |
| | | color: #282F3D; |
| | | } |
| | | |
| | | .goto-app { |
| | | width: 96px; |
| | | height: 35px; |
| | | margin-top: 5px; |
| | | background: #3A99FD; |
| | | border-radius: 80px 80px 80px 80px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .goto-app > div { |
| | | padding: 6px; |
| | | margin-left: 5px; |
| | | font-family: Microsoft YaHei UI, Microsoft YaHei UI; |
| | | font-weight: 400; |
| | | font-size: 18px; |
| | | color: #FFFFFF; |
| | | } |
| | | </style> |