mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	refactor: vue3 index
This commit is contained in:
		| @@ -6,7 +6,7 @@ | |||||||
|   "private": false, |   "private": false, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "i": "pnpm install", |     "i": "pnpm install", | ||||||
|     "dev": "vite --mode base", |     "dev": "vite --mode base --open", | ||||||
|     "ts:check": "vue-tsc --noEmit", |     "ts:check": "vue-tsc --noEmit", | ||||||
|     "build:pro": "vite build --mode pro", |     "build:pro": "vite build --mode pro", | ||||||
|     "build:dev": "vite build --mode dev", |     "build:dev": "vite build --mode dev", | ||||||
|   | |||||||
| @@ -171,15 +171,16 @@ export default { | |||||||
|     sunday: 'Sunday' |     sunday: 'Sunday' | ||||||
|   }, |   }, | ||||||
|   workplace: { |   workplace: { | ||||||
|     goodMorning: 'Good morning', |     welcome: 'Hello', | ||||||
|     happyDay: 'Wish you happy every day!', |     happyDay: 'Wish you happy every day!', | ||||||
|     toady: `It's sunny today`, |     toady: `It's sunny today`, | ||||||
|  |     notice: 'Announcement', | ||||||
|     project: 'Project', |     project: 'Project', | ||||||
|     access: 'Project access', |     access: 'Project access', | ||||||
|     toDo: 'To do', |     toDo: 'To do', | ||||||
|     introduction: 'A serious introduction', |     introduction: 'A serious introduction', | ||||||
|     more: 'More', |     more: 'More', | ||||||
|     shortcutOperation: 'Shortcut operation', |     shortcutOperation: 'Quick entry', | ||||||
|     operation: 'Operation', |     operation: 'Operation', | ||||||
|     index: 'Index', |     index: 'Index', | ||||||
|     personal: 'Personal', |     personal: 'Personal', | ||||||
|   | |||||||
| @@ -171,15 +171,16 @@ export default { | |||||||
|     sunday: '周日' |     sunday: '周日' | ||||||
|   }, |   }, | ||||||
|   workplace: { |   workplace: { | ||||||
|     goodMorning: '早安', |     welcome: '你好', | ||||||
|     happyDay: '祝你开心每一天!', |     happyDay: '祝你开心每一天!', | ||||||
|     toady: '今日晴', |     toady: '今日晴', | ||||||
|  |     notice: '通知公告', | ||||||
|     project: '项目数', |     project: '项目数', | ||||||
|     access: '项目访问', |     access: '项目访问', | ||||||
|     toDo: '待办', |     toDo: '待办', | ||||||
|     introduction: '一个正经的简介', |     introduction: '一个正经的简介', | ||||||
|     more: '更多', |     more: '更多', | ||||||
|     shortcutOperation: '快捷操作', |     shortcutOperation: '快捷入口', | ||||||
|     operation: '操作', |     operation: '操作', | ||||||
|     index: '指数', |     index: '指数', | ||||||
|     personal: '个人', |     personal: '个人', | ||||||
|   | |||||||
| @@ -1,38 +1,187 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref, reactive } from 'vue' | import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus' | ||||||
| import { set } from 'lodash-es' |  | ||||||
| import { EChartsOption } from 'echarts' |  | ||||||
| import { Echart } from '@/components/Echart' |  | ||||||
| import { useI18n } from '@/hooks/web/useI18n' | import { useI18n } from '@/hooks/web/useI18n' | ||||||
|  | import { ref, reactive } from 'vue' | ||||||
| import { CountTo } from '@/components/CountTo' | import { CountTo } from '@/components/CountTo' | ||||||
| import type { AnalysisTotalTypes } from './types' | import { formatTime } from '@/utils' | ||||||
| import { useDesign } from '@/hooks/web/useDesign' | import { Echart } from '@/components/Echart' | ||||||
| import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus' | import { EChartsOption } from 'echarts' | ||||||
|  | import { radarOption } from './echarts-data' | ||||||
|  | import { Highlight } from '@/components/Highlight' | ||||||
|  | import type { WorkplaceTotal, Project, Notice, Shortcut } from './types' | ||||||
|  | import { set } from 'lodash-es' | ||||||
|  | import { useCache } from '@/hooks/web/useCache' | ||||||
| import { pieOptions, barOptions, lineOptions } from './echarts-data' | import { pieOptions, barOptions, lineOptions } from './echarts-data' | ||||||
|  |  | ||||||
| const { t } = useI18n() | const { t } = useI18n() | ||||||
|  | const { wsCache } = useCache() | ||||||
| const loading = ref(true) | const loading = ref(true) | ||||||
| const { getPrefixCls } = useDesign() | const avatar = wsCache.get('user').user.avatar | ||||||
| const prefixCls = getPrefixCls('panel') | const username = wsCache.get('user').user.nickname | ||||||
| const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption | const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption | ||||||
|  | // 获取统计数 | ||||||
| let totalState = reactive<AnalysisTotalTypes>({ | let totalSate = reactive<WorkplaceTotal>({ | ||||||
|   users: 0, |   project: 0, | ||||||
|   messages: 0, |   access: 0, | ||||||
|   moneys: 0, |   todo: 0 | ||||||
|   shoppings: 0 |  | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const getCount = async () => { | const getCount = async () => { | ||||||
|   const data = { |   const data = { | ||||||
|     users: 102400, |     project: 40, | ||||||
|     messages: 81212, |     access: 2340, | ||||||
|     moneys: 9280, |     todo: 10 | ||||||
|     shoppings: 13600 |  | ||||||
|   } |   } | ||||||
|   totalState = Object.assign(totalState, data) |   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 = [ | ||||||
|  |     { | ||||||
|  |       keys: ['workplace.push', 'Github'], | ||||||
|  |       time: new Date() | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       keys: ['workplace.push', 'Github'], | ||||||
|  |       time: new Date() | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       keys: ['workplace.push', 'Github'], | ||||||
|  |       time: 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) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取指数 | ||||||
|  | let radarOptionData = reactive<EChartsOption>(radarOption) as EChartsOption | ||||||
|  |  | ||||||
|  | const getRadar = async () => { | ||||||
|  |   const data = [ | ||||||
|  |     { name: 'workplace.quote', max: 65, personal: 42, team: 50 }, | ||||||
|  |     { name: 'workplace.contribution', max: 160, personal: 30, team: 140 }, | ||||||
|  |     { name: 'workplace.hot', max: 300, personal: 20, team: 28 }, | ||||||
|  |     { name: 'workplace.yield', max: 130, personal: 35, team: 35 }, | ||||||
|  |     { name: 'workplace.follow', max: 100, personal: 80, team: 90 } | ||||||
|  |   ] | ||||||
|  |   set( | ||||||
|  |     radarOptionData, | ||||||
|  |     'radar.indicator', | ||||||
|  |     data.map((v) => { | ||||||
|  |       return { | ||||||
|  |         name: t(v.name), | ||||||
|  |         max: v.max | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   ) | ||||||
|  |   set(radarOptionData, 'series', [ | ||||||
|  |     { | ||||||
|  |       name: '指数', | ||||||
|  |       type: 'radar', | ||||||
|  |       data: [ | ||||||
|  |         { | ||||||
|  |           value: data.map((v) => v.personal), | ||||||
|  |           name: t('workplace.personal') | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: data.map((v) => v.team), | ||||||
|  |           name: t('workplace.team') | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|  |     } | ||||||
|  |   ]) | ||||||
|  | } | ||||||
| // 用户来源 | // 用户来源 | ||||||
| const getUserAccessSource = async () => { | const getUserAccessSource = async () => { | ||||||
|   const data = [ |   const data = [ | ||||||
| @@ -121,7 +270,16 @@ const getMonthlySales = async () => { | |||||||
| } | } | ||||||
|  |  | ||||||
| const getAllApi = async () => { | const getAllApi = async () => { | ||||||
|   await Promise.all([getCount(), getUserAccessSource(), getWeeklyUserActivity(), getMonthlySales()]) |   await Promise.all([ | ||||||
|  |     getCount(), | ||||||
|  |     getProject(), | ||||||
|  |     getNotice(), | ||||||
|  |     getShortcut(), | ||||||
|  |     getRadar(), | ||||||
|  |     getUserAccessSource(), | ||||||
|  |     getWeeklyUserActivity(), | ||||||
|  |     getMonthlySales() | ||||||
|  |   ]) | ||||||
|   loading.value = false |   loading.value = false | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -129,123 +287,99 @@ getAllApi() | |||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <template> | <template> | ||||||
|   <el-row :gutter="20" justify="space-between" :class="prefixCls"> |  | ||||||
|     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> |  | ||||||
|       <el-card shadow="hover" class="mb-20px"> |  | ||||||
|         <el-skeleton :loading="loading" animated :rows="2"> |  | ||||||
|           <template #default> |  | ||||||
|             <div :class="`${prefixCls}__item flex justify-between`"> |  | ||||||
|   <div> |   <div> | ||||||
|                 <div |     <el-card shadow="never"> | ||||||
|                   :class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`" |       <el-skeleton :loading="loading" animated> | ||||||
|                 > |         <el-row :gutter="20" justify="space-between"> | ||||||
|                   <Icon icon="svg-icon:peoples" :size="40" /> |           <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> | ||||||
|  |             <div class="flex items-center"> | ||||||
|  |               <img :src="avatar" alt="" class="w-70px h-70px rounded-[50%] mr-20px" /> | ||||||
|  |               <div> | ||||||
|  |                 <div class="text-20px text-700"> | ||||||
|  |                   {{ 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> |               </div> | ||||||
|               <div class="flex flex-col justify-between"> |             </div> | ||||||
|                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ |           </el-col> | ||||||
|                   t('analysis.newUser') |           <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> | ||||||
|                 }}</div> |             <div class="flex h-70px items-center justify-end <sm:mt-20px"> | ||||||
|  |               <div class="px-8px text-right"> | ||||||
|  |                 <div class="text-14px text-gray-400 mb-20px">{{ t('workplace.project') }}</div> | ||||||
|                 <CountTo |                 <CountTo | ||||||
|                   class="text-20px font-700 text-right" |                   class="text-20px" | ||||||
|                   :start-val="0" |                   :start-val="0" | ||||||
|                   :end-val="102400" |                   :end-val="totalSate.project" | ||||||
|  |                   :duration="2600" | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |               <el-divider direction="vertical" /> | ||||||
|  |               <div class="px-8px text-right"> | ||||||
|  |                 <div class="text-14px text-gray-400 mb-20px">{{ 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="text-14px text-gray-400 mb-20px">{{ t('workplace.access') }}</div> | ||||||
|  |                 <CountTo | ||||||
|  |                   class="text-20px" | ||||||
|  |                   :start-val="0" | ||||||
|  |                   :end-val="totalSate.access" | ||||||
|                   :duration="2600" |                   :duration="2600" | ||||||
|                 /> |                 /> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|           </template> |           </el-col> | ||||||
|  |         </el-row> | ||||||
|       </el-skeleton> |       </el-skeleton> | ||||||
|     </el-card> |     </el-card> | ||||||
|     </el-col> |   </div> | ||||||
|  |  | ||||||
|     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> |   <el-row class="mt-20px" :gutter="20" justify="space-between"> | ||||||
|       <el-card shadow="hover" class="mb-20px"> |     <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-20px"> | ||||||
|         <el-skeleton :loading="loading" animated :rows="2"> |       <el-card shadow="never"> | ||||||
|           <template #default> |         <template #header> | ||||||
|             <div :class="`${prefixCls}__item flex justify-between`"> |           <div class="flex justify-between"> | ||||||
|               <div> |             <span>{{ t('workplace.project') }}</span> | ||||||
|                 <div |             <el-link type="primary" :underline="false">{{ t('workplace.more') }}</el-link> | ||||||
|                   :class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`" |  | ||||||
|                 > |  | ||||||
|                   <Icon icon="svg-icon:message" :size="40" /> |  | ||||||
|                 </div> |  | ||||||
|               </div> |  | ||||||
|               <div class="flex flex-col justify-between"> |  | ||||||
|                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ |  | ||||||
|                   t('analysis.unreadInformation') |  | ||||||
|                 }}</div> |  | ||||||
|                 <CountTo |  | ||||||
|                   class="text-20px font-700 text-right" |  | ||||||
|                   :start-val="0" |  | ||||||
|                   :end-val="81212" |  | ||||||
|                   :duration="2600" |  | ||||||
|                 /> |  | ||||||
|               </div> |  | ||||||
|           </div> |           </div> | ||||||
|         </template> |         </template> | ||||||
|         </el-skeleton> |         <el-skeleton :loading="loading" animated> | ||||||
|       </el-card> |           <el-row> | ||||||
|     </el-col> |             <el-col | ||||||
|  |               v-for="(item, index) in projects" | ||||||
|     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> |               :key="`card-${index}`" | ||||||
|       <el-card shadow="hover" class="mb-20px"> |               :xl="8" | ||||||
|         <el-skeleton :loading="loading" animated :rows="2"> |               :lg="8" | ||||||
|           <template #default> |               :md="12" | ||||||
|             <div :class="`${prefixCls}__item flex justify-between`"> |               :sm="24" | ||||||
|               <div> |               :xs="24" | ||||||
|                 <div |  | ||||||
|                   :class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`" |  | ||||||
|             > |             > | ||||||
|                   <Icon icon="svg-icon:money" :size="40" /> |               <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> | ||||||
|  |                 <div class="mt-15px text-14px text-gray-400">{{ t(item.message) }}</div> | ||||||
|  |                 <div class="mt-20px text-12px text-gray-400 flex justify-between"> | ||||||
|  |                   <span>{{ item.personal }}</span> | ||||||
|  |                   <span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span> | ||||||
|                 </div> |                 </div> | ||||||
|               <div class="flex flex-col justify-between"> |  | ||||||
|                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ |  | ||||||
|                   t('analysis.transactionAmount') |  | ||||||
|                 }}</div> |  | ||||||
|                 <CountTo |  | ||||||
|                   class="text-20px font-700 text-right" |  | ||||||
|                   :start-val="0" |  | ||||||
|                   :end-val="9280" |  | ||||||
|                   :duration="2600" |  | ||||||
|                 /> |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|           </template> |  | ||||||
|         </el-skeleton> |  | ||||||
|       </el-card> |  | ||||||
|     </el-col> |  | ||||||
|  |  | ||||||
|     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> |  | ||||||
|       <el-card shadow="hover" class="mb-20px"> |  | ||||||
|         <el-skeleton :loading="loading" animated :rows="2"> |  | ||||||
|           <template #default> |  | ||||||
|             <div :class="`${prefixCls}__item flex justify-between`"> |  | ||||||
|               <div> |  | ||||||
|                 <div |  | ||||||
|                   :class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`" |  | ||||||
|                 > |  | ||||||
|                   <Icon icon="svg-icon:shopping" :size="40" /> |  | ||||||
|                 </div> |  | ||||||
|               </div> |  | ||||||
|               <div class="flex flex-col justify-between"> |  | ||||||
|                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ |  | ||||||
|                   t('analysis.totalShopping') |  | ||||||
|                 }}</div> |  | ||||||
|                 <CountTo |  | ||||||
|                   class="text-20px font-700 text-right" |  | ||||||
|                   :start-val="0" |  | ||||||
|                   :end-val="13600" |  | ||||||
|                   :duration="2600" |  | ||||||
|                 /> |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|           </template> |  | ||||||
|         </el-skeleton> |  | ||||||
|               </el-card> |               </el-card> | ||||||
|             </el-col> |             </el-col> | ||||||
|           </el-row> |           </el-row> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |  | ||||||
|  |       <el-card shadow="never" class="mt-20px"> | ||||||
|  |         <el-skeleton :loading="loading" animated> | ||||||
|           <el-row :gutter="20" justify="space-between"> |           <el-row :gutter="20" justify="space-between"> | ||||||
|             <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> |             <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> | ||||||
|               <el-card shadow="hover" class="mb-20px"> |               <el-card shadow="hover" class="mb-20px"> | ||||||
| @@ -269,48 +403,61 @@ getAllApi() | |||||||
|               </el-card> |               </el-card> | ||||||
|             </el-col> |             </el-col> | ||||||
|           </el-row> |           </el-row> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |     </el-col> | ||||||
|  |     <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-20px"> | ||||||
|  |       <el-card shadow="never"> | ||||||
|  |         <template #header> | ||||||
|  |           <span>{{ t('workplace.shortcutOperation') }}</span> | ||||||
|  |         </template> | ||||||
|  |         <el-skeleton :loading="loading" animated> | ||||||
|  |           <el-row> | ||||||
|  |             <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="12" class="mb-20px"> | ||||||
|  |               <div class="flex items-center"> | ||||||
|  |                 <Icon :icon="item.icon" class="mr-10px" /> | ||||||
|  |                 <el-link type="default" :underline="false" :href="item.url"> | ||||||
|  |                   {{ item.name }} | ||||||
|  |                 </el-link> | ||||||
|  |               </div> | ||||||
|  |             </el-col> | ||||||
|  |           </el-row> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |       <el-card shadow="never" class="mt-20px"> | ||||||
|  |         <template #header> | ||||||
|  |           <div class="flex justify-between"> | ||||||
|  |             <span>{{ t('workplace.notice') }}</span> | ||||||
|  |             <el-link type="primary" :underline="false">{{ t('workplace.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="w-35px h-35px rounded-[50%] mr-20px" /> | ||||||
|  |               <div> | ||||||
|  |                 <div class="text-14px"> | ||||||
|  |                   <Highlight :keys="item.keys.map((v) => t(v))"> | ||||||
|  |                     {{ username }} {{ t('workplace.pushCode') }} | ||||||
|  |                   </Highlight> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="mt-15px text-12px text-gray-400"> | ||||||
|  |                   {{ formatTime(item.time, 'yyyy-MM-dd') }} | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             <el-divider /> | ||||||
|  |           </div> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |       <el-card shadow="never" class="mt-20px"> | ||||||
|  |         <template #header> | ||||||
|  |           <span>{{ t('workplace.index') }}</span> | ||||||
|  |         </template> | ||||||
|  |         <el-skeleton :loading="loading" animated> | ||||||
|  |           <Echart :options="radarOptionData" :height="400" /> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |     </el-col> | ||||||
|  |   </el-row> | ||||||
| </template> | </template> | ||||||
| <style lang="less" scoped> |  | ||||||
| @prefix-cls: ~'@{namespace}-panel'; |  | ||||||
|  |  | ||||||
| .@{prefix-cls} { |  | ||||||
|   &__item { |  | ||||||
|     &--peoples { |  | ||||||
|       color: #40c9c6; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &--message { |  | ||||||
|       color: #36a3f7; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &--money { |  | ||||||
|       color: #f4516c; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &--shopping { |  | ||||||
|       color: #34bfa3; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &:hover { |  | ||||||
|       :deep(.@{namespace}-icon) { |  | ||||||
|         color: #fff !important; |  | ||||||
|       } |  | ||||||
|       .@{prefix-cls}__item--icon { |  | ||||||
|         transition: all 0.38s ease-out; |  | ||||||
|       } |  | ||||||
|       .@{prefix-cls}__item--peoples { |  | ||||||
|         background: #40c9c6; |  | ||||||
|       } |  | ||||||
|       .@{prefix-cls}__item--message { |  | ||||||
|         background: #36a3f7; |  | ||||||
|       } |  | ||||||
|       .@{prefix-cls}__item--money { |  | ||||||
|         background: #f4516c; |  | ||||||
|       } |  | ||||||
|       .@{prefix-cls}__item--shopping { |  | ||||||
|         background: #34bfa3; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|   | |||||||
| @@ -1,197 +1,127 @@ | |||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { useTimeAgo } from '@/hooks/web/useTimeAgo' |  | ||||||
| import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus' |  | ||||||
| import { useI18n } from '@/hooks/web/useI18n' |  | ||||||
| import { ref, reactive } from 'vue' | import { ref, reactive } from 'vue' | ||||||
| import { CountTo } from '@/components/CountTo' |  | ||||||
| import { formatTime } from '@/utils' |  | ||||||
| import { Echart } from '@/components/Echart' |  | ||||||
| import { EChartsOption } from 'echarts' |  | ||||||
| import { radarOption } from './echarts-data' |  | ||||||
| import { Highlight } from '@/components/Highlight' |  | ||||||
| import type { WorkplaceTotal, Project, Dynamic, Team } from './types' |  | ||||||
| import { set } from 'lodash-es' | import { set } from 'lodash-es' | ||||||
| import { useCache } from '@/hooks/web/useCache' | import { EChartsOption } from 'echarts' | ||||||
|  | import { Echart } from '@/components/Echart' | ||||||
|  | import { useI18n } from '@/hooks/web/useI18n' | ||||||
|  | import { CountTo } from '@/components/CountTo' | ||||||
|  | import type { AnalysisTotalTypes } from './types' | ||||||
|  | import { useDesign } from '@/hooks/web/useDesign' | ||||||
|  | import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus' | ||||||
|  | import { pieOptions, barOptions, lineOptions } from './echarts-data' | ||||||
|  |  | ||||||
| const { t } = useI18n() | const { t } = useI18n() | ||||||
| const { wsCache } = useCache() |  | ||||||
| const loading = ref(true) | const loading = ref(true) | ||||||
| const avatar = wsCache.get('user').user.avatar | const { getPrefixCls } = useDesign() | ||||||
| const username = wsCache.get('user').user.nickname | const prefixCls = getPrefixCls('panel') | ||||||
| // 获取统计数 | const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption | ||||||
| let totalSate = reactive<WorkplaceTotal>({ |  | ||||||
|   project: 0, | let totalState = reactive<AnalysisTotalTypes>({ | ||||||
|   access: 0, |   users: 0, | ||||||
|   todo: 0 |   messages: 0, | ||||||
|  |   moneys: 0, | ||||||
|  |   shoppings: 0 | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const getCount = async () => { | const getCount = async () => { | ||||||
|   const data = { |   const data = { | ||||||
|     project: 40, |     users: 102400, | ||||||
|     access: 2340, |     messages: 81212, | ||||||
|     todo: 10 |     moneys: 9280, | ||||||
|  |     shoppings: 13600 | ||||||
|   } |   } | ||||||
|   totalSate = Object.assign(totalSate, data) |   totalState = Object.assign(totalState, data) | ||||||
| } | } | ||||||
|  |  | ||||||
| let projects = reactive<Project[]>([]) | // 用户来源 | ||||||
|  | const getUserAccessSource = async () => { | ||||||
| // 获取项目数 |  | ||||||
| const getProject = async () => { |  | ||||||
|   const data = [ |   const data = [ | ||||||
|     { |     { value: 335, name: 'analysis.directAccess' }, | ||||||
|       name: 'Github', |     { value: 310, name: 'analysis.mailMarketing' }, | ||||||
|       icon: 'akar-icons:github-fill', |     { value: 234, name: 'analysis.allianceAdvertising' }, | ||||||
|       message: 'workplace.introduction', |     { value: 135, name: 'analysis.videoAdvertising' }, | ||||||
|       personal: 'Archer', |     { value: 1548, name: 'analysis.searchEngines' } | ||||||
|       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 dynamics = reactive<Dynamic[]>([]) |  | ||||||
|  |  | ||||||
| const getDynamic = async () => { |  | ||||||
|   const data = [ |  | ||||||
|     { |  | ||||||
|       keys: ['workplace.push', 'Github'], |  | ||||||
|       time: new Date() |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       keys: ['workplace.push', 'Github'], |  | ||||||
|       time: new Date() |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       keys: ['workplace.push', 'Github'], |  | ||||||
|       time: new Date() |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       keys: ['workplace.push', 'Github'], |  | ||||||
|       time: new Date() |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       keys: ['workplace.push', 'Github'], |  | ||||||
|       time: new Date() |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       keys: ['workplace.push', 'Github'], |  | ||||||
|       time: new Date() |  | ||||||
|     } |  | ||||||
|   ] |  | ||||||
|   dynamics = Object.assign(dynamics, data) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 获取团队 |  | ||||||
| let team = reactive<Team[]>([]) |  | ||||||
|  |  | ||||||
| const getTeam = async () => { |  | ||||||
|   const data = [ |  | ||||||
|     { |  | ||||||
|       name: 'Github', |  | ||||||
|       icon: 'akar-icons:github-fill' |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       name: 'Vue', |  | ||||||
|       icon: 'logos:vue' |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       name: 'Angular', |  | ||||||
|       icon: 'logos:angular-icon' |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       name: 'React', |  | ||||||
|       icon: 'logos:react' |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       name: 'Webpack', |  | ||||||
|       icon: 'logos:webpack' |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       name: 'Vite', |  | ||||||
|       icon: 'vscode-icons:file-type-vite' |  | ||||||
|     } |  | ||||||
|   ] |  | ||||||
|   team = Object.assign(team, data) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 获取指数 |  | ||||||
| let radarOptionData = reactive<EChartsOption>(radarOption) as EChartsOption |  | ||||||
|  |  | ||||||
| const getRadar = async () => { |  | ||||||
|   const data = [ |  | ||||||
|     { name: 'workplace.quote', max: 65, personal: 42, team: 50 }, |  | ||||||
|     { name: 'workplace.contribution', max: 160, personal: 30, team: 140 }, |  | ||||||
|     { name: 'workplace.hot', max: 300, personal: 20, team: 28 }, |  | ||||||
|     { name: 'workplace.yield', max: 130, personal: 35, team: 35 }, |  | ||||||
|     { name: 'workplace.follow', max: 100, personal: 80, team: 90 } |  | ||||||
|   ] |   ] | ||||||
|   set( |   set( | ||||||
|     radarOptionData, |     pieOptionsData, | ||||||
|     'radar.indicator', |     'legend.data', | ||||||
|     data.map((v) => { |     data.map((v) => t(v.name)) | ||||||
|       return { |  | ||||||
|         name: t(v.name), |  | ||||||
|         max: v.max |  | ||||||
|       } |  | ||||||
|     }) |  | ||||||
|   ) |   ) | ||||||
|   set(radarOptionData, 'series', [ |   set(pieOptionsData, 'series.data', data) | ||||||
|  | } | ||||||
|  | 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: '指数', |       name: t('analysis.activeQuantity'), | ||||||
|       type: 'radar', |       data: data.map((v) => v.value), | ||||||
|       data: [ |       type: 'bar' | ||||||
|  |     } | ||||||
|  |   ]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const lineOptionsData = reactive<EChartsOption>(lineOptions) as EChartsOption | ||||||
|  |  | ||||||
|  | // 每月销售总额 | ||||||
|  | const getMonthlySales = async () => { | ||||||
|  |   const data = [ | ||||||
|  |     { estimate: 100, actual: 120, name: 'analysis.january' }, | ||||||
|  |     { estimate: 120, actual: 82, name: 'analysis.february' }, | ||||||
|  |     { estimate: 161, actual: 91, name: 'analysis.march' }, | ||||||
|  |     { estimate: 134, actual: 154, name: 'analysis.april' }, | ||||||
|  |     { estimate: 105, actual: 162, name: 'analysis.may' }, | ||||||
|  |     { estimate: 160, actual: 140, name: 'analysis.june' }, | ||||||
|  |     { estimate: 165, actual: 145, name: 'analysis.july' }, | ||||||
|  |     { estimate: 114, actual: 250, name: 'analysis.august' }, | ||||||
|  |     { estimate: 163, actual: 134, name: 'analysis.september' }, | ||||||
|  |     { estimate: 185, actual: 56, name: 'analysis.october' }, | ||||||
|  |     { estimate: 118, actual: 99, name: 'analysis.november' }, | ||||||
|  |     { estimate: 123, actual: 123, name: 'analysis.december' } | ||||||
|  |   ] | ||||||
|  |   set( | ||||||
|  |     lineOptionsData, | ||||||
|  |     'xAxis.data', | ||||||
|  |     data.map((v) => t(v.name)) | ||||||
|  |   ) | ||||||
|  |   set(lineOptionsData, 'series', [ | ||||||
|     { |     { | ||||||
|           value: data.map((v) => v.personal), |       name: t('analysis.estimate'), | ||||||
|           name: t('workplace.personal') |       smooth: true, | ||||||
|  |       type: 'line', | ||||||
|  |       data: data.map((v) => v.estimate), | ||||||
|  |       animationDuration: 2800, | ||||||
|  |       animationEasing: 'cubicInOut' | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|           value: data.map((v) => v.team), |       name: t('analysis.actual'), | ||||||
|           name: t('workplace.team') |       smooth: true, | ||||||
|         } |       type: 'line', | ||||||
|       ] |       itemStyle: {}, | ||||||
|  |       data: data.map((v) => v.actual), | ||||||
|  |       animationDuration: 2800, | ||||||
|  |       animationEasing: 'quadraticOut' | ||||||
|     } |     } | ||||||
|   ]) |   ]) | ||||||
| } | } | ||||||
|  |  | ||||||
| const getAllApi = async () => { | const getAllApi = async () => { | ||||||
|   await Promise.all([getCount(), getProject(), getDynamic(), getTeam(), getRadar()]) |   await Promise.all([getCount(), getUserAccessSource(), getWeeklyUserActivity(), getMonthlySales()]) | ||||||
|   loading.value = false |   loading.value = false | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -199,173 +129,188 @@ getAllApi() | |||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <template> | <template> | ||||||
|  |   <el-row :gutter="20" justify="space-between" :class="prefixCls"> | ||||||
|  |     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> | ||||||
|  |       <el-card shadow="hover" class="mb-20px"> | ||||||
|  |         <el-skeleton :loading="loading" animated :rows="2"> | ||||||
|  |           <template #default> | ||||||
|  |             <div :class="`${prefixCls}__item flex justify-between`"> | ||||||
|               <div> |               <div> | ||||||
|     <el-card shadow="never"> |                 <div | ||||||
|       <el-skeleton :loading="loading" animated> |                   :class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`" | ||||||
|  |                 > | ||||||
|  |                   <Icon icon="svg-icon:peoples" :size="40" /> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <div class="flex flex-col justify-between"> | ||||||
|  |                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ | ||||||
|  |                   t('analysis.newUser') | ||||||
|  |                 }}</div> | ||||||
|  |                 <CountTo | ||||||
|  |                   class="text-20px font-700 text-right" | ||||||
|  |                   :start-val="0" | ||||||
|  |                   :end-val="102400" | ||||||
|  |                   :duration="2600" | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </template> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |     </el-col> | ||||||
|  |  | ||||||
|  |     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> | ||||||
|  |       <el-card shadow="hover" class="mb-20px"> | ||||||
|  |         <el-skeleton :loading="loading" animated :rows="2"> | ||||||
|  |           <template #default> | ||||||
|  |             <div :class="`${prefixCls}__item flex justify-between`"> | ||||||
|  |               <div> | ||||||
|  |                 <div | ||||||
|  |                   :class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`" | ||||||
|  |                 > | ||||||
|  |                   <Icon icon="svg-icon:message" :size="40" /> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <div class="flex flex-col justify-between"> | ||||||
|  |                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ | ||||||
|  |                   t('analysis.unreadInformation') | ||||||
|  |                 }}</div> | ||||||
|  |                 <CountTo | ||||||
|  |                   class="text-20px font-700 text-right" | ||||||
|  |                   :start-val="0" | ||||||
|  |                   :end-val="81212" | ||||||
|  |                   :duration="2600" | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </template> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |     </el-col> | ||||||
|  |  | ||||||
|  |     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> | ||||||
|  |       <el-card shadow="hover" class="mb-20px"> | ||||||
|  |         <el-skeleton :loading="loading" animated :rows="2"> | ||||||
|  |           <template #default> | ||||||
|  |             <div :class="`${prefixCls}__item flex justify-between`"> | ||||||
|  |               <div> | ||||||
|  |                 <div | ||||||
|  |                   :class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`" | ||||||
|  |                 > | ||||||
|  |                   <Icon icon="svg-icon:money" :size="40" /> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <div class="flex flex-col justify-between"> | ||||||
|  |                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ | ||||||
|  |                   t('analysis.transactionAmount') | ||||||
|  |                 }}</div> | ||||||
|  |                 <CountTo | ||||||
|  |                   class="text-20px font-700 text-right" | ||||||
|  |                   :start-val="0" | ||||||
|  |                   :end-val="9280" | ||||||
|  |                   :duration="2600" | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </template> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |     </el-col> | ||||||
|  |  | ||||||
|  |     <el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> | ||||||
|  |       <el-card shadow="hover" class="mb-20px"> | ||||||
|  |         <el-skeleton :loading="loading" animated :rows="2"> | ||||||
|  |           <template #default> | ||||||
|  |             <div :class="`${prefixCls}__item flex justify-between`"> | ||||||
|  |               <div> | ||||||
|  |                 <div | ||||||
|  |                   :class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`" | ||||||
|  |                 > | ||||||
|  |                   <Icon icon="svg-icon:shopping" :size="40" /> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <div class="flex flex-col justify-between"> | ||||||
|  |                 <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ | ||||||
|  |                   t('analysis.totalShopping') | ||||||
|  |                 }}</div> | ||||||
|  |                 <CountTo | ||||||
|  |                   class="text-20px font-700 text-right" | ||||||
|  |                   :start-val="0" | ||||||
|  |                   :end-val="13600" | ||||||
|  |                   :duration="2600" | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </template> | ||||||
|  |         </el-skeleton> | ||||||
|  |       </el-card> | ||||||
|  |     </el-col> | ||||||
|  |   </el-row> | ||||||
|   <el-row :gutter="20" justify="space-between"> |   <el-row :gutter="20" justify="space-between"> | ||||||
|           <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> |     <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> | ||||||
|             <div class="flex items-center"> |       <el-card shadow="hover" class="mb-20px"> | ||||||
|               <img :src="avatar" alt="" class="w-70px h-70px rounded-[50%] mr-20px" /> |  | ||||||
|               <div> |  | ||||||
|                 <div class="text-20px text-700"> |  | ||||||
|                   {{ t('workplace.goodMorning') }} {{ 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="flex h-70px items-center justify-end <sm:mt-20px"> |  | ||||||
|               <div class="px-8px text-right"> |  | ||||||
|                 <div class="text-14px text-gray-400 mb-20px">{{ 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="text-14px text-gray-400 mb-20px">{{ 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="text-14px text-gray-400 mb-20px">{{ 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-20px" :gutter="20" justify="space-between"> |  | ||||||
|     <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-20px"> |  | ||||||
|       <el-card shadow="never"> |  | ||||||
|         <template #header> |  | ||||||
|           <div class="flex justify-between"> |  | ||||||
|             <span>{{ t('workplace.project') }}</span> |  | ||||||
|             <ElLink type="primary" :underline="false">{{ t('workplace.more') }}</ElLink> |  | ||||||
|           </div> |  | ||||||
|         </template> |  | ||||||
|         <el-skeleton :loading="loading" animated> |         <el-skeleton :loading="loading" animated> | ||||||
|           <el-row> |           <Echart :options="pieOptionsData" :height="300" /> | ||||||
|             <el-col |  | ||||||
|               v-for="(item, index) in projects" |  | ||||||
|               :key="`card-${index}`" |  | ||||||
|               :xl="8" |  | ||||||
|               :lg="8" |  | ||||||
|               :md="12" |  | ||||||
|               :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 text-12px text-gray-400 flex justify-between"> |  | ||||||
|                   <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-20px"> |  | ||||||
|         <template #header> |  | ||||||
|           <div class="flex justify-between"> |  | ||||||
|             <span>{{ t('workplace.dynamic') }}</span> |  | ||||||
|             <ElLink type="primary" :underline="false">{{ t('workplace.more') }}</ElLink> |  | ||||||
|           </div> |  | ||||||
|         </template> |  | ||||||
|         <el-skeleton :loading="loading" animated> |  | ||||||
|           <div v-for="(item, index) in dynamics" :key="`dynamics-${index}`"> |  | ||||||
|             <div class="flex items-center"> |  | ||||||
|               <img :src="avatar" alt="" class="w-35px h-35px rounded-[50%] mr-20px" /> |  | ||||||
|               <div> |  | ||||||
|                 <div class="text-14px"> |  | ||||||
|                   <Highlight :keys="item.keys.map((v) => t(v))"> |  | ||||||
|                     {{ username }} {{ t('workplace.pushCode') }} |  | ||||||
|                   </Highlight> |  | ||||||
|                 </div> |  | ||||||
|                 <div class="mt-15px text-12px text-gray-400"> |  | ||||||
|                   {{ useTimeAgo(item.time) }} |  | ||||||
|                 </div> |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|             <el-divider /> |  | ||||||
|           </div> |  | ||||||
|         </el-skeleton> |         </el-skeleton> | ||||||
|       </el-card> |       </el-card> | ||||||
|     </el-col> |     </el-col> | ||||||
|     <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-20px"> |     <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24"> | ||||||
|       <el-card shadow="never"> |       <el-card shadow="hover" class="mb-20px"> | ||||||
|         <template #header> |  | ||||||
|           <span>{{ t('workplace.shortcutOperation') }}</span> |  | ||||||
|         </template> |  | ||||||
|         <el-skeleton :loading="loading" animated> |         <el-skeleton :loading="loading" animated> | ||||||
|           <el-col |           <Echart :options="barOptionsData" :height="300" /> | ||||||
|             v-for="item in 9" |  | ||||||
|             :key="`card-${item}`" |  | ||||||
|             :xl="12" |  | ||||||
|             :lg="12" |  | ||||||
|             :md="12" |  | ||||||
|             :sm="24" |  | ||||||
|             :xs="24" |  | ||||||
|             class="mb-10px" |  | ||||||
|           > |  | ||||||
|             <ElLink type="default" :underline="false"> |  | ||||||
|               {{ t('workplace.operation') }}{{ item }} |  | ||||||
|             </ElLink> |  | ||||||
|           </el-col> |  | ||||||
|         </el-skeleton> |         </el-skeleton> | ||||||
|       </el-card> |       </el-card> | ||||||
|  |  | ||||||
|       <el-card shadow="never" class="mt-20px"> |  | ||||||
|         <template #header> |  | ||||||
|           <span>{{ t('workplace.index') }}</span> |  | ||||||
|         </template> |  | ||||||
|         <el-skeleton :loading="loading" animated> |  | ||||||
|           <Echart :options="radarOptionData" :height="400" /> |  | ||||||
|         </el-skeleton> |  | ||||||
|       </el-card> |  | ||||||
|  |  | ||||||
|       <el-card shadow="never" class="mt-20px"> |  | ||||||
|         <template #header> |  | ||||||
|           <span>{{ t('workplace.team') }}</span> |  | ||||||
|         </template> |  | ||||||
|         <el-skeleton :loading="loading" animated> |  | ||||||
|           <el-row> |  | ||||||
|             <el-col v-for="item in team" :key="`team-${item.name}`" :span="12" class="mb-20px"> |  | ||||||
|               <div class="flex items-center"> |  | ||||||
|                 <Icon :icon="item.icon" class="mr-10px" /> |  | ||||||
|                 <ElLink type="default" :underline="false"> |  | ||||||
|                   {{ item.name }} |  | ||||||
|                 </ElLink> |  | ||||||
|               </div> |  | ||||||
|     </el-col> |     </el-col> | ||||||
|           </el-row> |     <el-col :span="24"> | ||||||
|  |       <el-card shadow="hover" class="mb-20px"> | ||||||
|  |         <el-skeleton :loading="loading" animated :rows="4"> | ||||||
|  |           <Echart :options="lineOptionsData" :height="350" /> | ||||||
|         </el-skeleton> |         </el-skeleton> | ||||||
|       </el-card> |       </el-card> | ||||||
|     </el-col> |     </el-col> | ||||||
|   </el-row> |   </el-row> | ||||||
| </template> | </template> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | @prefix-cls: ~'@{namespace}-panel'; | ||||||
|  |  | ||||||
|  | .@{prefix-cls} { | ||||||
|  |   &__item { | ||||||
|  |     &--peoples { | ||||||
|  |       color: #40c9c6; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     &--message { | ||||||
|  |       color: #36a3f7; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     &--money { | ||||||
|  |       color: #f4516c; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     &--shopping { | ||||||
|  |       color: #34bfa3; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     &:hover { | ||||||
|  |       :deep(.@{namespace}-icon) { | ||||||
|  |         color: #fff !important; | ||||||
|  |       } | ||||||
|  |       .@{prefix-cls}__item--icon { | ||||||
|  |         transition: all 0.38s ease-out; | ||||||
|  |       } | ||||||
|  |       .@{prefix-cls}__item--peoples { | ||||||
|  |         background: #40c9c6; | ||||||
|  |       } | ||||||
|  |       .@{prefix-cls}__item--message { | ||||||
|  |         background: #36a3f7; | ||||||
|  |       } | ||||||
|  |       .@{prefix-cls}__item--money { | ||||||
|  |         background: #f4516c; | ||||||
|  |       } | ||||||
|  |       .@{prefix-cls}__item--shopping { | ||||||
|  |         background: #34bfa3; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|   | |||||||
| @@ -12,14 +12,15 @@ export type Project = { | |||||||
|   time: Date | number | string |   time: Date | number | string | ||||||
| } | } | ||||||
|  |  | ||||||
| export type Dynamic = { | export type Notice = { | ||||||
|   keys: string[] |   keys: string[] | ||||||
|   time: Date | number | string |   time: Date | number | string | ||||||
| } | } | ||||||
|  |  | ||||||
| export type Team = { | export type Shortcut = { | ||||||
|   name: string |   name: string | ||||||
|   icon: string |   icon: string | ||||||
|  |   url: string | ||||||
| } | } | ||||||
|  |  | ||||||
| export type RadarData = { | export type RadarData = { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 xingyu
					xingyu