47 1 mese fa
parent
commit
d888d646f4

+ 11 - 1
src/api/system/studentAttendance/index.ts

@@ -87,9 +87,19 @@ export const StudentAttendanceApi = {
     return await request.get({ url: `/system/student-attendance/weekendAttendance` })
   },
 
-  // 日出勤统计
+  // 日出勤统计数量
   getDayAttendance: async () => {
     return await request.get({ url: `/system/student-attendance/dayAttendanceCount` })
   },
 
+  // 日出勤统计列表
+  getDayStudentAttendance: async () => {
+    return await request.get({ url: `/system/student-attendance/dayAttendance` })
+  },
+
+  // 日缺勤统计列表
+  getDayStudentErrorAttendance: async () => {
+    return await request.get({ url: `/system/student-attendance/dayErrorAttendance` })
+  },
+
 }

BIN
src/assets/imgs/GLUT.png


+ 6 - 9
src/views/Home/Index.vue

@@ -182,7 +182,7 @@
                       </el-col>
                       <el-col :span="6" class="center"
                         style="display: flex; justify-content: center; align-items: center; padding: 6px;">
-                        <div>{{ item.clockInTime }}</div>
+                        <div>{{ formatDate(item.clockInTime) }}</div>
                       </el-col>
                     </el-row>
                   </div>
@@ -299,6 +299,7 @@
 <script lang="ts" setup>
 import { EChartsOption, List } from 'echarts'
 import { formatTime } from '@/utils'
+import {formatDate} from "@/utils/formatTime";
 import { useUserStore } from '@/store/modules/user'
 import { useWatermark } from '@/hooks/web/useWatermark'
 import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
@@ -474,16 +475,12 @@ const getWeekend = async () => {
     barOptionsData.series[2].data = excuseData;
 };
 
