Przeglądaj źródła

feat(system): 添加参赛队员导入功能并优化相关页面

- 在 gameAthlete 页面添加导入按钮和相关功能
- 优化 gameEvent、gameEventGroup、gameEventProject 和 gameTeam 页面的样式和布局
- 移除不必要的注释代码
- 统一表单验证规则的格式
zhou 2 tygodni temu
rodzic
commit
2ddae79dd4

+ 91 - 8
src/views/system/gameAthlete/index.vue

@@ -50,6 +50,9 @@
           <el-col :span="1.5">
             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameAthlete:export']">导出 </el-button>
           </el-col>
+          <el-col :span="1.5">
+            <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:gameAthlete:import']"> 导入 </el-button>
+          </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
       </template>
@@ -119,11 +122,7 @@
         </el-form-item>
         <el-form-item label="性别" prop="gender">
           <el-radio-group v-model="form.gender">
-            <el-radio 
-            v-for="dict in sys_user_sex"
-            :key="dict.value"
-            :value="dict.value"
-            >{{dict.label}}</el-radio>
+            <el-radio v-for="dict in sys_user_sex" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="年龄" prop="age">
@@ -150,6 +149,42 @@
         </div>
       </template>
     </el-dialog>
+    <!-- 用户导入对话框 -->
+    <el-dialog v-model="upload.open" :title="upload.title" width="400px" append-to-body>
+      <el-upload
+        ref="uploadRef"
+        :limit="1"
+        accept=".xlsx, .xls"
+        :headers="upload.headers"
+        :action="upload.url + '?updateSupport=' + upload.updateSupport"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <el-icon class="el-icon--upload">
+          <i-ep-upload-filled />
+        </el-icon>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <template #tip>
+          <div class="text-center el-upload__tip">
+            <!--            <div class="el-upload__tip">-->
+            <!--              <el-checkbox v-model="upload.updateSupport" />-->
+            <!--              是否更新已经存在的用户数据-->
+            <!--            </div>-->
+            <span>仅允许导入xls、xlsx格式文件。</span>
+            <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
+          </div>
+        </template>
+      </el-upload>
+      <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>
 
@@ -162,13 +197,12 @@ import { getDefaultEvent } from '@/api/system/gameEvent';
 import { GameAthleteVO, GameAthleteQuery, GameAthleteForm } from '@/api/system/gameAthlete/types';
 import { GameTeamVO } from '@/api/system/gameTeam/types';
 import { GameEventVO } from '@/api/system/gameEvent/types';
-import { GameEventProjectVO } from '@/api/system/gameEventProject/types';
+import { globalHeaders } from '@/utils/request';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex'));
 const defaultEvent = ref<GameEventVO | null>(null); // 默认赛事信息
 
-
 const gameTeamList = ref<GameTeamVO[]>([]); // 队伍列表
 const gameAthleteList = ref<GameAthleteVO[]>([]);
 const gameEventProjectList = ref<Array<{ key: string; label: string }>>([]); // 赛事项目列表(用于穿梭框)
@@ -182,12 +216,29 @@ const total = ref(0);
 
 const queryFormRef = ref<ElFormInstance>();
 const gameAthleteFormRef = ref<ElFormInstance>();
+const uploadRef = ref<ElUploadInstance>();
 
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
 });
 
+/*** 用户导入参数 */
+const upload = reactive<ImportOption>({
+  // 是否显示弹出层(用户导入)
+  open: false,
+  // 弹出层标题(用户导入)
+  title: '',
+  // 是否禁用上传
+  isUploading: false,
+  // 是否更新已经存在的用户数据
+  updateSupport: 0,
+  // 设置上传的请求头部
+  headers: globalHeaders(),
+  // 上传的地址
+  url: import.meta.env.VITE_APP_BASE_API + '/system/gameAthlete/import'
+});
+
 const initFormData: GameAthleteForm = {
   athleteId: undefined,
   userId: undefined,
@@ -301,7 +352,7 @@ const getList = async () => {
     loading.value = false;
     return;
   }
-  
+
   loading.value = true;
   const res = await listGameAthlete(queryParams.value);
   gameAthleteList.value = res.rows;
@@ -437,6 +488,38 @@ const handleExport = () => {
   );
 };
 
