Forráskód Böngészése

feat(gameScore): 添加成绩导入功能

- 新增importScore API用于导入成绩数据
- 新增importTemplate API用于下载导入模板
- 在成绩编辑页面添加导入按钮和导入对话框组件
- 实现文件上传拖拽功能,支持xlsx/xls格式
- 添加导入模板下载链接和覆盖选项
- 实现上传进度提示和成功处理逻辑
zhou 2 hete
szülő
commit
f3a50f475d

+ 30 - 0
src/api/system/gameScore/index.ts

@@ -219,3 +219,33 @@ export const exportProjectScore = (eventId: string | number, projectId: string |
     responseType: 'blob'
   });
 };
+
+/**
+ * 导入成绩
+ * @param data 导入数据
+ */
+export const importScore = (data: any) => {
+  return request({
+    url: '/system/gameScore/import',
+    method: 'post',
+    data: data,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+};
+
+/**
+ * 下载导入模板
+ * @param eventId 赛事ID
+ * @param projectId 项目ID
+ * @param classification 项目分类
+ */
+export const importTemplate = (eventId: string | number, projectId: string | number, classification: string) => {
+  return request({
+    url: '/system/gameScore/importTemplate',
+    method: 'post',
+    params: { eventId, projectId, classification },
+    responseType: 'blob'
+  });
+};

+ 114 - 1
src/views/system/gameScore/gameScoreEdit.vue

@@ -24,6 +24,9 @@
       <el-button type="warning" @click="handleExport">
         <el-icon><Download /></el-icon> 导出
       </el-button>
+      <el-button type="info" plain @click="handleImport">
+        <el-icon><Upload /></el-icon> 导入
+      </el-button>
       <el-input 
         v-model="searchValue" 
         :placeholder="projectClassification === '0' ? '输入运动员姓名搜索' : '输入队伍名称搜索'" 
@@ -229,13 +232,49 @@
         </div>
       </template>
     </el-dialog>
+
+    <!-- 成绩导入对话框 -->
+    <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+      <el-upload
+        ref="uploadRef"
+        :limit="1"
+        accept=".xlsx, .xls"
+        :headers="upload.headers"
+        :action="upload.url"
+        :data="upload.data"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <template #tip>
+          <div class="text-center el-upload__tip">
+            <span>仅允许导入xls、xlsx格式文件。</span>
+            <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplateFile">下载模板 </el-link>
+          </div>
+        </template>
+      </el-upload>
+      <div style="margin-top: 10px; text-align: center;">
+        <el-checkbox v-model="upload.updateSupport" :true-label="1" :false-label="0"> 存在是否覆盖成绩</el-checkbox>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">确 定</el-button>
+          <el-button @click="upload.open = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup name="GameScoreEdit" lang="ts">
 import { onMounted, onUnmounted, ref, reactive, getCurrentInstance, toRefs } from 'vue';
 import { useRoute } from 'vue-router';
-import { getProjectScoreData, updateScoreAndRecalculate, exportProjectScore } from '@/api/system/gameScore/index';
+import { getProjectScoreData, updateScoreAndRecalculate, exportProjectScore, importTemplate } from '@/api/system/gameScore/index';
+import { globalHeaders } from '@/utils/request';
 import { getGameEventProject } from '@/api/system/gameEventProject';
 import { listScoreDetail } from '@/api/system/scoreDetail';
 import { GameScoreForm } from '@/api/system/gameScore/types';
@@ -264,6 +303,26 @@ const exportDialog = reactive({
   topN: undefined as number | undefined
 });
 
+/*** 成绩导入参数 */
+const upload = reactive<ImportOption>({
+  // 是否显示弹出层(成绩导入)
+  open: false,
+  // 弹出层标题(成绩导入)
+  title: '',
+  // 是否禁用上传
+  isUploading: false,
+  // 设置上传的请求头部
+  headers: globalHeaders(),
+  // 上传的地址
+  url: import.meta.env.VITE_APP_BASE_API + '/system/gameScore/import',
+  eventId: '',
+  projectId: '',
+  classification: '',
+  updateSupport: 1,
+  data: {}
+});
+const uploadRef = ref<ElUploadInstance>();
+
 // 统计数据
 const stats = reactive({
   registrationCount: 0,
@@ -572,6 +631,60 @@ const doExport = async () => {
   }
 };
 
+/** 导入按钮操作 */
+const handleImport = () => {
+  upload.title = '成绩导入';
+  upload.eventId = eventId;
+  upload.projectId = projectId;
+  upload.classification = projectClassification;
+  upload.updateSupport = 1;
+  upload.data = {
+    eventId: eventId,
+    projectId: projectId,
+    classification: projectClassification,
+    updateSupport: upload.updateSupport
+  };
+  upload.open = true;
+};
+
+/** 下载模板操作 */
+const importTemplateFile = async () => {
+  try {
+    const response = await importTemplate(eventId, projectId, projectClassification);
+    const fileName = `成绩导入模板_${projectName}.xlsx`;
+    const blob = new Blob([response as any]);
+    const link = document.createElement('a');
+    link.href = window.URL.createObjectURL(blob);
+    link.download = fileName;
+    link.click();
+    window.URL.revokeObjectURL(link.href);
+  } catch (error) {
+    proxy?.$modal.msgError("下载模板失败");
+  }
+};
+
+/**文件上传中处理 */
+const handleFileUploadProgress = () => {
+  upload.isUploading = true;
+};
+
+/** 文件上传成功处理 */
+const handleFileSuccess = (response: any, file: any) => {
+  upload.open = false;
+  upload.isUploading = false;
+  uploadRef.value?.handleRemove(file);
+  ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", {
+    dangerouslyUseHTMLString: true
+  });
+  loadData(false);
+};
+
+/** 提交上传文件 */
+function submitFileForm() {
+  upload.data.updateSupport = upload.updateSupport;
+  uploadRef.value?.submit();
+}
+
 // 加载数据方法,支持搜索
 const loadData = async (autoCalculateRanking = false) => {
   loading.value = true;