-
-const params = reactive({
-  list: [] 
-})
 /**缺勤预警 */
 let list2 = reactive([]);
 const getStudentAttendanceError = async () => {
-  const data = await StudentAttendanceApi.getStudentAttendanceErrorPage(params)
+  const data = await StudentAttendanceApi.getDayStudentErrorAttendance()
   console.log("缺勤列表", data);
-  list2.splice(0, list2.length, ...data.list);
+  list2.splice(0, list2.length, ...data);
 }
 const class2Options = reactive({
   step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
@@ -495,9 +492,9 @@ const class2Options = reactive({
 //打卡滚动列表
 const list = reactive([]);
 const getStudentAttendance = async () => {
-  const data = await StudentAttendanceApi.getStudentAttendancePage(params)
+  const data = await StudentAttendanceApi.getDayStudentAttendance()
   console.log("打卡列表", data);
-  list.splice(0, list.length, ...data.list);
+  list.splice(0, list.length, ...data);
 }
 const classOptions = reactive({
   step: 0.5,//滚动速度值越大越快,但是值太小会卡顿

+ 97 - 12
src/views/Login/Login.vue

@@ -1,3 +1,4 @@
+<!--
 <template>
   <div
     :class="prefixCls"
@@ -7,12 +8,12 @@
       <div
         :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
       >
-        <!-- 左上角的 logo + 系统标题 -->
+        左上角的 logo + 系统标题
         <div class="relative flex items-center text-white">
           <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
           <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
         </div>
-        <!-- 左边的背景图 + 欢迎语 -->
+        左边的背景图 + 欢迎语
         <div class="h-[calc(100%-60px)] flex items-center justify-center">
           <TransitionGroup
             appear
@@ -30,7 +31,7 @@
       <div
         class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
       >
-        <!-- 右上角的主题、语言选择 -->
+        右上角的主题、语言选择
         <div
           class="flex items-center justify-between at-2xl:justify-end at-xl:justify-end"
           style="color: var(--el-text-color-primary);"
@@ -44,20 +45,20 @@
             <LocaleDropdown />
           </div>
         </div>
-        <!-- 右边的登录界面 -->
+        右边的登录界面
         <Transition appear enter-active-class="animate__animated animate__bounceInRight">
           <div
             class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
           >
-            <!-- 账号登录 -->
+            账号登录
             <LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 手机登录 -->
+            手机登录
             <MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 二维码登录 -->
+            二维码登录
             <QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 注册 -->
+            注册
             <RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 三方登录 -->
+            三方登录
             <SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
           </div>
         </Transition>
@@ -65,7 +66,9 @@
     </div>
   </div>
 </template>
-<script lang="ts" setup>
+-->
+
+<!-- <script lang="ts" setup>
 import { underlineToHump } from '@/utils'
 
 import { useDesign } from '@/hooks/web/useDesign'
@@ -81,9 +84,9 @@ const { t } = useI18n()
 const appStore = useAppStore()
 const { getPrefixCls } = useDesign()
 const prefixCls = getPrefixCls('login')
-</script>
+</script> -->
 
-<style lang="scss" scoped>
+<!-- <style lang="scss" scoped>
 $prefix-cls: #{$namespace}-login;
 
 .#{$prefix-cls} {
@@ -104,6 +107,88 @@ $prefix-cls: #{$namespace}-login;
     }
   }
 }
+</style> -->
+
+<!-- <style lang="scss">
+.dark .login-form {
+  .el-divider__text {
+    background-color: var(--login-bg-color);
+  }
+
+  .el-card {
+    background-color: var(--login-bg-color);
+  }
+}
+</style> -->
+
+<template>
+  <div
+    :class="prefixCls"
+    class="relative h-full flex flex-col items-center justify-center"
+    style="background-image: url('http://172.16.59.50:9000/graduate/c252713a841785081220d558b8967220116fa4798d15b2600c402d9742963b2a.jpg'); background-size: cover;"
+  >
+    <!-- 上部的 logo + 系统标题 -->
+    <div class="absolute top-5 left-5 flex items-center text-white">
+      <img alt="" class="mr-2 h-48px w-48px" src="@/assets/imgs/logo.png" />
+      <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
+    </div>
+    
+    <div class="m-auto h-auto  max-w-3xl p-15 bg-#32393f bg-opacity-70 backdrop-blur-md rounded-lg shadow-lg">
+      
+      <!-- 登录表单区域 -->
+      <div class="flex justify-center ">
+        <Transition appear enter-active-class="animate__animated animate__bounceIn">
+          <div class="m-auto h-auto w-full max-w-2xl p-20px">
+            <LoginForm class="m-auto h-auto" />
+            <MobileForm class="m-auto h-auto" />
+            <QrCodeForm class="m-auto h-auto" />
+            <RegisterForm class="m-auto h-auto" />
+            <!-- 三方登录 -->
+            <SSOLoginVue class="m-auto h-auto" />
+          </div>
+        </Transition>
+      </div>
+    </div>
+     
+    <!-- 主题、语言选择区域 -->
+    <div class="flex items-center justify-end w-full absolute top-5 right-5">
+      <ThemeSwitch />
+      <LocaleDropdown />
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { underlineToHump } from '@/utils'
+import { useDesign } from '@/hooks/web/useDesign'
+import { useAppStore } from '@/store/modules/app'
+import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
+import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
+import { LoginForm, MobileForm, QrCodeForm, RegisterForm } from './components'
+
+defineOptions({ name: 'Login' })
+
+const { t } = useI18n()
+const appStore = useAppStore()
+const { getPrefixCls } = useDesign()
+const prefixCls = getPrefixCls('login')
+</script>
+
+<style lang="scss" scoped>
+$prefix-cls: #{$namespace}-login;
+
+.#{$prefix-cls} {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100vh; /* 使整体容器充满视口高度 */
+  
+  img {
+    max-width: 100%;
+    height: auto;
+  }
+}
 </style>
 
 <style lang="scss">

+ 8 - 8
src/views/Login/components/LoginForm.vue

@@ -82,7 +82,7 @@
         mode="pop"
         @success="handleLogin"
       />
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
+      <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
         <el-form-item>
           <el-row :gutter="5" justify="space-between" style="width: 100%">
             <el-col :span="8">
@@ -108,9 +108,9 @@
             </el-col>
           </el-row>
         </el-form-item>
-      </el-col>
-      <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
+      </el-col> -->
+      <!-- <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider> -->
+      <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
         <el-form-item>
           <div class="w-[100%] flex justify-between">
             <Icon
@@ -124,9 +124,9 @@
             />
           </div>
         </el-form-item>
-      </el-col>
-      <el-divider content-position="center">萌新必读</el-divider>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
+      </el-col> -->
+      <!-- <el-divider content-position="center">萌新必读</el-divider> -->
+      <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
         <el-form-item>
           <div class="w-[100%] flex justify-between">
             <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
@@ -139,7 +139,7 @@
             </el-link>
           </div>
         </el-form-item>
-      </el-col>
+      </el-col> -->
     </el-row>
   </el-form>
 </template>

+ 19 - 5
src/views/Login/components/LoginFormTitle.vue

@@ -1,7 +1,12 @@
 <template>
-  <h2 class="enter-x mb-3 text-center text-2xl font-bold xl:text-center xl:text-3xl">
-    {{ getFormTitle }}
-  </h2>
+  <div class="text-center">
+    <h2 class="enter-x mb-15 text-2xl font-bold xl:text-3xl">
+      {{ getFormTitle.defaultTitle }}
+    </h2>
+    <h3 class="enter-x mb-3 text-2xl font-bold xl:text-3xl">
+      {{ getFormTitle.currentTitle }}
+    </h3>
+  </div>
 </template>
 <script lang="ts" setup>
 import { LoginStateEnum, useLoginState } from './useLogin'
@@ -13,14 +18,23 @@ const { t } = useI18n()
 const { getLoginState } = useLoginState()
 
 const getFormTitle = computed(() => {
+  const defaultTitle = '欢迎使用测绘学院研究生管理系统';
   const titleObj = {
     [LoginStateEnum.RESET_PASSWORD]: t('sys.login.forgetFormTitle'),
     [LoginStateEnum.LOGIN]: t('sys.login.signInFormTitle'),
     [LoginStateEnum.REGISTER]: t('sys.login.signUpFormTitle'),
     [LoginStateEnum.MOBILE]: t('sys.login.mobileSignInFormTitle'),
     [LoginStateEnum.QR_CODE]: t('sys.login.qrSignInFormTitle'),
-    [LoginStateEnum.SSO]: t('sys.login.ssoFormTitle')
+    [LoginStateEnum.SSO]: t('sys.login.ssoFormTitle'),
+    DEFAULT: defaultTitle,
   }
-  return titleObj[unref(getLoginState)]
+  const currentTitle = titleObj[unref(getLoginState)] || defaultTitle;
+  return {
+    currentTitle,
+    defaultTitle,
+  };
 })
 </script>
+
+<style scoped>
+</style>

+ 609 - 0
src/views/system/Home/Index.vue

@@ -0,0 +1,609 @@
+<template>
+  <el-skeleton :loading="loading" animated>
+    <el-row :gutter="8">
+      <!-- 第一部分 -->
+      <el-col :xs="24" :sm="24" :md="24" :lg="14" :xl="10">
+        <el-card>
+          <el-card shadow="never" class="h-100%  ">
+
+            <template #header>
+              <div class="h-7 flex justify-between fw-800 text-20px">
+                <span>基本信息</span>
+              </div>
+            </template>
+
+            <el-row style="flex-wrap: wrap; ">
+              <el-col v-for="(item, index) in projects" :key="`card-${index}`" :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
+                <el-card shadow="hover" class="mr-5px mt-5px " style="background-color:#2585a6 ">
+                  <div class="flex items-center h-75px ">
+                    <!-- <Icon :icon="item.icon" :size="25" class="mr-8px" /> -->
+                    <Icon :icon="item.icon" :size="38" class="mr-8px " />
+
+                    <span class="text-17px c-white">{{ item.name }}</span>
+                  </div>
+                  <div class="mb-18px text-32px c-white ml-20px text-center">{{ t(item.message) }}</div>
+                  <!-- <div class="mt-12px flex justify-between text-12px text-gray-400">                                                                  </div> -->
+                </el-card>
+              </el-col>
+            </el-row>
+          </el-card>
+
+          <el-card shadow="never" class=" h-100% mt-10px ">
+            <template #header>
+              <div class="h-5 flex justify-between fw-800 text-20px">
+                <span>实时出勤统计</span>
+              </div>
+            </template>
+            <el-card shadow="never" class="ml-2px  pr-10px  mt-10px" style="background-color:#33a3dc ;opacity: 0.8; ">
+              <div class="h-70px flex items-center justify-center flex-wrap mt-10px c-white">
+                <div class="px-25px text-center" style="margin: auto;">
+                  <div class="mb-16px text-18px text-white ">打卡人数</div>
+                  <CountTo class="text-28px" :start-val="0" :end-val="totalSate.normalNum" :duration="2600" />
+                </div>
+                <el-divider direction="vertical" border-style="dashed" />
+                <div class="px-8px text-center" style="margin: auto;">
+                  <div class="mb-16px text-18px text-white ">未打卡人数</div>
+                  <CountTo class="text-28px" :start-val="0" :end-val="totalSate.errorNum" :duration="2600" />
+                </div>
+                <el-divider direction="vertical" border-style="dashed" />
+                <div class="px-8px text-center" style="margin: auto;">
+                  <div class="mb-16px text-18px text-white ">请假人数</div>
+                  <CountTo class="text-28px" :start-val="0" :end-val="totalSate.excuseNum" :duration="2600" />
+                </div>
+              </div>
+            </el-card>
+          </el-card>
+
+          <el-card shadow="hover" class=" mt-10px">
+            <template #header>
+              <div class="h-5 flex justify-between fw-800 text-20px">
+                <span>毕业条件达成统计</span>
+              </div>
+            </template>
+            <el-skeleton :loading="loading" animated>
+              <Echart :options="pieOptionsData" :height="250" />
+            </el-skeleton>
+          </el-card>
+        </el-card>
+      </el-col>
+      <!-- 第二部分 -->
+      <el-col :xs="24" :sm="24" :md="24" :lg="10" :xl="7">
+        <!-- 第一个el-row -->
+        <el-row :gutter="15">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-card shadow="never" class=" mb-10px h-480px ">
+              <template #header>
+                <div class="h-7 flex justify-between fw-800 text-20px">
+                  <span>周出勤情况</span>
+                </div>
+              </template>
+              <el-card shadow="hover" class="mt-1px h-380px ">
+                <el-skeleton :loading="loading" animated>
+                  <Echart :options="barOptionsData" :height="350" />
+                </el-skeleton>
+              </el-card>
+            </el-card>
+          </el-col>
+
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-card shadow="never" class="mb-10px ">
+              <template #header>
+                <div class="h-7 flex justify-between fw-800 text-20px">
+                  <span>实时打卡状态</span>
+                </div>
+              </template>
+              <div class="demo">
+                <div class="table-header">
+                          <div class="header">
+                            <el-row class="shouye" style="border: none;">
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>姓名</div>
+                              </el-col>
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>学号</div>
+                              </el-col>
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>工作间</div>
+                              </el-col>
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>打卡时间</div>
+                              </el-col>
+                              <!-- <el-col :span="10" class="center text-18px" style="border: none;">
+                                <div>缺勤时间段</div>
+                              </el-col> -->
+                            </el-row>
+                          </div>
+                        </div>
+                <vue3ScrollSeamless class="scroll-wrap text-color" :classOptions="classOptions" :dataList="list">
+                  <div v-if="list.length > 0">
+                    <el-row v-for="(item, i) of list" :key="i" class="shouye"
+                      style="margin-bottom: 10px; display: flex; justify-content: center;">
+                      <el-col :span="6" class="center"
+                        style="display: flex; justify-content: center; align-items: center; padding: 6px;">
+                        <div>{{ item.studentName }}</div>
+                      </el-col>
+                      <el-col :span="6" class="center"
+                        style="display: flex; justify-content: center; align-items: center; padding: 6px;">
+                        <div>{{ item.userNumber }}</div>
+                      </el-col>
+                      <el-col :span="6" class="center"
+                        style="display: flex; justify-content: center; align-items: center; padding: 6px;">
+                        <div>{{ item.daptName }}</div>
+                      </el-col>
+                      <el-col :span="6" class="center"
+                        style="display: flex; justify-content: center; align-items: center; padding: 6px;">
+                        <div>{{ formatDate(item.clockInTime) }}</div>
+                      </el-col>
+                    </el-row>
+                  </div>
+                  <div v-if="list.length == 0"
+                    style="width: 100%; height: 100px; display: flex; justify-content: center; align-items: center; color: white; font-size: 18px;">
+                    暂无预测记录
+                  </div>
+                </vue3ScrollSeamless>
+              </div>
+            </el-card>
+
+          </el-col>
+        </el-row>
+      </el-col>
+              <!-- 第二个el-row -->
+      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="7">
+        <el-row :gutter="15" align="center">
+          <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="24">
+            <el-card shadow="never" class=" h-480px mb-10px "> <!-- 移除 el-card 的边框 -->
+              <template #header>
+                <div class="h-7 flex justify-between fw-800 text-20px">
+                  <span>缺勤预警</span>
+                </div>
+              </template>
+
+              <div class="demos">
+                <div class="table-header">
+                          <div class="header">
+                            <el-row class="shouye" style="border: none;">
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>姓名</div>
+                              </el-col>
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>学号</div>
+                              </el-col>
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>工作间</div>
+                              </el-col>
+                              <el-col :span="6" class="center text-18px" style="border: none;">
+                                <div>导师</div>
+                              </el-col>
+                              <!-- <el-col :span="10" class="center text-18px" style="border: none;">
+                                <div>缺勤时间段</div>
+                              </el-col> -->
+                            </el-row>
+                          </div>
+                        </div>
+
+                <vue3ScrollSeamless class="scroll-wraps text-color" :classOptions="class2Options" :dataList="list2">
+                  <div v-if="list2.length > 0">
+                    <el-row v-for="(item, i) of list2" :key="i" class="shouye">
+                      <!-- <el-col :span="6" class="center" style="padding: 10px; border: none;">
+                        <div>{{ item.ID }}</div>
+                      </el-col> -->
+                      <el-col :span="6" class="center" style="padding: 10px; border: none;">
+                        <div>{{ item.studentName }}</div>
+                      </el-col>
+                      <el-col :span="6" class="center" style="padding: 10px; border: none;">
+                        <div>{{ item.userNumber }}</div>
+                      </el-col>
+                      <el-col :span="6" class="center" style="padding: 10px; border: none;">
+                        <div>{{ item.deptName }}</div>
+                      </el-col>
+                      <el-col :span="6" class="center" style="padding: 10px; border: none;">
+                        <div>{{ item.supervisor }}</div>
+                      </el-col>
+                    </el-row>
+                  </div>
+                  <div v-if="list2.length == 0"
+                    style="width: 100%; height: 100px; display: flex; justify-content: center; align-items: center; color: white; font-size: 18px;">
+                    暂无预测记录
+                  </div>
+                </vue3ScrollSeamless>
+              </div>
+            </el-card>
+
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="24">
+            <el-card shadow="never" class=" mt-1px h-480px ">
+              <template #header>
+                <div class="h-7 flex justify-between fw-800 text-20px">
+                  <span>学生毕业条件达成率</span>
+                </div>
+              </template>
+              <div class="demoss">
+                <vue3ScrollSeamless class="scroll-wrapss  text-color" :classOptions="list1Options" :dataList="list1">
+                  <div v-if="list1.length > 0">
+                    <el-row v-for="(item, i) of list1" :key="i" class="shouye" :style="{ marginBottom: '10px' }">
+                      <!-- 增加行与行之间的间距 -->
+                      <el-col :span="12" class="center" style="padding: 8px;"> <!-- 增加内边距 -->
+                        <div>{{ item.name }}</div>
+                      </el-col>
+                      <el-col :span="12" class="center" style="padding: 8px;">
+                        <div>{{ item.graduationRate }}%</div>
+                      </el-col>
+                    </el-row>
+                  </div>
+                  <div v-if="list1.length == 0"
+                    style="width: 100%; height: 100px; display: flex; justify-content: center; align-items: center; color: white; font-size: 18px;">
+                    暂无预测记录
+                  </div>
+                </vue3ScrollSeamless>
+
+              </div>
+
+            </el-card>
+          </el-col>
+        </el-row>
+      </el-col>
+    </el-row>
+  </el-skeleton>
+</template>
+
+<script lang="ts" setup>
+import { EChartsOption, List } from 'echarts'
+import { formatTime } from '@/utils'
+import {formatDate} from "@/utils/formatTime";
+import { useUserStore } from '@/store/modules/user'
+import { useWatermark } from '@/hooks/web/useWatermark'
+import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
+import { pieOptions, barOptions } from './echarts-data'
+import { reactive, onMounted, watchEffect } from "vue";
+import { vue3ScrollSeamless } from "vue3-scroll-seamless";
+import * as UserApi from '@/api/system/user'
+import * as  DeptApi from '@/api/system/dept'
+import  { StudentAttendanceApi } from '@/api/system/studentAttendance'
+
+defineOptions({ name: 'Home' })
+
+const { t } = useI18n()
+const userStore = useUserStore()
+const { setWatermark } = useWatermark()
+const loading = ref(true)
+const avatar = userStore.getUser.avatar
+const username = userStore.getUser.nickname
+
+
+
+/*基本信息*/
+const detail = reactive({
+  deptNum: undefined,
+  teacherNum: undefined,
+  studentNum: undefined
+})
+const getDetail = async () => {
+  const data = await UserApi.getDetail()
+  console.log("基本信息", data);
+  detail.deptNum = data.deptNum
+  detail.teacherNum = data.teacherNum
+  detail.studentNum = data.studentNum
+}
+// 获取项目数
+let projects = reactive<Project[]>([])
+const getProject = async () => {
+  const data = [
+    {
+      name: '学院工作间数量',
+      icon: 'svg-icon:gzs',
+      message: String(detail.deptNum) + '个',
+      time: new Date()
+    },
+    {
+      name: '导师数量',
+      icon: 'svg-icon:ds',
+      message: String(detail.teacherNum) + '人',
+      time: new Date()
+    },
+    {
+      name: '在校生数量',
+      icon: 'svg-icon:zxs',
+      message: String(detail.studentNum) + '人',
+      time: new Date()
+    }
+
+  ]
+  projects = Object.assign(projects, data)
+}
+
+
+/**实时出勤统计 */
+// 获取统计数
+let totalSate = reactive<WorkplaceTotal>({
+  normalNum: 0,
+  errorNum: 0,
+  excuseNum: 0,
+})
+const getCount = async () => {
+  const data = await StudentAttendanceApi.getDayAttendance()
+  console.log("实时出勤统计", data);
+  totalSate = Object.assign(totalSate, data)
+}
+
+// /**毕业达成统计 */
+let pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
+const getGraduateCount = async () => {
+  const data = await UserApi.getGraduateCount()
+  console.log("毕业达成统计", data);
+  const studentNum = data.studentNum;
+  const graduateNum = data.graduateNum;
+  const options = {
+    title: {
+      text: null,
+      left: 'center',
+      top: '20px'
+    },
+    tooltip: {
+      trigger: 'item',
+    },
+    legend: {
+      top: '10%',  // 顶部位置
+      left: 'left', // 左侧位置
+    },
+    series: [
+      {
+        type: 'pie',
+        radius: ['120px', '70px'], // 设定内圈和外圈半径
+        center: ['50%', '85%'], // 中心位置
+        startAngle: 180,
+        endAngle: 360,
+        data: [
+          {
+            value: graduateNum, // 畢業生数量
+            name: '已达成', // 国际化名称或直接用字符串 '毕业生'
+            label: {
+              show: true,
+              position: 'outside',  // 拉出标注
+              formatter: '{b}: {c}', // 显示名称和数值
+              emphasis: {
+                show: true,
+              },
+            },
+            labelLine: {
+              show: true,
+              length: 75,
+              length2: 10,
+              smooth: true,
+              lineDash: [5, 5],
+            },
+          },
+          {
+            value: studentNum, // 在校生数量(总在校生减去毕业生)
+            name: '未达成', // 国际化名称或直接用字符串 '在校生'
+            label: {
+              show: true,
+              position: 'outside',
+              formatter: '{b}: {c}',
+              emphasis: {
+                show: true,
+              },
+            },
+            labelLine: {
+              show: true,
+              length: 75,
+              length2: 10,
+              smooth: true,
+              lineDash: [5, 5],
+            },
+          },
+        ],
+        itemStyle: {
+          // 设置饼图的颜色
+          color: function (params) {
+            const colorList = ['#2585a6','#5cb5e3' ]; // 定义颜色数组
+            return colorList[params.dataIndex]; // 根据数据索引返回对应的颜色
+          }
+        }
+      },
+    ],
+  };
+  pieOptionsData = Object.assign(pieOptionsData, options);
+};
+
+/**周出勤情况 */
+let barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
+const getWeekend = async () => {
+    const data = await StudentAttendanceApi.getWeekendAttendance();
+    console.log("周出勤情况", data);
+    const normalData = data.dailyNormalList
+    const errorData = data.dailyErrorList;
+    const excuseData = data.dailyExcuseList;
+    barOptionsData.series[0].data = normalData;
+    barOptionsData.series[1].data = errorData; 
+    barOptionsData.series[2].data = excuseData;
+};
+
+/**缺勤预警 */
+let list2 = reactive([]);
+const getStudentAttendanceError = async () => {
+  const data = await StudentAttendanceApi.getDayStudentErrorAttendance()
+  console.log("缺勤列表", data);
+  list2.splice(0, list2.length, ...data);
+}
+const class2Options = reactive({
+  step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
+  limitMoveNum: list2.length,//无缝滚动列表元素的长度,一般设置为列表的长度
+  direction: 1,//方向: 0 往下 1 往上 2 向左 3 向右。
+});
+
+/**实时打卡状态 */
+//打卡滚动列表
+const list = reactive([]);
+const getStudentAttendance = async () => {
+  const data = await StudentAttendanceApi.getDayStudentAttendance()
+  console.log("打卡列表", data);
+  list.splice(0, list.length, ...data);
+}
+const classOptions = reactive({
+  step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
+  limitMoveNum: list.length,//无缝滚动列表元素的长度,一般设置为列表的长度
+  direction: 1,//方向: 0 往下 1 往上 2 向左 3 向右。
+
+});
+
+/** 学生毕业条件达成率 */
+//在线情况滚动列表
+const list1 = reactive([]);
+const getGraduationSource = async () => {
+  const data = await DeptApi.getGraduationSource()
+  console.log("毕业条件达成率", data);
+  list1.splice(0, list1.length,  ...data);
+}
+const list1Options = reactive({
+  step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
+  limitMoveNum: list1.length,//无缝滚动列表元素的长度,一般设置为列表的长度
+  direction: 1,//方向: 0 往下 1 往上 2 向左 3 向右。
+});
+
+onMounted(() => {
+  getDetail()
+  getWeekend()
+  getProject()
+  getCount()
+  getStudentAttendanceError()
+  getStudentAttendance()
+  getGraduationSource()
+  getGraduateCount()
+})
+
+const getAllApi = async () => {
+  await Promise.all([
+    await getDetail(),
+    getCount(),
+    getProject(),
+    getWeekend(),
+    getStudentAttendanceError(),
+    getStudentAttendance(),
+    getGraduationSource(),
+    getGraduateCount(),
+    // getNotice(),
+    // getBasic(),
+    // getShortcut(),
+    // getUserAccessSource(),
+    // getWeeklyUserActivity()
+  ])
+  loading.value = false
+}
+
+getAllApi()
+</script>
+
+<style scoped>
+.center {
+  text-align: center;
+}
+
+/* 滚动列表 */
+.title-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 20px;
+  margin-bottom: 30px;
+}
+
+.title {
+  font-size: 19px;
+}
+
+.demo {
+  width: 99%;
+  height: 362px;
+}
+
+.demos {
+  width: 95%;
+  height: 100%;
+}
+
+.demoss {
+  width: 95%;
+  height: 100%;
+}
+
+.scroll-wrap {
+  width: 95%;
+  height: 330px;
+  margin: 0 auto;
+  overflow: hidden;
+  /* background-color: #feeeed; */
+  opacity: 0.6;
+  font-size: 15px;
+  margin-top: 20px;
+  margin-left: 18px;
+
+}
+
+.scroll-wraps {
+  width: 101%;
+  height: 350px;
+  margin: 0 auto;
+  overflow: hidden;
+  opacity: 0.6;
+  font-size: 15px;
+  border: none;
+  margin-top: 20px;
+
+}
+
+.scroll-wrapss {
+  width: 90%;
+  height: 350px;
+  margin: 0 auto;
+  overflow: hidden;
+  /* background-color: rgba(198, 204, 238, 0.3); */
+  /* background-color: #feeeed; */
+  opacity: 0.6;
+  font-size: 15px;
+  margin-top: 10px;
+
+}
+
+/* 轮播图的导航条
+.table-header {
+  font-family: Arial, sans-serif;
+  height: 30px;
+  display: flex;
+  align-items: center;
+  border: 1px solid;
+  width: 280px;
+  flex-wrap: nowrap;
+  padding-right: 10px;
+}
+
+.header {
+  width: 660px;
+  font-size: 16px;
+} */
+
+.border {
+  border: 1px solid;
+}
+
+.center {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+}
+
+/* .text-color {
+ color: rgb(15, 131, 233);
+} */
+
+
+
+.shouye {
+  flex-wrap: nowrap;
+  border-bottom: 1px solid;
+}
+
+::v-deep .el-divider.el-divider--vertical {
+  border-left: 1px solid white;
+}
+</style>

+ 329 - 0
src/views/system/Home/echarts-data.ts

@@ -0,0 +1,329 @@
+import { EChartsOption } from 'echarts'
+
+const { t } = useI18n()
+
+export const lineOptions: EChartsOption = {
+  title: {
+    text: t('analysis.monthlySales'),
+    left: 'center'
+  },
+  xAxis: {
+    data: [
+      t('analysis.january'),
+      t('analysis.february'),
+      t('analysis.march'),
+      t('analysis.april'),
+      t('analysis.may'),
+      t('analysis.june'),
+      t('analysis.july'),
+      t('analysis.august'),
+      t('analysis.september'),
+      t('analysis.october'),
+      t('analysis.november'),
+      t('analysis.december')
+    ],
+    boundaryGap: false,
+    axisTick: {
+      show: false
+    }
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    top: 80,
+    containLabel: true
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross'
+    },
+    padding: [5, 10]
+  },
+  yAxis: {
+    axisTick: {
+      show: false
+    }
+  },
+  legend: {
+    data: [t('analysis.estimate'), t('analysis.actual')],
+    top: 50
+  },
+  series: [
+    {
+      name: t('analysis.estimate'),
+      smooth: true,
+      type: 'line',
+      data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
+      animationDuration: 2800,
+      animationEasing: 'cubicInOut'
+    },
+    {
+      name: t('analysis.actual'),
+      smooth: true,
+      type: 'line',
+      itemStyle: {},
+      data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123],
+      animationDuration: 2800,
+      animationEasing: 'quadraticOut'
+    }
+  ]
+}
+
+export const pieOptions: EChartsOption = {
+  title: {
+    text: t('analysis.userAccessSource'),
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item',
+    formatter: '{a} <br/>{b} : {c} ({d}%)'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left',
+    data: [
+      t('analysis.directAccess'),
+      t('analysis.mailMarketing'),
+      t('analysis.allianceAdvertising'),
+      t('analysis.videoAdvertising'),
+      t('analysis.searchEngines')
+    ]
+  },
+  series: [
+    {
+      name: t('analysis.userAccessSource'),
+      type: 'pie',
+      radius: ['120px', '70px'],
+      center: ['50%', '85%'],
+      startAngle: 180,
+      endAngle: 360,
+      data: [
+        { value: 335, name: t('analysis.directAccess') },
+        { value: 310, name: t('analysis.mailMarketing') },
+        { value: 234, name: t('analysis.allianceAdvertising') },
+        { value: 135, name: t('analysis.videoAdvertising') },
+        { value: 1548, name: t('analysis.searchEngines') }
+      ]
+    }
+  ]
+}
+
+export const barOptions: EChartsOption = {
+  // title: {
+  // text: t('analysis.weeklyUserActivity'),
+  //   left: 'center'
+  // },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: 50,
+    right: 20,
+    bottom: 20
+  },
+  xAxis: {
+    type: 'category',
+    data: [
+      t('analysis.monday'),
+      t('analysis.tuesday'),
+      t('analysis.wednesday'),
+      t('analysis.thursday'),
+      t('analysis.friday'),
+      t('analysis.saturday'),
+      t('analysis.sunday')
+    ],
+    axisTick: {
+      alignWithLabel: true
+    }
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '正常',
+      data: [],
+      type: 'bar',
+      itemStyle: {
+        color: '#5cb5e3'
+      }
+    },
+    {
+      name: '异常',
+      data: [],
+      type: 'bar',
+      itemStyle: {
+        color: '#CD5C5C'
+      }
+    },
+    {
+      name: '请假',
+      data: [],
+      type: 'bar',
+      itemStyle: {
+        color: '#FFF68F'
+      }
+    }
+  ]
+}
+
+export const radarOption: EChartsOption = {
+  legend: {
+    data: [t('workplace.personal'), t('workplace.team')]
+  },
+  radar: {
+    // shape: 'circle',
+    indicator: [
+      { name: t('workplace.quote'), max: 65 },
+      { name: t('workplace.contribution'), max: 160 },
+      { name: t('workplace.hot'), max: 300 },
+      { name: t('workplace.yield'), max: 130 },
+      { name: t('workplace.follow'), max: 100 }
+    ]
+  },
+  series: [
+    {
+      name: `xxx${t('workplace.index')}`,
+      type: 'radar',
+      data: [
+        {
+          value: [42, 30, 20, 35, 80],
+          name: t('workplace.personal')
+        },
+        {
+          value: [50, 140, 290, 100, 90],
+          name: t('workplace.team')
+        }
+      ]
+    }
+  ]
+}
+
+export const wordOptions = {
+  series: [
+    {
+      type: 'wordCloud',
+      gridSize: 2,
+      sizeRange: [12, 50],
+      rotationRange: [-90, 90],
+      shape: 'pentagon',
+      width: 600,
+      height: 400,
+      drawOutOfBound: true,
+      textStyle: {
+        color: function () {
+          return (
+            'rgb(' +
+            [
+              Math.round(Math.random() * 160),
+              Math.round(Math.random() * 160),
+              Math.round(Math.random() * 160)
+            ].join(',') +
+            ')'
+          )
+        }
+      },
+      emphasis: {
+        textStyle: {
+          shadowBlur: 10,
+          shadowColor: '#333'
+        }
+      },
+      data: [
+        {
+          name: 'Sam S Club',
+          value: 10000,
+          textStyle: {
+            color: 'black'
+          },
+          emphasis: {
+            textStyle: {
+              color: 'red'
+            }
+          }
+        },
+        {
+          name: 'Macys',
+          value: 6181
+        },
+        {
+          name: 'Amy Schumer',
+          value: 4386
+        },
+        {
+          name: 'Jurassic World',
+          value: 4055
+        },
+        {
+          name: 'Charter Communications',
+          value: 2467
+        },
+        {
+          name: 'Chick Fil A',
+          value: 2244
+        },
+        {
+          name: 'Planet Fitness',
+          value: 1898
+        },
+        {
+          name: 'Pitch Perfect',
+          value: 1484
+        },
+        {
+          name: 'Express',
+          value: 1112
+        },
+        {
+          name: 'Home',
+          value: 965
+        },
+        {
+          name: 'Johnny Depp',
+          value: 847
+        },
+        {
+          name: 'Lena Dunham',
+          value: 582
+        },
+        {
+          name: 'Lewis Hamilton',
+          value: 555
+        },
+        {
+          name: 'KXAN',
+          value: 550
+        },
+        {
+          name: 'Mary Ellen Mark',
+          value: 462
+        },
+        {
+          name: 'Farrah Abraham',
+          value: 366
+        },
+        {
+          name: 'Rita Ora',
+          value: 360
+        },
+        {
+          name: 'Serena Williams',
+          value: 282
+        },
+        {
+          name: 'NCAA baseball tournament',
+          value: 273
+        },
+        {
+          name: 'Point Break',
+          value: 265
+        }
+      ]
+    }
+  ]
+}

