Prechádzať zdrojové kódy

Merge branch 'dev_zlt' into dev

zhou 1 mesiac pred
rodič
commit
3984be7948

+ 1 - 1
.env.production

@@ -14,7 +14,7 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications'
 VITE_APP_SNAILJOB_ADMIN = '/snail-job'
 
 # 生产环境
-VITE_APP_BASE_API = '/prod-api'
+VITE_APP_BASE_API = 'http://meet2.sportsrobo.club:8080'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip

+ 3 - 3
package.json

@@ -22,7 +22,7 @@
   "dependencies": {
     "@element-plus/icons-vue": "2.3.1",
     "@highlightjs/vue-plugin": "2.1.0",
-    "@types/qrcode": "^1.5.5",
+    
     "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "13.1.0",
     "animate.css": "4.1.1",
@@ -38,14 +38,14 @@
     "jsencrypt": "3.3.2",
     "nprogress": "0.2.0",
     "pinia": "3.0.2",
-    "qrcode": "^1.5.4",
+    
     "screenfull": "6.0.2",
     "vue": "3.5.13",
     "vue-cropper": "1.1.1",
     "vue-cropperjs": "^5.0.0",
     "vue-i18n": "11.1.3",
     "vue-json-pretty": "2.4.0",
-    "vue-qr": "^4.0.9",
+    
     "vue-router": "4.5.0",
     "vue-types": "6.0.0",
     "vxe-table": "4.13.7"

+ 3 - 3
src/api/system/gameAthlete/types.ts

@@ -73,7 +73,7 @@ export interface GameAthleteVO {
    * 参与项目列表
    */
   projectValue: string;
-  projectList: String[];
+  projectList: number[];
 
   /**
    * 状态(0正常 1停用)
@@ -166,12 +166,12 @@ export interface GameAthleteForm extends BaseEntity {
    * 参与项目列表
    */
   projectValue?: string;
-  projectList: String[];
+  projectList: number[];
 
   /**
    * 选中的项目列表(用于穿梭框)
    */
-  selectedProjects?: string[];
+  selectedProjects?: number[];
 
   /**
    * 状态(0正常 1停用)

+ 1 - 1
src/api/system/gameEventGroup/types.ts

@@ -111,7 +111,7 @@ export interface GameEventGroupForm extends BaseEntity {
   /**
    * 选中的项目列表(用于穿梭框)
    */
-  selectedProjects?: string[];
+  selectedProjects?: number[];
 
   /**
    * 成员性别(0不限1男2女)

+ 2 - 2
src/api/system/gameEventProject/types.ts

@@ -26,7 +26,7 @@ export interface GameEventProjectVO {
   /**
    * 裁判组员
    */
-  refereeGroups: String[];
+  refereeGroups: number[];
   /**
    * 比赛场地
    */
@@ -132,7 +132,7 @@ export interface GameEventProjectForm extends BaseEntity {
   /**
    * 裁判组员
    */
-  refereeGroups?: String[];
+  refereeGroups?: number[];
 
   /**
    * 比赛场地

+ 13 - 1
src/api/system/gameReferee/index.ts

@@ -1,4 +1,4 @@
-import request from '@/utils/request';
+import request from '@/utils/request';
 import { AxiosPromise } from 'axios';
 import { GameRefereeVO, GameRefereeForm, GameRefereeQuery } from '@/api/system/gameReferee/types';
 
@@ -72,3 +72,15 @@ export const getRefereeCount = () => {
     method: 'get'
   });
 };
+
+/**
+ * 生成裁判二维码
+ * @param refereeId 裁判ID
+ * @returns {*}
+ */
+export const generateRefereeQRCode = (refereeId: string | number): AxiosPromise<string> => {
+  return request({
+    url: '/system/gameReferee/qrcode/' + refereeId,
+    method: 'get'
+  });
+};

+ 2 - 2
src/api/system/gameReferee/types.ts

@@ -33,7 +33,7 @@ export interface GameRefereeVO {
    * 负责的项目
    */
   projectList: string;
-  projectList2: string[];
+  projectList2: number[];
 
   /**
    * 裁判码
@@ -87,7 +87,7 @@ export interface GameRefereeForm extends BaseEntity {
    * 负责的项目
    */
   projectList?: string;
-  projectList2?: string[];
+  projectList2?: number[];
 
   /**
    * 裁判码

+ 2 - 1
src/components/Editor/index.vue

@@ -32,6 +32,7 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css';
 import { QuillEditor, Quill } from '@vueup/vue-quill';
 import { propTypes } from '@/utils/propTypes';
 import { globalHeaders } from '@/utils/request';
+import { getUploadUrl } from '@/config/api';
 
 defineEmits(['update:modelValue']);
 
@@ -54,7 +55,7 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const upload = reactive<UploadOption>({
   headers: globalHeaders(),
-  url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
+  url: getUploadUrl('oss')
 });
 const quillEditorRef = ref();
 const uploadRef = ref<HTMLDivElement>();

+ 12 - 2
src/components/FileUpload/index.vue

@@ -48,6 +48,7 @@
 import { propTypes } from '@/utils/propTypes';
 import { delOss, listByIds } from '@/api/system/oss';
 import { globalHeaders } from '@/utils/request';
+import { getUploadUrl } from '@/config/api';
 
 const props = defineProps({
   modelValue: {
@@ -72,7 +73,7 @@ const number = ref(0);
 const uploadList = ref<any[]>([]);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadFileUrl = ref(baseUrl + '/resource/oss/upload'); // 上传文件服务器地址
+const uploadFileUrl = ref(getUploadUrl('oss')); // 上传文件服务器地址
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);
@@ -92,7 +93,16 @@ watch(
       let list: any[] = [];
       if (Array.isArray(val)) {
         list = val;
-      } else {
+      } else if (typeof val === 'string') {
+        const res = await listByIds(val);
+        list = res.data.map((oss) => {
+          return {
+            name: oss.originalName,
+            url: oss.url,
+            ossId: oss.ossId
+          };
+        });
+      } else if (typeof val === 'number') {
         const res = await listByIds(val);
         list = res.data.map((oss) => {
           return {

+ 2 - 1
src/components/ImageOrUrlInput/index.vue

@@ -61,6 +61,7 @@ import { listByIds, delOss } from '@/api/system/oss';
 import { OssVO } from '@/api/system/oss/types';
 import { propTypes } from '@/utils/propTypes';
 import { globalHeaders } from '@/utils/request';
+import { getUploadUrl } from '@/config/api';
 import { compressAccurately } from 'image-conversion';
 import { Plus } from '@element-plus/icons-vue';
 
@@ -99,7 +100,7 @@ const urlValue = ref('');
 const isValidUrl = ref(false);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadImgUrl = ref(baseUrl + '/resource/oss/upload');
+const uploadImgUrl = ref(getUploadUrl('oss'));
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);

+ 2 - 1
src/components/ImageUpload/index.vue

@@ -45,6 +45,7 @@ import { listByIds, delOss } from '@/api/system/oss';
 import { OssVO } from '@/api/system/oss/types';
 import { propTypes } from '@/utils/propTypes';
 import { globalHeaders } from '@/utils/request';
+import { getUploadUrl } from '@/config/api';
 import { compressAccurately } from 'image-conversion';
 
 const props = defineProps({
@@ -80,7 +81,7 @@ const dialogImageUrl = ref('');
 const dialogVisible = ref(false);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 上传的图片服务器地址
+const uploadImgUrl = ref(getUploadUrl('oss')); // 上传的图片服务器地址
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);

+ 2 - 1
src/components/ImageUploadCropper/index.vue

@@ -138,6 +138,7 @@ import { listByIds, delOss } from '@/api/system/oss';
 import { OssVO } from '@/api/system/oss/types';
 import { propTypes } from '@/utils/propTypes';
 import { globalHeaders } from '@/utils/request';
+import { getUploadUrl } from '@/config/api';
 import { compressAccurately } from 'image-conversion';
 import { UploadRawFile } from 'element-plus';
 
@@ -200,7 +201,7 @@ const dialogVisible = ref(false);
 const cropperVisible = ref(false);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 上传的图片服务器地址
+const uploadImgUrl = ref(getUploadUrl('oss')); // 上传的图片服务器地址
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);

+ 127 - 0
src/config/api.ts

@@ -0,0 +1,127 @@
+/**
+ * API配置管理
+ * 统一管理所有API相关的URL配置,避免在多个地方重复修改
+ */
+
+// 环境配置
+const isDev = import.meta.env.DEV;
+const isProd = import.meta.env.PROD;
+
+// 基础API地址配置
+const API_CONFIG = {
+  // 开发环境
+  development: import.meta.env.VITE_APP_BASE_API,
+  // 生产环境
+  production: 'http://meet2.sportsrobo.club:8080',
+  // 测试环境
+  test: 'http://192.168.1.126:8080',
+  // 本地环境
+  local: 'http://localhost:8080'
+};
+
+// 获取当前环境的API基础地址
+export const getBaseURL = (): string => {
+  // 优先使用环境变量中的配置
+  if (import.meta.env.VITE_APP_BASE_API) {
+    return import.meta.env.VITE_APP_BASE_API;
+  }
+  
+  // 根据环境自动选择
+  if (isDev) {
+    return API_CONFIG.development;
+  } else if (isProd) {
+    return API_CONFIG.production;
+  }
+  
+  // 默认返回开发环境配置
+  return API_CONFIG.development;
+};
+
+// 导出基础URL
+export const BASE_URL = getBaseURL();
+
+// 常用API端点
+export const API_ENDPOINTS = {
+  // 文件上传相关
+  UPLOAD: '/resource/oss/upload',
+  UPLOAD_COMMON: '/common/upload',
+  DOWNLOAD: '/resource/oss/download',
+  
+  // 系统相关
+  USER_IMPORT: '/system/user/importData',
+  GAME_TEAM_IMPORT: '/system/gameTeam/import',
+  GAME_ATHLETE_IMPORT: '/system/gameAthlete/import',
+  ENROLL_IMPORT: '/system/enroll/importData',
+  
+  // WebSocket和SSE
+  WEBSOCKET: '/resource/websocket',
+  SSE: '/resource/sse',
+  
+  // 工作流
+  WORKFLOW_UI: '/warm-flow-ui/index.html'
+} as const;
+
+// 获取完整的API URL
+export const getApiUrl = (endpoint: string): string => {
+  return `${BASE_URL}${endpoint}`;
+};
+
+// 获取上传URL
+export const getUploadUrl = (type: 'oss' | 'common' = 'oss'): string => {
+  const endpoint = type === 'oss' ? API_ENDPOINTS.UPLOAD : API_ENDPOINTS.UPLOAD_COMMON;
+  return getApiUrl(endpoint);
+};
+
+// 获取下载URL
+export const getDownloadUrl = (filePath: string): string => {
+  return getApiUrl(`${API_ENDPOINTS.DOWNLOAD}/${filePath}`);
+};
+
+// 获取文件完整URL
+export const getFileUrl = (filePath: string): string => {
+  if (filePath.startsWith('http')) {
+    return filePath;
+  }
+  return `${BASE_URL}${filePath}`;
+};
+
+// 获取工作流URL
+export const getWorkflowUrl = (id: string, disabled: boolean = false, type: string = 'FlowChart'): string => {
+  const params = new URLSearchParams({
+    id,
+    disabled: disabled.toString(),
+    type,
+    t: Date.now().toString()
+  });
+  return getApiUrl(`${API_ENDPOINTS.WORKFLOW_UI}?${params.toString()}`);
+};
+
+// 导出配置对象,方便调试
+export const API_CONFIG_DEBUG = {
+  currentEnv: isDev ? 'development' : 'production',
+  baseURL: BASE_URL,
+  endpoints: API_ENDPOINTS,
+  isDev,
+  isProd,
+  envVars: {
+    VITE_APP_BASE_API: import.meta.env.VITE_APP_BASE_API,
+    VITE_APP_CLIENT_ID: import.meta.env.VITE_APP_CLIENT_ID
+  }
+};
+
+// 在开发环境下打印环境信息
+if (isDev) {
+  console.log(' API配置信息:', API_CONFIG_DEBUG);
+  console.log(' 当前使用的API地址:', BASE_URL);
+}
+
+// 手动切换环境的函数(仅用于调试)
+export const switchEnvironment = (env: 'development' | 'production' | 'test' | 'local') => {
+  if (isDev) {
+    console.warn(' 手动切换环境仅用于调试,实际部署时请使用环境变量');
+    const newBaseURL = API_CONFIG[env];
+    console.log(` 切换到 ${env} 环境:`, newBaseURL);
+    return newBaseURL;
+  }
+  return BASE_URL;
+};

+ 2 - 1
src/utils/request.ts

@@ -11,6 +11,7 @@ import { getLanguage } from '@/lang';
 import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from '@/utils/crypto';
 import { encrypt, decrypt } from '@/utils/jsencrypt';
 import router from '@/router';
+import { BASE_URL } from '@/config/api';
 
 const encryptHeader = 'encrypt-key';
 let downloadLoadingInstance: LoadingInstance;
@@ -27,7 +28,7 @@ axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
 axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID;
 // 创建 axios 实例
 const service = axios.create({
-  baseURL: import.meta.env.VITE_APP_BASE_API,
+  baseURL: BASE_URL,
   // baseURL: 'http://192.168.1.126:8080',
   // baseURL: 'http://meet2.sportsrobo.club:8080',
   // baseURL: 'http://localhost:8080',

+ 20 - 10
src/views/system/gameAthlete/index.vue

@@ -56,7 +56,7 @@
 
       <el-table v-loading="loading" border :data="gameAthleteList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="athleteId" v-if="columns[0].visible" />
+        <el-table-column label="运动员id" align="center" prop="athleteId" v-if="columns[0].visible" />
         <el-table-column label="赛事名称" align="center" prop="eventName" width="120px" v-if="columns[1].visible" />
         <el-table-column label="号码" align="center" prop="athleteCode" width="100px" v-if="columns[2].visible" />
         <el-table-column label="姓名" align="center" prop="name" v-if="columns[3].visible" />
@@ -121,9 +121,9 @@
         <el-form-item label="年龄" prop="age">
           <el-input v-model="form.age" placeholder="请输入年龄" />
         </el-form-item>
-        <el-form-item label="参与项目" prop="projectValue">
+        <el-form-item label="参与项目" prop="projectList">
           <el-transfer
-            v-model="form.selectedProjects"
+            v-model="projectListStr"
             :data="gameEventProjectList"
             :titles="['可选项目', '已选项目']"
             :button-texts="['移除', '添加']"
@@ -209,7 +209,7 @@ const total = ref(0);
 
 // 列显隐数据
 const columns = ref<FieldOption[]>([
-  { key: 0, label: '主键', visible: false },
+  { key: 0, label: '运动员id', visible: false },
   { key: 1, label: '赛事名称', visible: false },
   { key: 2, label: '号码', visible: true },
   { key: 3, label: '姓名', visible: true },
@@ -297,7 +297,6 @@ const data = reactive<PageData<GameAthleteForm, GameAthleteQuery>>({
     teamId: [{ required: true, message: '队伍ID不能为空', trigger: 'blur' }],
     athleteCode: [{ required: true, message: '运动员编号不能为空', trigger: 'blur' }],
     gender: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
-    age: [{ required: true, message: '年龄不能为空', trigger: 'blur' }]
   }
 });
 
@@ -325,7 +324,8 @@ const { queryParams, form, rules } = toRefs(data);
 // };
 
 /** 获取队伍名称 */
-const getTeamNameById = (teamId: string | number) => {
+const getTeamNameById = (teamId: string | number | null | undefined) => {
+  if (!teamId) return '';
   const team = gameTeamList.value.find((team) => team.teamId === teamId);
   return team ? team.teamName : '';
 };
@@ -346,7 +346,7 @@ const getProjectList = async (eventId?: string) => {
 };
 
 // 格式化项目列表显示
-const formatProjectList = (projectList: string[]) => {
+const formatProjectList = (projectList: number[]) => {
   if (!projectList) return '';
   // 将逗号分隔的ID列表转换为项目名称列表
   // const projectIds = projectValue.split(',');
@@ -354,7 +354,7 @@ const formatProjectList = (projectList: string[]) => {
   //   const project = gameEventProjectList.value.find((p) => p.key === id);
   //   return project ? project.label : id;
   // });
-  const projectNames = gameEventProjectList.value.filter((p) => projectList.includes(p.key)).map((p) => p.label);
+  const projectNames = gameEventProjectList.value.filter((p) => projectList.includes(Number(p.key))).map((p) => p.label);
   return projectNames.join(',');
 };
 
@@ -432,8 +432,10 @@ const handleUpdate = async (row?: GameAthleteVO) => {
   Object.assign(form.value, res.data);
 
   // 处理项目列表,将逗号分隔的字符串转换为数组
-  if (res.data.projectValue) {
-    form.value.selectedProjects = res.data.projectValue.split(',');
+  if (res.data.projectList) {
+    form.value.selectedProjects = res.data.projectList;
+  } else if (res.data.projectValue) {
+    form.value.selectedProjects = res.data.projectValue.split(',').map(Number);
   } else {
     form.value.selectedProjects = [];
   }
@@ -448,6 +450,14 @@ const handleUpdate = async (row?: GameAthleteVO) => {
   // });
 };
 
+// 添加一个计算属性用于处理projectList2的类型转换
+const projectListStr = computed({
+  get: () => form.value.selectedProjects.map(id => String(id)),
+  set: (value) => {
+    form.value.selectedProjects = value.map(id => Number(id));
+  }
+});
+
 /** 提交按钮 */
 const submitForm = () => {
   gameAthleteFormRef.value?.validate(async (valid: boolean) => {

+ 2 - 2
src/views/system/gameEvent/RefereeForm.vue

@@ -126,8 +126,8 @@ const submitForm = () => {
               // 直接使用 refereeGroups 字段(字符串数组)
               let currentRefereeGroups = project.refereeGroups || [];
               // 检查是否已存在该裁判ID,避免重复添加
-              if (!currentRefereeGroups.includes(String(newRefereeId))) {
-                currentRefereeGroups.push(String(newRefereeId));
+              if (!currentRefereeGroups.includes(newRefereeId)) {
+                currentRefereeGroups.push(newRefereeId);
               }
               
               // 更新项目表中的裁判组字段

+ 1 - 4
src/views/system/gameEvent/athlete.vue

@@ -93,7 +93,7 @@ const athleteForm = reactive({
   unit: '',
   age: 0,
   gender: '',
-  projectValue: [] as string[]  // 确保初始化为数组
+  projectValue: [] as number[]  // 确保初始化为数组
 });
 
 const athleteRules = {
@@ -103,9 +103,6 @@ const athleteRules = {
   athleteCode: [
     { required: true, message: '请输入号码', trigger: 'blur' }
   ],
-  age: [
-    { required: true, message: '请输入年龄', trigger: 'blur' }
-  ],
   gender: [
     { required: true, message: '请选择性别', trigger: 'change' }
   ]

+ 2 - 2
src/views/system/gameEventConfig/index.vue

@@ -50,7 +50,7 @@
 
       <el-table v-loading="loading" border :data="gameEventConfigList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="configId" v-if="columns[0].visible" />
+        <el-table-column label="配置id" align="center" prop="configId" v-if="columns[0].visible" />
         <!-- <el-table-column label="赛事ID" align="center" prop="eventId" /> -->
         <el-table-column label="配置类型" align="center" prop="configType" v-if="columns[1].visible" />
         <el-table-column label="配置描述" align="center" prop="configDesc" v-if="columns[2].visible" />
@@ -146,7 +146,7 @@ const total = ref(0);
 
 // 列显隐数据
 const columns = ref<FieldOption[]>([
-  { key: 0, label: '主键', visible: true },
+  { key: 0, label: '配置id', visible: true },
   { key: 1, label: '配置类型', visible: true },
   { key: 2, label: '配置描述', visible: true },
   { key: 3, label: '配置键', visible: true },

+ 2 - 2
src/views/system/gameEventConfigType/index.vue

@@ -51,7 +51,7 @@
 
       <el-table v-loading="loading" border :data="gameEventConfigTypeList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="typeId" v-if="columns[0].visible" />
+        <el-table-column label="类型id" align="center" prop="typeId" v-if="columns[0].visible" />
         <el-table-column label="类型编码" align="center" prop="typeCode" v-if="columns[1].visible" />
         <el-table-column label="类型名称" align="center" prop="typeName" v-if="columns[2].visible" />
         <el-table-column label="类型描述" align="center" prop="typeDesc" v-if="columns[3].visible" />
@@ -144,7 +144,7 @@ const total = ref(0);
 
 // 列显隐数据
 const columns = ref<FieldOption[]>([
-  { key: 0, label: '主键', visible: false },
+  { key: 0, label: '类型id', visible: false },
   { key: 1, label: '类型编码', visible: true },
   { key: 2, label: '类型名称', visible: true },
   { key: 3, label: '类型描述', visible: true },

+ 2 - 2
src/views/system/gameEventProject/RefereeGroupDialog.vue

@@ -44,7 +44,7 @@ interface GameRefereeVOExt extends GameRefereeVO {
   projectNameList?: string;
 }
 
-const openDialog = async (refereeGroups: string[], projectNameParam: string) => {
+const openDialog = async (refereeGroups: number[], projectNameParam: string) => {
   projectName.value = projectNameParam;
   dialog.visible = true;
   loading.value = true;
@@ -52,7 +52,7 @@ const openDialog = async (refereeGroups: string[], projectNameParam: string) =>
   try {
     if (refereeGroups) {
       // 解析裁判组ID列表
-      const refereeIds = refereeGroups.filter(id => id.trim());
+      const refereeIds = refereeGroups.filter(id => id);
       
       if (refereeIds.length > 0) {
         // 获取裁判详细信息

+ 2 - 2
src/views/system/gameEventProject/index.vue

@@ -46,7 +46,7 @@
 
       <el-table v-loading="loading" border :data="gameEventProjectList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="projectId" v-if="columns[0].visible" />
+        <el-table-column label="项目id" align="center" prop="projectId" v-if="columns[0].visible" />
         <el-table-column label="项目名称" align="center" prop="projectName" v-if="columns[1].visible" />
         <el-table-column label="项目类型" align="center" prop="projectType" v-if="columns[2].visible">
           <template #default="scope">
@@ -240,7 +240,7 @@ const dialog = reactive<DialogOption>({
 
 // 列显隐数据
 const columns = ref<FieldOption[]>([
-  { key: 0, label: '主键', visible: false },
+  { key: 0, label: '项目id', visible: false },
   { key: 1, label: '项目名称', visible: true },
   { key: 2, label: '项目类型', visible: true },
   { key: 3, label: '归类', visible: true },

+ 29 - 110
src/views/system/gameReferee/index.vue

@@ -80,7 +80,7 @@
         <el-form-item label="赛事项目" prop="projectList2">
           <!-- 赛事项目穿梭框 -->
           <el-transfer
-            v-model="form.projectList2"
+            v-model="projectList2Str"
             :data="allProjects"
             :titles="['可选项目', '已选项目']"
             :button-texts="['移除', '添加']"
@@ -114,10 +114,9 @@
 </template>
 
 <script setup name="GameReferee" lang="ts">
-import { listGameReferee, getGameReferee, delGameReferee, addGameReferee, updateGameReferee } from '@/api/system/gameReferee';
+import { listGameReferee, getGameReferee, delGameReferee, addGameReferee, updateGameReferee, generateRefereeQRCode } from '@/api/system/gameReferee';
 import { getGameEventProject, listGameEventProject, updateGameEventProject } from '@/api/system/gameEventProject';
 import { GameRefereeVO, GameRefereeQuery, GameRefereeForm } from '@/api/system/gameReferee/types';
-import { toDataURL } from 'qrcode';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
@@ -133,7 +132,7 @@ const allProjects = ref<{key: string, label: string }[]>([]); // 添加项目数
 
 // 列显隐数据
 const columns = ref<FieldOption[]>([
-  { key: 0, label: '裁判ID', visible: true },
+  { key: 0, label: '裁判ID', visible: false },
   { key: 1, label: '裁判姓名', visible: true },
   { key: 2, label: '账号', visible: true },
   { key: 3, label: '密码', visible: true },
@@ -176,7 +175,7 @@ const form = reactive({
   account: undefined,
   password: undefined,
   // projectList: [] as string[], // 存储所负责的项目
-  projectList2: [] as string[], // 存储所负责的项目
+  projectList2: [] as number[], // 存储所负责的项目
   refereeCode: undefined,
   createTime: undefined,
   updateTime: undefined,
@@ -223,7 +222,7 @@ const getList = async () => {
       
       // 从所有项目中筛选出在projectIds中存在的项目,并提取它们的label值(项目名称)
       const projectNames = projectRes.rows
-        .filter(project => projectIds.includes(String(project.projectId)))
+        .filter(project => projectIds.map(id => Number(id)).includes(Number(project.projectId)))
         .map(project => project.projectName);
       
       // 添加projectNameList属性到裁判对象中用于展示
@@ -260,6 +259,14 @@ const reset = () => {
   gameRefereeFormRef.value?.resetFields();
 }
 
+// 添加一个计算属性用于处理projectList2的类型转换
+const projectList2Str = computed({
+  get: () => form.projectList2.map(id => String(id)),
+  set: (value) => {
+    form.projectList2 = value.map(id => Number(id));
+  }
+});
+
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.pageNum = 1;
@@ -308,20 +315,18 @@ const handleUpdate = async (row?: GameRefereeVO) => {
   const _refereeId = row?.refereeId || ids.value[0]
   const res = await getGameReferee(_refereeId);
   Object.assign(form, res.data);
-  
-  // 如果是修改操作,先加载项目列表
+
+  // 等待项目列表加载完成
   await loadProjects();
-  
+
   // 处理项目列表数据格式
-  // if (res.data.projectList && typeof res.data.projectList === 'string') {
-  //   form.projectList = res.data.projectList.split(',').filter(id => id);
-  // } else 
   if (Array.isArray(res.data.projectList2)) {
-    form.projectList2 = res.data.projectList2;
+    // 将数字数组转换为字符串数组,以匹配 allProjects 的 key 类型
+    form.projectList2 = res.data.projectList2.map(id => id);
   } else {
     form.projectList2 = [];
   }
-  
+
   dialog.visible = true;
   dialog.title = "修改裁判";
 }
@@ -331,103 +336,23 @@ const submitForm = async () => {
   const valid = await gameRefereeFormRef.value?.validate();
   if (valid) {
     buttonLoading.value = true;
-    // 提交前处理项目列表数据格式
     const submitForm: any = { ...form };
-    // if (Array.isArray(form.projectList)) {
-    //   submitForm.projectList = form.projectList.join(',');
-    // }
     if (form.projectList2) {
       submitForm.projectList2 = form.projectList2;
     }
 
     try {
       if (form.refereeId) {
-        // 如果是更新操作,需要先获取原裁判信息,然后更新项目表中的裁判组
-        const originalReferee = await getGameReferee(form.refereeId);
-        const originalProjectList = originalReferee.data.projectList2 || [];
-        const newProjectList = submitForm.projectList2 || [];
-        
-        // 获取当前项目列表中的项目ID集合
-        // const currentProjectIds = new Set<string>(newProjectList.filter(id => id.trim()));
-        
-        // 获取原项目列表中的项目ID集合
-        // const originalProjectIds = new Set<string>(originalProjectList.filter(id => id.trim()));
-        
-        // 筛选出在原项目列表中但不在当前项目列表中的项目(即被删除的项目)
-        // const removedProjectIds = Array.from(currentProjectIds).filter(id => !originalProjectIds.has(id));
-        const removedProjectIds = newProjectList.filter(id => !originalProjectList.includes(id));
-        
-        // 从被删除的项目中移除该裁判
-        for (const projectId of removedProjectIds) {
-          const projectRes = await getGameEventProject(projectId);
-          
-          if (projectRes) {
-            const project = projectRes.data;
-            let currentRefereeGroups = project.refereeGroups || [];
-            
-            // 从裁判组中移除该裁判ID
-            if (currentRefereeGroups) {
-              const refereeIds = currentRefereeGroups.filter(id => id.trim() !== String(form.refereeId));
-              // 更新项目表中的裁判组字段
-              await updateGameEventProject({
-                ...project,
-                refereeGroups: refereeIds
-              });
-            }
-          }
-        }
-        
-        // 向新项目中添加该裁判
-        // const newProjectIds = Array.from<string>(currentProjectIds).filter(id => !originalProjectIds.has(
-        const newProjectIds = newProjectList.filter(id => !originalProjectList.includes(id));
-        for (const projectId of newProjectIds) {
-          const projectRes = await getGameEventProject(projectId);
-          
-          if (projectRes) {
-            const project = projectRes.data;
-            let currentRefereeGroups = project.refereeGroups || [];
-            // 如果该项目的裁判组中没有该裁判,则添加
-            if(!project.refereeGroups.includes(String(form.refereeId))){
-              currentRefereeGroups.push(String(form.refereeId)) ;
-              await updateGameEventProject({
-                ...project,
-                refereeGroups: currentRefereeGroups
-              });
-            }
-          }
-        }
-        
         await updateGameReferee(submitForm as GameRefereeForm);
       } else {
-        // 新增裁判
-        const refereeResult = await addGameReferee(submitForm as GameRefereeForm);
-        
-        // 获取新添加的裁判ID
-        const newRefereeId = refereeResult.data?.refereeId || refereeResult.data;
-        
-        // 更新所选项目的裁判组字段
-        if (newRefereeId && form.projectList2.length > 0) {
-          for (const projectId of form.projectList2) {
-            const projectRes = await getGameEventProject(projectId);
-            
-            if (projectRes) {
-              const project = projectRes.data;
-              let currentRefereeGroups = project.refereeGroups || [];
-              currentRefereeGroups.push(String(newRefereeId));
-              await updateGameEventProject({
-                ...project,
-                refereeGroups: currentRefereeGroups
-              });
-            }
-          }
-        }
+        await addGameReferee(submitForm as GameRefereeForm);
       }
       
       proxy?.$modal.msgSuccess("操作成功");
       dialog.visible = false;
       await getList();
     } catch (error) {
-      console.error('操作失败:', error);
+      console.error("操作失败:", error);
       proxy?.$modal.msgError("操作失败");
     } finally {
       buttonLoading.value = false;
@@ -447,11 +372,11 @@ const handleDelete = async (row?: GameRefereeVO) => {
     if (row) {
       const refereeId = row.refereeId;
       if (row.projectList2) {
-        const projectIds = row.projectList2.filter(id => id.trim());
+        const projectIds = row.projectList2.filter(id => id);
         
         for (const projectId of projectIds) {
           // 获取当前项目信息
-          const getProject = await getGameEventProject(projectId.trim());
+          const getProject = await getGameEventProject(projectId);
           const project = getProject.data;
           if (project) {
             
@@ -459,7 +384,7 @@ const handleDelete = async (row?: GameRefereeVO) => {
             
             // 从裁判组中移除该裁判ID
             if (currentRefereeGroups) {
-              const refereeIds = currentRefereeGroups.filter(id => id.trim() !== String(refereeId));
+              const refereeIds = currentRefereeGroups.filter(id => id !== refereeId);
               // 更新项目表中的裁判组字段
               await updateGameEventProject({
                 ...project,
@@ -482,18 +407,12 @@ const handleDelete = async (row?: GameRefereeVO) => {
 /** 生成裁判二维码 */
 const handleGenerateQRCode = async (row: GameRefereeVO) => {
   currentReferee.value = row;
-  // 生成二维码数据,可以包含裁判的基本信息
-  const qrData = {
-    eventId: row.eventId,
-    refereeId: row.refereeId,
-    name: row.name,
-    account: row.account,
-    password: row.password,
-    projectList: row.projectList2,
-  };
   
   try {
-    qrCodeData.value = await toDataURL(JSON.stringify(qrData), { width: 200 });
+    // 生成二维码数据
+    const res = await generateRefereeQRCode(row.refereeId);
+    qrCodeData.value = res.data;
+    console.log('二维码数据:', res);
     qrCodeDialog.visible = true;
   } catch (error) {
     proxy?.$modal.msgError("生成二维码失败");
@@ -522,4 +441,4 @@ onMounted(() => {
   getList();
 });
 
-</script>
+</script>

+ 4 - 4
src/views/system/gameTeam/index.vue

@@ -47,8 +47,8 @@
 
       <el-table v-loading="loading" border :data="gameTeamList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <!--        <el-table-column label="主键" align="center" prop="teamId" v-if="columns[0].visible" />-->
-        <!--        <el-table-column label="队伍编号" align="center" prop="teamCode" v-if="columns[1].visible" />-->
+        <el-table-column label="队伍id" align="center" prop="teamId" v-if="columns[0].visible" />
+        <el-table-column label="队伍编号" align="center" prop="teamCode" v-if="columns[1].visible" />
         <el-table-column label="赛事名称" align="center" prop="eventName" v-if="columns[2].visible" />
         <el-table-column label="队伍名称" align="center" prop="teamName" v-if="columns[3].visible" />
         <el-table-column label="团队描述" align="center" prop="teamDescribe" v-if="columns[4].visible">
@@ -168,8 +168,8 @@ const eventOptions = ref<Array<{ label: string; value: string | number }>>([]);
 
 // 列显隐数据
 const columns = ref<FieldOption[]>([
-  { key: 0, label: '主键', visible: false },
-  { key: 1, label: '队伍编号', visible: true },
+  { key: 0, label: '队伍id', visible: false },
+  { key: 1, label: '队伍编号', visible: false },
   { key: 2, label: '赛事名称', visible: false },
   { key: 3, label: '队伍名称', visible: true },
   { key: 4, label: '团队描述', visible: true },