47 3 weeks ago
parent
commit
a3fe8d630b

+ 2 - 2
package.json

@@ -22,7 +22,7 @@
     "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
     "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
     "lint:style": "stylelint --fix \"./src/**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
-    "lint:lint-staged": "lint-staged -c "
+    "lint:lint-staged": "lint-staged -c " 
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.1.0",
@@ -159,4 +159,4 @@
     "node": ">= 16.0.0",
     "pnpm": ">=8.6.0"
   }
-}
+}

File diff suppressed because it is too large
+ 230 - 203
pnpm-lock.yaml


+ 1 - 1
src/router/modules/remaining.ts

@@ -166,7 +166,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
     children: [
       {
         path: '/redirect/:path(.*)',
-        name: 'Redirect',
+        name: 'RedirectDetail',
         component: () => import('@/views/Redirect/Redirect.vue'),
         meta: {}
       }

+ 34 - 42
src/utils/doc.js

@@ -1,20 +1,22 @@
 import JSZip from 'jszip';
 import { saveAs } from 'file-saver';
 import JSZipUtils from 'jszip-utils';
-import PizZip from 'pizzip';
 import Docxtemplater from 'docxtemplater';
-import ImageModule from 'docxtemplater-image-module-free'
+import ImageModule from 'docxtemplater-image-module-free';
 
-// function base64ToUint8Array(base64) {
-//   const base64Data = base64.split(',')[1]; // 去掉前缀 "data:image/png;base64,"
-//   const binaryString = atob(base64Data); // 解码 Base64
-//   const length = binaryString.length;
-//   const uint8Array = new Uint8Array(length);
-//   for (let i = 0; i < length; i++) {
-//     uint8Array[i] = binaryString.charCodeAt(i);
-//   }
-//   return uint8Array;
-// }
+const loadImage = async (tagValue) => {
+  try {
+    const response = await fetch(tagValue);
+    if (!response.ok) {
+      throw new Error('Network response was not ok');
+    }
+    const blob = await response.blob();
+    return URL.createObjectURL(blob);
+  } catch (error) {
+    console.error('There was a problem with the fetch operation:', error);
+    return null; // 返回 null 或者合适的错误处理
+  }
+};
 export const exportDocx = async (tempDocxPath, dataList, zipFileName) => {
   try {
     // 加载模板文件
@@ -34,40 +36,30 @@ export const exportDocx = async (tempDocxPath, dataList, zipFileName) => {
     // 遍历每一条数据,生成单独的 .docx 文件
     for (let i = 0; i < dataList.length; i++) {
       const data = dataList[i];
-      // if (data.studentSignature) {
-      //   data.studentSignature = {
-      //     data: base64ToUint8Array(data.studentSignature),
-      //     width: 100, // 图片宽度(单位:像素)
-      //     height: 50, // 图片高度(单位:像素)
-      //   };
-      // }
-      // if (data.supervisorSignature) {
-      //   data.supervisorSignature = {
-      //     data: base64ToUint8Array(data.supervisorSignature),
-      //     width: 100, // 图片宽度(单位:像素)
-      //     height: 50, // 图片高度(单位:像素)
-      //   };
-      // }
-      const zipInstance = new PizZip(content);
-      const doc = new Docxtemplater().loadZip(zipInstance);
-       // 加载图片模块
-      //  const imageModule = new ImageModule({
-      //   centered: false, // 图片是否居中
-      //   getImage: (tagValue) => tagValue.data, // 获取图片数据
-      //   getSize: (tagValue) => [tagValue.width, tagValue.height], // 获取图片尺寸
-      // });
-      // doc.attachModule(imageModule);
-      doc.setData(data);
 
-      try {
-        doc.render(); // 渲染模板
-      } catch (error) {
-        console.error(`渲染第 ${i + 1} 条数据时出错:`, error);
-        throw error;
+      const imageOptions = {
+        async getImage(tagValue) {
+          const imageUrl = await loadImage(tagValue);
+          console.log(imageUrl, "我在这里");
+          return imageUrl;
+        },
+        getSize() {
+          return [150, 150];
+        },
+      };
+      const doc = new Docxtemplater(zip, {
+        modules: new ImageModule(imageOptions),
+      });
+      doc.render({
+        image: "logo.png",
+      });
+      const zipContent = doc.getZip(); // 获取 zip 对象
+      if (!zipContent) {
+        throw new Error('Docxtemplater failed to load zip content.');
       }
 
       // 生成 .docx 文件
-      const out = doc.getZip().generate({
+      const out = await zipContent.generateAsync({
         type: 'blob',
         mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
       });

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

@@ -166,21 +166,26 @@
                         </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"
+                    <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"
+                      <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"
+                      <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"
+                      <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"
+                      <el-col
+:span="6" class="center"
                         style="display: flex; justify-content: center; align-items: center; padding: 6px;">
                         <div>{{ formatDate(item.clockInTime) }}</div>
                       </el-col>
@@ -297,7 +302,8 @@
                       </el-col>
                     </el-row>
                   </div>
-                  <div v-if="list1.length == 0"
+                  <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>

+ 2 - 1
src/views/Profile/components/FaceInfo.vue

@@ -2,7 +2,8 @@
   <ContentWrap>
     <el-form ref="formRef" :model="formData" label-width="100px">
       <el-form-item label="人脸信息" prop="photoUrl">
-        <UploadImg v-model="formData.photoUrl"       
+        <UploadImg
+v-model="formData.photoUrl"       
           successMessage = "加载成功,请点击确认上传!!"
         >
           <template #default>

+ 1 - 1
src/views/Redirect/Redirect.vue

@@ -2,7 +2,7 @@
   <div></div>
 </template>
 <script lang="ts" setup>
-defineOptions({ name: 'Redirect' })
+defineOptions({ name: 'RedirectDetail' })
 
 const { currentRoute, replace } = useRouter()
 const { params, query } = unref(currentRoute)

+ 50 - 25
src/views/system/Home/Index.vue

@@ -12,13 +12,16 @@
             </template>
 
             <el-row style="flex-wrap: wrap; ">
-              <el-col v-for="(item, index) in projects" :key="`card-${index}`" :xs="24" :sm="8"
+              <el-col
+v-for="(item, index) in projects" :key="`card-${index}`" :xs="24" :sm="8"
                       :md="8" :lg="8" :xl="8">
-                <el-card  class="mr-10px mt-5px  ml-10px "
+                <el-card
+class="mr-10px mt-5px  ml-10px "
                          :style="{ backgroundColor: item.cardBgColor }">
                   <div class="flex items-center h-120px ">
                     <!-- <Icon :icon="item.icon" :size="25" class="mr-8px" /> -->
-                    <Icon :icon="item.icon" :size="80"
+                    <Icon
+:icon="item.icon" :size="80"
                           :style="{ backgroundColor: item.iconBgColor, color: item. iconColor, borderRadius: '50%' }"
                           class="circle-icon"/>
                     <span class="text-30px c-black ">{{ item.name }}</span>
@@ -40,31 +43,36 @@
               <span>实时出勤统计</span>
             </div>
           </template>
-          <el-card  class="pr-5px  mt-10px"
+          <el-card
+class="pr-5px  mt-10px"
                    style="background-color:#56adfc ;opacity: 0.8; width: 90%; margin: auto;border-radius:10px">
             <div class="h-35px flex items-center justify-center flex-wrap mt-0px c-white mb-15px">
               <div class="px-25px text-center" style="margin: auto;">
                 <div class="mb-5px text-20px text-white fw-550">打卡人数</div>
-                <CountTo class="text-28px" :start-val="0" :end-val="totalSate.normalNum"
+                <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-5px text-20px text-white fw-550">未打卡人数</div>
-                <CountTo class="text-28px" :start-val="0" :end-val="totalSate.errorNum"
+                <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=" text-20px text-white fw-550">请假人数</div>
-                <CountTo class="text-28px" :start-val="0" :end-val="totalSate.excuseNum"
+                <CountTo
+class="text-28px" :start-val="0" :end-val="totalSate.excuseNum"
                          :duration="2600"/>
               </div>
             </div>
           </el-card>
           <el-row :gutter="8">
             <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
-              <el-card  class="mb-27px h-465px mt-40px mr-15px ="
+              <el-card
+class="mb-27px h-465px mt-40px mr-15px ="
                        style="min-width: 250px ;background-color: rgb(255,255,255);border: 4px solid rgba(109,181,255,0.42);border-radius:15px">
                 <template #header >
                   <div class="h-7 flex justify-between fw-800 text-22px ">
@@ -93,24 +101,30 @@
                     </el-row>
                   </div>
                 </div>
-                <vue3ScrollSeamless class="scroll-wrap text-color " :classOptions="classOptions"
+                <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"
+                    <el-row
+v-for="(item, i) of list" :key="i" class="shouye"
                             style="margin-bottom: 5px; display: flex; justify-content: center;background :rgba(147,208,255,0.18);padding: 5px">
-                      <el-col :span="6" class="center"
+                      <el-col
+:span="6" class="center"
                               style="display: flex; justify-content: center; align-items: center; padding: 6px;">
                         <div style="font-weight: 900">{{ item.studentName }}</div>
                       </el-col>
-                      <el-col :span="6" class="center"
+                      <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"
+                      <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"
+                      <el-col
+:span="6" class="center"
                               style="display: flex; justify-content: center; align-items: center; padding: 6px;">
                         <div>{{ formatDate(item.clockInTime) }}</div>
                       </el-col>
@@ -121,7 +135,8 @@
                     暂无预测记录
                   </div> -->
                 </vue3ScrollSeamless>
-                <div v-if="list.length == 0"
+                <div
+v-if="list.length == 0"
                      style="width: 100%; height: 100px; display: flex; justify-content: center; align-items: center; margin-top: -230px;">
                   <el-empty description="暂无数据"/>
                 </div>
@@ -129,7 +144,8 @@
               </el-card>
             </el-col>
             <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
-              <el-card  class=" h-465px mb-10px mt-40px mr-15px "
+              <el-card
+class=" h-465px mb-10px mt-40px mr-15px "
                        style="min-width: 250px ;background-color: rgb(255,255,255);border: 4px solid rgba(109,181,255,0.42);border-radius:15px">
                 <!-- 移除 el-card 的边框 -->
                 <template #header>
@@ -161,10 +177,12 @@
                     </div>
                   </div>
 
-                  <vue3ScrollSeamless class="scroll-wraps text-color" :classOptions="class2Options"
+                  <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-row
+v-for="(item, i) of list2" :key="i" class="shouye"
                               style="margin-bottom: 5px; display: flex; justify-content: center;background :rgba(147,208,255,0.18);padding:2px">
                         <!-- <el-col :span="6" class="center" style="padding: 10px; border: none;">
                           <div>{{ item.ID }}</div>
@@ -188,7 +206,8 @@
                       暂无预测记录
                     </div> -->
                   </vue3ScrollSeamless>
-                  <div v-if="list2.length == 0"
+                  <div
+v-if="list2.length == 0"
                        style="width: 100%; height: 100px; display: flex; justify-content: center; align-items: center; margin-top: -230px;">
                     <el-empty description="暂无数据"/>
                   </div>
@@ -196,7 +215,8 @@
               </el-card>
             </el-col>
             <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
-              <el-card  class=" mb-27px h-465px mt-40px "
+              <el-card
+class=" mb-27px h-465px mt-40px "
                        style="border: 4px solid rgba(109,181,255,0.42);border-radius:15px">
                 <template #header>
                   <div class="h-7 flex justify-between fw-800 text-20px">
@@ -243,20 +263,24 @@
               <div class="demoss">
                 <div class="header">
                   <el-row class="shouye h-40px" style="border: none;">
-                    <el-col :span="12" class="center text-18px"
+                    <el-col
+:span="12" class="center text-18px"
                             style="border: none;padding: 8px;margin-left: 5px; margin-top: -5px;">
                       <div style="font-weight:550">工作间</div>
                     </el-col>
-                    <el-col :span="12" class="center text-18px"
+                    <el-col
+:span="12" class="center text-18px"
                             style="border: none;padding: 8px;margin-left: -15px; margin-top: -5px;">
                       <div style="font-weight:550">达成率</div>
                     </el-col>
                   </el-row>
                 </div>
-                <vue3ScrollSeamless class="scroll-wrapss  text-color" :classOptions="list1Options"
+                <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"
+                    <el-row
+v-for="(item, i) of list1" :key="i" class="shouye"
                             style="margin-bottom: 10px ;  display: flex; justify-content: center;background: rgba(138,193,248,0.17);padding: 2px">
                       <!-- 增加行与行之间的间距 -->
                       <el-col :span="12" class="center" style="padding: 15px;"> <!-- 增加内边距 -->
@@ -267,7 +291,8 @@
                       </el-col>
                     </el-row>
                   </div>
-                  <div v-if="list1.length == 0"
+                  <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>

+ 1 - 1
src/views/system/studentSelectSupervisorRecord/index.vue

@@ -247,7 +247,7 @@ import { DICT_TYPE } from '@/utils/dict'
 import {selectionBookVO,printBookVO,selectionBookApi} from '@/api/system/studentSelectSupervisorRecord/selectionBook'
 
 /** 师生互选记录 列表 */
-defineOptions({ name: 'studentSelectSupervisorRecord' })
+defineOptions({ name: 'StudentSelectSupervisorRecord' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化

+ 23 - 15
src/views/system/studentSelectSupervisorRecord/record.vue

@@ -210,6 +210,8 @@ import {DICT_TYPE} from "@/utils/dict";
 import studentSelectSupervisorRecordForm from './studentSelectSupervisorRecordForm.vue'
 import {studentSelectionProjectApi} from "@/api/system/studentSelectionProject"
 import { exportDocx } from '@/utils/doc.js';
+import { selectionBookApi } from '@/api/system/studentSelectSupervisorRecord/selectionBook';
+
 // import docxtemplater from "docxtemplater"
 // import PizZip from "pizzip"
 // import JSZipUtils from "jszip-utils"
@@ -298,22 +300,27 @@ const exportWordTemplate = async () => {
   if (selectedRows.value.length === 0) {
     message.error('请先选择需要导出的数据!');
     return;
-   }
-
-  const dataList = selectedRows.value.map((row) => ({
-    studentName: row.studentName,
-    userNumber: row.userNumber,
-    major: row.major,
-    mobile: row.mobile,
-    nickname: row.nickname,
-    title: row.title,
-    studentAchievementRequirement: row.studentAchievementRequirement,
-    studentSignDate: row.studentSignDate,
-    supervisorSignDate: row.supervisorSignDate,
-    studentSignature: row.studentSignature,
-    supervisorSignature: row.supervisorSignature
+  }
+  const dataList = await Promise.all(selectedRows.value.map(async (row) => {
+    // 获取每一行的详细信息
+    const selectionBook = await selectionBookApi.getSelectionBook(row.id);
+    console.log(selectionBook);
+    return {
+      studentName: selectionBook.studentName,
+      userNumber: selectionBook.studentNumber,
+      major: selectionBook.major,
+      mobile: selectionBook.studentMobile,
+      nickname: selectionBook.supervisor,
+      // title: selectionBook.title,
+      title: selectionBook.supervisor,
+      studentAchievementRequirement: selectionBook.studentAchievementRequirement,
+      studentSignDate: new Date(selectionBook.studentSignDate).toLocaleDateString(),
+      supervisorSignDate: new Date(selectionBook.supervisorSignDate).toLocaleDateString(),
+      // studentSignature: selectionBook.studentSignature,
+      supervisorSignature: selectionBook.supervisorSignature,
+      studentSignature: "/logo.png"
+    };
   }));
-
   console.log("导出的数据对象:", dataList);
     
   const zipFileName = '师生互选表.zip';
@@ -323,6 +330,7 @@ const exportWordTemplate = async () => {
     console.error('导出文档失败:', error);
   }
 };
+
 // const exportWordTemplate = async () => {
 //   if (selectedRows.value.length === 0) {
 //     message.error('请先选择需要导出的数据!');

+ 1 - 1
src/views/system/studentSelectSupervisorRecord/studentSelectSupervisorRecordForm.vue

@@ -158,7 +158,7 @@ import { htmlPdf } from "@/utils/htmlToPDF.js"
 
 
 /** 师生互选记录 表单 */
-defineOptions({ name: 'studentSelectSupervisorRecordForm' })
+defineOptions({ name: 'StudentSelectSupervisorRecordForm' })
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗

+ 1 - 1
src/views/system/studentSelectionProject/index.vue

@@ -187,7 +187,7 @@ import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
 import { getPopData, updateIsPop,PopDo } from '@/api/system/user/pop'
 
 /** 师生互选项目 列表 */
-defineOptions({ name: 'studentSelectionProject' })
+defineOptions({ name: 'StudentSelectionProject' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化

+ 1 - 1
src/views/system/studentSelectionProject/studentSelectionProjectForm.vue

@@ -69,7 +69,7 @@ import { studentSelectionProjectApi, studentSelectionProjectVO } from '@/api/sys
 import * as UserApi from '@/api/system/user'
 
 /** 师生互选项目 表单 */
-defineOptions({ name: 'studentSelectionProjectForm' })
+defineOptions({ name: 'StudentSelectionProjectForm' })
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗

+ 3 - 2
src/views/system/supervisorSelectionSetting/index.vue

@@ -128,7 +128,8 @@
 
       <el-table-column label="导师详情" align="center" min-width="120px"  v-if="userInfo?.userType === '4' || userInfo?.userType === '1'">
         <template #default="scope">
-          <el-button link
+          <el-button
+link
                       type="primary"
                       @click="openTeacherRequireForm( scope.row.supervisorId)"
                       v-hasPermi="['system:user:query']"
@@ -281,7 +282,7 @@ import {
 import {DICT_TYPE} from "@/utils/dict";
 
 /** 导师学硕专硕名额设置 列表 */
-defineOptions({ name: 'supervisorSelectionSetting' })
+defineOptions({ name: 'SupervisorSelectionSetting' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化

+ 1 - 1
src/views/system/supervisorSelectionSetting/supervisorSelectionSettingForm.vue

@@ -30,7 +30,7 @@
 import { supervisorSelectionSettingApi, supervisorSelectionSettingVO } from '@/api/system/supervisorSelectionSetting'
 
 /** 导师学硕专硕名额设置 表单 */
-defineOptions({ name: 'supervisorSelectionSettingForm' })
+defineOptions({ name: 'SupervisorSelectionSettingForm' })
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗

+ 1 - 1
src/views/system/userDetail/student.vue

@@ -61,7 +61,7 @@ import {ref} from "vue";
 
 import {selectionBookVO,selectionBookApi} from '@/api/system/studentSelectSupervisorRecord/selectionBook'
 
-defineOptions({ name: 'studentForm' })
+defineOptions({ name: 'StudentForm' })
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗

+ 1 - 1
src/views/system/userDetail/teacher.vue

@@ -95,7 +95,7 @@ import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
 import {selectionBookVO,selectionBookApi} from '@/api/system/studentSelectSupervisorRecord/selectionBook'
 import download from '@/utils/download'
 
-defineOptions({ name: 'teacherRequireForm' })
+defineOptions({ name: 'TeacherRequireForm' })
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗

Some files were not shown because too many files changed in this diff