+ 55 - 0
src/views/system/Home/types.ts

@@ -0,0 +1,55 @@
+export type WorkplaceTotal = {
+  normalNum: number
+  errorNum: number
+  excuseNum: number
+}
+
+export type Project = {
+  name: string
+  icon: string
+  message: string
+  personal: string
+  time: Date | number | string
+}
+
+export type Notice = {
+  title: string
+  type: string
+  keys: string[]
+  date: Date | number | string
+}
+
+export type Shortcut = {
+  name: string
+  icon: string
+  url: string
+}
+
+export type RadarData = {
+  personal: number
+  team: number
+  max: number
+  name: string
+}
+export type AnalysisTotalTypes = {
+  users: number
+  messages: number
+  moneys: number
+  shoppings: number
+}
+
+export type UserAccessSource = {
+  value: number
+  name: string
+}
+
+export type WeeklyUserActivity = {
+  value: number
+  name: string
+}
+
+export type MonthlySales = {
+  name: string
+  estimate: number
+  actual: number
+}

+ 2 - 0
src/views/system/selfAchievement/index.vue

@@ -176,6 +176,7 @@
             @click="openForm('update', rowData.row.id)"
             v-hasPermi="['system:user-achievement:update']"
           >
+            <Icon icon="ep:edit"/>
             编辑
           </el-button>
           <el-button