+/** 导入按钮操作 */
+const handleImport = () => {
+  upload.title = '参赛队员导入';
+  upload.open = true;
+};
+
+/**文件上传中处理 */
+const handleFileUploadProgress = () => {
+  upload.isUploading = true;
+};
+
+/** 文件上传成功处理 */
+const handleFileSuccess = (response: any, file: UploadFile) => {
+  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
+  });
+  getList();
+};
+
+/** 提交上传文件 */
+function submitFileForm() {
+  uploadRef.value?.submit();
+}
+
+/** 下载模板操作 */
+const importTemplate = () => {
+  proxy?.download('system/gameAthlete/importTemplate', {}, `game_athlete_template_${new Date().getTime()}.xlsx`);
+};
+
 onMounted(() => {
   getDefaultEventInfo().then(() => {
     getList();

+ 3 - 3
src/views/system/gameEvent/detail.vue

@@ -42,7 +42,7 @@
         <!-- <el-row :gutter="20" class="mt-4">
           <el-col :span="24">
             <el-descriptions :column="1" border>
-              <el-descriptions-item label="备注">{{ eventData.remark }}</el-descriptions-item>
+              <el-descriptions-item label="备注" label-width="10%">{{ eventData.remark }}</el-descriptions-item>
             </el-descriptions>
           </el-col>
         </el-row> -->
@@ -153,8 +153,8 @@ const loadProjectData = async (eventId: string) => {
       eventId: eventId,
       pageNum: 1,
       pageSize: 1000,
-      orderByColumn: '',
-      isAsc: ''
+      orderByColumn: undefined,
+      isAsc: undefined
     });
     projectData.value = res.rows;
   } catch (error) {

+ 6 - 10
src/views/system/gameEvent/index.vue

@@ -99,7 +99,7 @@
         <el-table-column label="举办单位" align="center" prop="unit" />
         <el-table-column label="是否默认赛事" align="center" prop="isDefault">
           <template #default="scope">
-             <el-switch v-model="scope.row.isDefault" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+            <el-switch v-model="scope.row.isDefault" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
           </template>
         </el-table-column>
         <el-table-column label="创建时间" align="center" prop="createTime" width="180">
@@ -199,6 +199,7 @@ import { ref } from 'vue';
 import RefereeForm from '@/views/system/gameEvent/RefereeForm.vue';
 import RankingBoard from './RankingBoard.vue';
 import { useTagsViewStore } from '@/store/modules/tagsView';
+import { useTagsViewStore } from '@/store/modules/tagsView';
 
 const router = useRouter();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -264,15 +265,9 @@ const data = reactive<PageData<GameEventForm, GameEventQuery>>({
     isAsc: ''
   },
   rules: {
-    eventCode: [
-      { required: true, message: "赛事编号不能为空", trigger: "blur" }
-    ],
-    eventName: [
-      { required: true, message: "赛事名称不能为空", trigger: "blur" }
-    ],
-    eventType: [
-      { required: true, message: "赛事类型不能为空", trigger: "change" }
-    ],
+    eventCode: [{ required: true, message: '赛事编号不能为空', trigger: 'blur' }],
+    eventName: [{ required: true, message: '赛事名称不能为空', trigger: 'blur' }],
+    eventType: [{ required: true, message: '赛事类型不能为空', trigger: 'change' }]
   }
 });
 
@@ -478,6 +473,7 @@ const handleStatusChange = async (row: GameEventVO) => {
     await proxy?.$modal.confirm('确认要"' + text + '""' + row.eventName + '"为默认赛事吗?');
     await changeEventDefault(row.eventId, row.isDefault);
     await getList();
+    // localStorage.setItem('defaultEventId', row.eventId);
     proxy?.$modal.msgSuccess(text + '成功');
     // 刷新当前标签页
     await useTagsViewStore().delOthersViews(router.currentRoute.value)

+ 1 - 13
src/views/system/gameEventGroup/index.vue

@@ -174,20 +174,8 @@ const data = reactive<PageData<GameEventGroupForm, GameEventGroupQuery>>({
   }
 });
 
-// 添加额外的ref用于处理默认事件ID
-const defaultEventId = computed(() => defaultEvent.value?.eventId);
-
-// 监听默认事件变化
-watchEffect(() => {
-  if (defaultEventId.value) {
-    form.value.eventId = defaultEventId.value;
-    queryParams.value.eventId = defaultEventId.value;
-  }
-});
-
 const { queryParams, form, rules } = toRefs(data);
 
-
 /** 获取默认赛事 */
 const getDefaultEventInfo = async () => {
   try {
@@ -205,7 +193,7 @@ const getList = async () => {
     loading.value = false;
     return;
   }
-  
+
   loading.value = true;
   const res = await listGameEventGroup(queryParams.value);
   gameEventGroupList.value = res.rows;

+ 9 - 18
src/views/system/gameEventProject/index.vue

@@ -251,26 +251,16 @@ const data = reactive<PageData<GameEventProjectForm, GameEventProjectQuery>>({
     // }
   },
   rules: {
-    eventId: [
-      { required: true, message: "赛事ID不能为空", trigger: "blur" }
-    ],
-    projectName: [
-      { required: true, message: "项目名称不能为空", trigger: "blur" }
-    ],
-    projectType: [
-      { required: true, message: "项目类型不能为空", trigger: "change" }
-    ],
-    groupType: [
-      { required: true, message: "项目组别不能为空", trigger: "change" }
-    ],
-    location: [
-      { required: true, message: "比赛场地不能为空", trigger: "blur" }
-    ],
+    eventId: [{ required: true, message: '赛事ID不能为空', trigger: 'blur' }],
+    projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
+    projectType: [{ required: true, message: '项目类型不能为空', trigger: 'change' }],
+    groupType: [{ required: true, message: '项目组别不能为空', trigger: 'change' }],
+    location: [{ required: true, message: '比赛场地不能为空', trigger: 'blur' }]
   }
 });
 
 const { queryParams, form, rules } = toRefs(data);
-  
+
 // 添加额外的ref用于处理默认事件ID
 const defaultEventId = computed(() => defaultEvent.value?.eventId);
 
@@ -281,6 +271,7 @@ watchEffect(() => {
     queryParams.value.eventId = defaultEventId.value;
   }
 });
+
 /** 获取默认赛事 */
 const getDefaultEventInfo = async () => {
   try {
@@ -298,7 +289,7 @@ const getList = async () => {
     loading.value = false;
     return;
   }
-    
+
   loading.value = true;
   const res = await listGameEventProject(queryParams.value);
   gameEventProjectList.value = res.rows;
@@ -323,7 +314,7 @@ const cancel = () => {
 
 /** 表单重置 */
 const reset = () => {
-  form.value = {...initFormData, eventId: defaultEvent.value?.eventId};
+  form.value = { ...initFormData, eventId: defaultEvent.value?.eventId };
   gameEventProjectFormRef.value?.resetFields();
 };
 

+ 9 - 16
src/views/system/gameTeam/index.vue

@@ -52,7 +52,6 @@
         <!--        <el-table-column label="赛事ID" align="center" prop="eventId" />-->
         <el-table-column label="赛事名称" align="center" prop="eventName" />
         <el-table-column label="队伍名称" align="center" prop="teamName" />
-        <el-table-column label="队伍编号" align="center" prop="teamCode" />
         <el-table-column label="团队描述" align="center" prop="teamDescribe" />
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
           <template #default="scope">
@@ -114,10 +113,10 @@
         <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
         <template #tip>
           <div class="text-center el-upload__tip">
-            <div class="el-upload__tip">
-              <el-checkbox v-model="upload.updateSupport" />
-              是否更新已经存在的用户数据
-            </div>
+            <!--            <div class="el-upload__tip">-->
+            <!--              <el-checkbox v-model="upload.updateSupport" />-->
+            <!--              是否更新已经存在的用户数据-->
+            <!--            </div>-->
             <span>仅允许导入xls、xlsx格式文件。</span>
             <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
           </div>
@@ -196,7 +195,7 @@ const data = reactive<PageData<GameTeamForm, GameTeamQuery>>({
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    eventId: undefined,
+    eventId: undefined
     // teamName: undefined,
     // teamCode: undefined,
     // leader: undefined,
@@ -210,15 +209,9 @@ const data = reactive<PageData<GameTeamForm, GameTeamQuery>>({
     // }
   },
   rules: {
-    teamName: [
-      { required: true, message: "队伍名称不能为空", trigger: "blur" }
-    ],
-    teamCode: [
-      { required: true, message: "队伍编号不能为空", trigger: "blur" }
-    ],
-    teamDescribe: [
-      { required: true, message: "团队描述不能为空", trigger: "blur" }
-    ],
+    teamName: [{ required: true, message: '队伍名称不能为空', trigger: 'blur' }],
+    teamCode: [{ required: true, message: '队伍编号不能为空', trigger: 'blur' }],
+    teamDescribe: [{ required: true, message: '团队描述不能为空', trigger: 'blur' }]
   }
 });
 
@@ -280,7 +273,7 @@ const cancel = () => {
 
 /** 表单重置 */
 const reset = () => {
-  form.value = {...initFormData, eventId: defaultEvent.value?.eventId};
+  form.value = { ...initFormData, eventId: defaultEvent.value?.eventId };
   gameTeamFormRef.value?.resetFields();
 };