Browse Source

refactor: vue3 index

xingyu 2 years ago
parent
commit
bd5e0bf938

+ 1 - 1
yudao-ui-admin-vue3/package.json

@@ -6,7 +6,7 @@
   "private": false,
   "scripts": {
     "i": "pnpm install",
-    "dev": "vite --mode base",
+    "dev": "vite --mode base --open",
     "ts:check": "vue-tsc --noEmit",
     "build:pro": "vite build --mode pro",
     "build:dev": "vite build --mode dev",

+ 3 - 2
yudao-ui-admin-vue3/src/locales/en.ts

@@ -171,15 +171,16 @@ export default {
     sunday: 'Sunday'
   },
   workplace: {
-    goodMorning: 'Good morning',
+    welcome: 'Hello',
     happyDay: 'Wish you happy every day!',
     toady: `It's sunny today`,
+    notice: 'Announcement',
     project: 'Project',
     access: 'Project access',
     toDo: 'To do',
     introduction: 'A serious introduction',
     more: 'More',
-    shortcutOperation: 'Shortcut operation',
+    shortcutOperation: 'Quick entry',
     operation: 'Operation',
     index: 'Index',
     personal: 'Personal',

+ 3 - 2
yudao-ui-admin-vue3/src/locales/zh-CN.ts

@@ -171,15 +171,16 @@ export default {
     sunday: '周日'
   },
   workplace: {
-    goodMorning: '早安',
+    welcome: '你好',
     happyDay: '祝你开心每一天!',
     toady: '今日晴',
+    notice: '通知公告',
     project: '项目数',
     access: '项目访问',
     toDo: '待办',
     introduction: '一个正经的简介',
     more: '更多',
-    shortcutOperation: '快捷操作',
+    shortcutOperation: '快捷入口',
     operation: '操作',
     index: '指数',
     personal: '个人',

+ 317 - 170
yudao-ui-admin-vue3/src/views/Home/Index.vue

@@ -1,38 +1,187 @@
 <script setup lang="ts">
-import { ref, reactive } from 'vue'
-import { set } from 'lodash-es'
-import { EChartsOption } from 'echarts'
-import { Echart } from '@/components/Echart'
+import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus'
 import { useI18n } from '@/hooks/web/useI18n'
+import { ref, reactive } from 'vue'
 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 { 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, Notice, Shortcut } from './types'
+import { set } from 'lodash-es'
+import { useCache } from '@/hooks/web/useCache'
 import { pieOptions, barOptions, lineOptions } from './echarts-data'
 
 const { t } = useI18n()
+const { wsCache } = useCache()
 const loading = ref(true)
-const { getPrefixCls } = useDesign()
-const prefixCls = getPrefixCls('panel')
+const avatar = wsCache.get('user').user.avatar
+const username = wsCache.get('user').user.nickname
 const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
-
-let totalState = reactive<AnalysisTotalTypes>({
-  users: 0,
-  messages: 0,
-  moneys: 0,
-  shoppings: 0
+// 获取统计数
+let totalSate = reactive<WorkplaceTotal>({
+  project: 0,
+  access: 0,
+  todo: 0
 })
 
 const getCount = async () => {
   const data = {
-    users: 102400,
-    messages: 81212,
-    moneys: 9280,
-    shoppings: 13600
+    project: 40,
+    access: 2340,
+    todo: 10
   }
-  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 data = [
@@ -121,7 +270,16 @@ const getMonthlySales = 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
 }
 
@@ -129,188 +287,177 @@ getAllApi()
 </script>
 
 <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>
+    <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="w-70px h-70px rounded-[50%] mr-20px" />
               <div>
-                <div
-                  :class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`"
-                >
-                  <Icon icon="svg-icon:peoples" :size="40" />
+                <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 class="flex flex-col justify-between">
-                <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
-                  t('analysis.newUser')
-                }}</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 font-700 text-right"
+                  class="text-20px"
                   :start-val="0"
-                  :end-val="102400"
+                  :end-val="totalSate.project"
                   :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>
+              <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 font-700 text-right"
+                  class="text-20px"
                   :start-val="0"
-                  :end-val="81212"
+                  :end-val="totalSate.todo"
                   :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>
+              <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 font-700 text-right"
+                  class="text-20px"
                   :start-val="0"
-                  :end-val="9280"
+                  :end-val="totalSate.access"
                   :duration="2600"
                 />
               </div>
             </div>
-          </template>
+          </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>
+            <el-link type="primary" :underline="false">{{ t('workplace.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="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-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-card shadow="never" class="mt-20px">
+        <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-20px">
+                <el-skeleton :loading="loading" animated>
+                  <Echart :options="pieOptionsData" :height="300" />
+                </el-skeleton>
+              </el-card>
+            </el-col>
+            <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
+              <el-card shadow="hover" class="mb-20px">
+                <el-skeleton :loading="loading" animated>
+                  <Echart :options="barOptionsData" :height="300" />
+                </el-skeleton>
+              </el-card>
+            </el-col>
+            <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-card>
+            </el-col>
+          </el-row>
         </el-skeleton>
       </el-card>
     </el-col>
-  </el-row>
-  <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-20px">
+    <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>
-          <Echart :options="pieOptionsData" :height="300" />
+          <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-col>
-    <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
-      <el-card shadow="hover" class="mb-20px">
+      <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>
-          <Echart :options="barOptionsData" :height="300" />
+          <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-col>
-    <el-col :span="24">
-      <el-card shadow="hover" class="mb-20px">
-        <el-skeleton :loading="loading" animated :rows="4">
-          <Echart :options="lineOptionsData" :height="350" />
+      <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>
-<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>

+ 235 - 290
yudao-ui-admin-vue3/src/views/Home/Index2.vue

@@ -1,197 +1,127 @@
 <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 { 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 { 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 { wsCache } = useCache()
 const loading = ref(true)
-const avatar = wsCache.get('user').user.avatar
-const username = wsCache.get('user').user.nickname
-// 获取统计数
-let totalSate = reactive<WorkplaceTotal>({
-  project: 0,
-  access: 0,
-  todo: 0
+const { getPrefixCls } = useDesign()
+const prefixCls = getPrefixCls('panel')
+const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
+
+let totalState = reactive<AnalysisTotalTypes>({
+  users: 0,
+  messages: 0,
+  moneys: 0,
+  shoppings: 0
 })
 
 const getCount = async () => {
   const data = {
-    project: 40,
-    access: 2340,
-    todo: 10
+    users: 102400,
+    messages: 81212,
+    moneys: 9280,
+    shoppings: 13600
   }
-  totalSate = Object.assign(totalSate, data)
+  totalState = Object.assign(totalState, data)
 }
 
-let projects = reactive<Project[]>([])
-
-// 获取项目数
-const getProject = async () => {
+// 用户来源
+const getUserAccessSource = 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()
-    }
+    { 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' }
   ]
-  projects = Object.assign(projects, data)
+  set(
+    pieOptionsData,
+    'legend.data',
+    data.map((v) => t(v.name))
+  )
+  set(pieOptionsData, 'series.data', data)
 }
+const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
 
-// 获取动态
-let dynamics = reactive<Dynamic[]>([])
-
-const getDynamic = async () => {
+// 周活跃量
+const getWeeklyUserActivity = 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()
-    }
+    { 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' }
   ]
-  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'
-    },
+  set(
+    barOptionsData,
+    'xAxis.data',
+    data.map((v) => t(v.name))
+  )
+  set(barOptionsData, 'series', [
     {
-      name: 'Vite',
-      icon: 'vscode-icons:file-type-vite'
+      name: t('analysis.activeQuantity'),
+      data: data.map((v) => v.value),
+      type: 'bar'
     }
-  ]
-  team = Object.assign(team, data)
+  ])
 }
 
-// 获取指数
-let radarOptionData = reactive<EChartsOption>(radarOption) as EChartsOption
+const lineOptionsData = reactive<EChartsOption>(lineOptions) as EChartsOption
 
-const getRadar = async () => {
+// 每月销售总额
+const getMonthlySales = 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 }
+    { 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(
-    radarOptionData,
-    'radar.indicator',
-    data.map((v) => {
-      return {
-        name: t(v.name),
-        max: v.max
-      }
-    })
+    lineOptionsData,
+    'xAxis.data',
+    data.map((v) => t(v.name))
   )
-  set(radarOptionData, 'series', [
+  set(lineOptionsData, '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')
-        }
-      ]
+      name: t('analysis.estimate'),
+      smooth: true,
+      type: 'line',
+      data: data.map((v) => v.estimate),
+      animationDuration: 2800,
+      animationEasing: 'cubicInOut'
+    },
+    {
+      name: t('analysis.actual'),
+      smooth: true,
+      type: 'line',
+      itemStyle: {},
+      data: data.map((v) => v.actual),
+      animationDuration: 2800,
+      animationEasing: 'quadraticOut'
     }
   ])
 }
 
 const getAllApi = async () => {
-  await Promise.all([getCount(), getProject(), getDynamic(), getTeam(), getRadar()])
+  await Promise.all([getCount(), getUserAccessSource(), getWeeklyUserActivity(), getMonthlySales()])
   loading.value = false
 }
 
@@ -199,173 +129,188 @@ getAllApi()
 </script>
 
 <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="w-70px h-70px rounded-[50%] mr-20px" />
+  <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 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
+                  :class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`"
+                >
+                  <Icon icon="svg-icon:peoples" :size="40" />
                 </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>
+              <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"
+                  class="text-20px font-700 text-right"
                   :start-val="0"
-                  :end-val="totalSate.project"
+                  :end-val="102400"
                   :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>
+            </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"
+                  class="text-20px font-700 text-right"
                   :start-val="0"
-                  :end-val="totalSate.todo"
+                  :end-val="81212"
                   :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>
+            </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"
+                  class="text-20px font-700 text-right"
                   :start-val="0"
-                  :end-val="totalSate.access"
+                  :end-val="9280"
                   :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-row>
-            <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>
+          </template>
         </el-skeleton>
       </el-card>
+    </el-col>
 
-      <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" />
+    <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="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
+                  :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>
-            <el-divider />
-          </div>
+          </template>
         </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-row>
+  <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-20px">
         <el-skeleton :loading="loading" animated>
-          <el-col
-            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>
+          <Echart :options="pieOptionsData" :height="300" />
         </el-skeleton>
       </el-card>
-
-      <el-card shadow="never" class="mt-20px">
-        <template #header>
-          <span>{{ t('workplace.index') }}</span>
-        </template>
+    </el-col>
+    <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
+      <el-card shadow="hover" class="mb-20px">
         <el-skeleton :loading="loading" animated>
-          <Echart :options="radarOptionData" :height="400" />
+          <Echart :options="barOptionsData" :height="300" />
         </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-row>
+    </el-col>
+    <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-card>
     </el-col>
   </el-row>
 </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>

+ 3 - 2
yudao-ui-admin-vue3/src/views/Home/types.ts

@@ -12,14 +12,15 @@ export type Project = {
   time: Date | number | string
 }
 
-export type Dynamic = {
+export type Notice = {
   keys: string[]
   time: Date | number | string
 }
 
-export type Team = {
+export type Shortcut = {
   name: string
   icon: string
+  url: string
 }
 
 export type RadarData = {