@@ -184,6 +185,7 @@
             @click="handleDelete(rowData.row.id)"
             v-hasPermi="['system:user-achievement:delete']"
           >
+            <Icon icon="ep:delete"/>
             删除
           </el-button>
         </template>

+ 1 - 1
src/views/system/studentAttendanceManage/studentAttendance/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <ContentWrap>
+  <ContentWrap v-if="userInfo.userType !== '1'">
     <!-- 数据展示 -->
     <div class="flex">
       <div class="data-item" style="color: black;">

+ 1 - 1
src/views/system/studentAttendanceManage/studentAttendanceError/index.vue

@@ -1,5 +1,5 @@
 <template>
-<ContentWrap>
+<ContentWrap v-if="userInfo.userType !== '1'">
     <!-- 数据展示 -->
     <div class="flex">
       <div class="data-item" style="color: black;">

+ 1 - 1
src/views/system/studentAttendanceManage/studentAttendanceExcused/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <ContentWrap>
+  <ContentWrap v-if="userInfo.userType !== '1'">
     <!-- 数据展示 -->
     <div class="flex">
       <div class="data-item" style="color: black;">

+ 2 - 0
src/views/system/studentAttendanceManage/studentFaceManage/index.vue

@@ -173,6 +173,7 @@
             @click="openForm('update', scope.row.id)"
             v-hasPermi="['md:acs:teacher-user-image:update']"
           >
+            <Icon icon="ep:edit"/>
             编辑
           </el-button>
           <el-button
@@ -181,6 +182,7 @@
             @click="handleDelete(scope.row.userNumber)"
             v-hasPermi="['system:student-attendance:query']"
           >
+            <Icon icon="ep:delete" />
             删除
           </el-button>
         </template>

+ 3 - 0
src/views/system/workroomCollege/dept/index.vue

@@ -164,9 +164,11 @@
             @click="openForm('update', scope.row.id)"
             v-hasPermi="['system:dept:update']"
           >
+            <Icon icon="ep:edit" />
             修改
           </el-button>
           <el-button type="text" @click="openUserAchievement(scope.row.id)">
+            <Icon icon="ep:view" />
             详情
           </el-button>
           <el-button
@@ -175,6 +177,7 @@
             @click="handleDelete(scope.row.id)"
             v-hasPermi="['system:dept:delete']"
           >
+            <Icon icon="ep:delete" />
             删除
           </el-button>
         </template>

+ 6 - 3
src/views/system/workroomCollege/deptInfo/index.vue

@@ -17,7 +17,7 @@
         <ul class="user-info">
           <div class="info-row">
             <li class="info-item">
-              <Icon class="mr-5px" icon="ep:user" />
+              <Icon class="mr-5px" icon="ep:bell" />
               <span class="info-label">工作间名称:</span>
               <span class="pull-right">{{ userInfo.name }}</span>
             </li>
@@ -43,7 +43,7 @@
 
           <div class="info-row">
             <li class="info-item">
-              <Icon class="mr-5px" icon="fontisto:email" />
+              <Icon class="mr-5px" icon="ep:user" />
               <span class="info-label">导师人数:</span>
               <span class="pull-right">{{ userInfo.supervisorNum }}</span>
             </li>
@@ -64,7 +64,10 @@
         </ul>
         <div class="info-description">
           <!-- <Icon class="mr-5px" icon="ep:location" /> -->
-          <div class="div-label" style="margin-left: 20px;">简介:</div>
+          <div style="display: flex; align-items: center;">
+            <Icon icon="ep:picture" />
+            <div class="div-label" style="margin-left: 8px;">简介:</div>
+          </div>
           <div class="description-content">{{ cleanedDescription }}</div>
           <div class="image-container">
         <template v-for="(img, index) in extractedImages" :key="index">

+ 1 - 0
src/views/system/workroomCollege/user/student.vue

@@ -277,6 +277,7 @@
                   <Icon icon="ep:edit" />修改
                 </el-button>
                 <el-button type="text" @click="openUserAchievement(scope.row.id)">
+                  <Icon icon="ep:bell" />
                   成果详情
                 </el-button>
                 <el-button

+ 4 - 4
src/views/system/workroomTeacher/TeacherSelf/index.vue

@@ -17,7 +17,7 @@
               <span class="pull-right">{{ userInfo.nickname }}</span>
             </li>
             <li class="info-item">
-              <Icon class="mr-5px" icon="ep:phone" />
+              <Icon class="mr-5px" icon="ep:location" />
               <span class="info-label">工作间:</span>
               <span class="pull-right">{{ userInfo.dept ? userInfo.dept.name : '未知部门' }}</span>
             </li>
@@ -25,12 +25,12 @@
 
           <div class="info-row">
             <li class="info-item">
-              <Icon class="mr-5px" icon="fontisto:email" />
+              <Icon class="mr-5px" icon="ep:phone" />
               <span class="info-label">手机号码:</span>
               <span class="pull-right">{{ userInfo.mobile }}</span>
             </li>
             <li class="info-item">
-              <Icon class="mr-5px" icon="ep:location" />
+              <Icon class="mr-5px" icon="fontisto:email" />
               <span class="info-label">邮箱:</span>
               <span class="pull-right">{{ userInfo.email }}</span>
             </li>
@@ -43,7 +43,7 @@
               <span class="pull-right"> {{ userTypeMapping[userInfo.userType] || '未知用户类型' }}</span>
             </li>
             <li class="info-item">
-              <Icon class="mr-5px" icon="ep:location" />
+              <Icon class="mr-5px" icon="ep:bell" />
               <span class="info-label">工号:</span>
               <span class="pull-right">{{ userInfo.userNumber }}</span>
             </li>

+ 7 - 4
src/views/system/workroomTeacher/dept/index.vue

@@ -19,7 +19,7 @@
         <ul class="user-info">
           <div class="info-row">
             <li class="info-item">
-              <Icon class="mr-5px" icon="ep:user" />
+              <Icon class="mr-5px" icon="ep:bell" />
               <span class="info-label">工作间名称:</span>
               <span class="pull-right">{{ userInfo.name }}</span>
             </li>
@@ -45,7 +45,7 @@
 
           <div class="info-row">
             <li class="info-item">
-              <Icon class="mr-5px" icon="fontisto:email" />
+              <Icon class="mr-5px" icon="ep:user" />
               <span class="info-label">导师人数:</span>
               <span class="pull-right">{{ userInfo.supervisorNum }}</span>
             </li>
@@ -66,7 +66,10 @@
         </ul>
         <div class="info-description">
           <!-- <Icon class="mr-5px" icon="ep:location" /> -->
-          <div class="div-label" style="margin-left: 20px;">简介:</div>
+          <div style="display: flex; align-items: center;">
+            <Icon icon="ep:picture" />
+            <div class="div-label" style="margin-left: 8px;">简介:</div>
+          </div>
           <div class="description-content">{{ cleanedDescription }}</div>
           <div class="image-container">
             <div v-for="(url, index) in extractedImageUrls" :key="index" class="image-item">
@@ -168,7 +171,7 @@ export default defineComponent({
     const userInfo = ref({} as DeptVO);  
     const fetchUserInfo = async () => {
       const users = await getUserDept();
-      console.log(users,'userInfo');
+      console.log(users,'userDeptInfo');
       userInfo.value = {...users,
         // name: users.user[0].nickname // 提取 nickname
       };

+ 1 - 0
src/views/system/workroomTeacher/user/student.vue

@@ -242,6 +242,7 @@
                   <Icon icon="ep:edit" />修改
                 </el-button>
                 <el-button type="text" @click="openUserAchievement(scope.row.id)">
+                  <icon icon="ep:bell" />
                   成果详情
                 </el-button>
                 <el-button