Explorar o código

feat(tournaments): 更新赛事管理界面功能

- 添加报名时间和开赛时间字段
- 实现多报名条件支持及动态增减功能
- 增加比赛项目选择功能
- 更新表单验证逻辑以支持新的报名条件
- 优化赛事详情显示和数据绑定
- 添加报名条件提示工具栏
- 调整表单项布局和标签描述
fugui001 hai 1 semana
pai
achega
0c0c789bbb

+ 3 - 2
src/api/system/physical/tournaments/types.ts

@@ -89,7 +89,7 @@ export interface TournamentsVO {
    */
   competitionIcon: string;
   itemsPrizeList?: ItemsPrize[];
-
+  itemsConditionList?: ItemsPrize[];
   /**
    * 赛事背景图
    */
@@ -195,7 +195,7 @@ export interface TournamentsForm extends BaseEntity {
   /**
    * 游戏玩法类型:0=德州扑克, 1=奥马哈, 2=短牌
    */
-  gameVariant?: number;
+  gameVariant?: string;
 
   /**
    * 起始记分牌数量
@@ -326,6 +326,7 @@ export interface TournamentsForm extends BaseEntity {
    */
   qualifierValue?: number;
   itemsPrizeList?: ItemsPrize[];
+  itemsConditionList?: ItemsPrize[];
   tournamentsIntroduction?: string;
   leagueTournamentId?: number;
   leagueTournamentRegion?: string;

+ 163 - 25
src/views/system/physical/tournaments/index.vue

@@ -74,17 +74,27 @@
             <span v-else></span>
           </template>
         </el-table-column>
-        <el-table-column label="开始时间" align="center" prop="startTime" width="150" sortable="custom"> </el-table-column>
+        <el-table-column label="报名时间" align="center" prop="signTime" width="150" sortable="custom"> </el-table-column>
+        <el-table-column label="开赛时间" align="center" prop="startTime" width="150" sortable="custom"> </el-table-column>
         <el-table-column label="结束时间" align="center" prop="endTime" width="150"> </el-table-column>
         <el-table-column label="报名人数" align="center" prop="signNum" width="80"> </el-table-column>
         <el-table-column label="总手数" align="center" prop="totalSignup" width="70"> </el-table-column>
         <el-table-column label="报名要求" align="center">
           <template #default="scope">
-            {{
-              scope.row.itemsName && scope.row.itemsNum
-                ? `${scope.row.itemsName} x ${scope.row.itemsNum}`
-                : scope.row.itemsName || scope.row.itemsNum || '—'
-            }}
+            <div v-if="scope.row.itemsConditionList && scope.row.itemsConditionList.length > 0">
+              <!-- 显示第一个报名条件 -->
+              <div>{{ scope.row.itemsConditionList[0].itemsName }} x {{ scope.row.itemsConditionList[0].quantity }}</div>
+
+              <!-- 如果有更多报名条件,用 tooltip 显示 -->
+              <el-tooltip
+                v-if="scope.row.itemsConditionList.length > 1"
+                :content="getConditionTooltipContent(scope.row.itemsConditionList)"
+                placement="top"
+              >
+                <span class="more-conditions">更多</span>
+              </el-tooltip>
+            </div>
+            <span v-else>—</span>
           </template>
         </el-table-column>
 
@@ -125,7 +135,9 @@
               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['physical:tournaments:edit']">修改</el-button>
             </el-tooltip>
             <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['physical:tournaments:remove']">删除</el-button>
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['physical:tournaments:remove']"
+                >删除</el-button
+              >
             </el-tooltip>
             <el-tooltip content="复制" placement="top" v-hasPermi="['business:tournaments:query']">
               <el-button link type="primary" icon="Files" @click="handleCopy(scope.row)"> 复制 </el-button>
@@ -150,7 +162,12 @@
         <el-form-item label="所属赛区" prop="leagueTournamentRegion">
           <el-input v-model="form.leagueTournamentRegion" placeholder="请输入赛区" />
         </el-form-item>
-<!--        <el-form-item label="赛事位置" prop="competitionLocation">
+        <el-form-item label="比赛项目" prop="gameVariant">
+          <el-select aria-required="true" v-model="form.gameVariant" placeholder="请选择" :disabled="dialog.mode === 'view'">
+            <el-option v-for="dict in game_variant_type" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
+          </el-select>
+        </el-form-item>
+        <!--        <el-form-item label="赛事位置" prop="competitionLocation">
           <el-input v-model="form.competitionLocation" placeholder="请输入赛事位置" />
         </el-form-item>-->
         <!-- 比赛图标 -->
@@ -235,8 +252,11 @@
             </el-upload>
           </div>
         </el-form-item>
-
-        <el-form-item label="比赛开始时间" prop="startTime">
+        <el-form-item label="报名时间" prop="startTime">
+          <el-date-picker clearable v-model="form.signTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择比赛开始时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="开赛时间" prop="startTime">
           <el-date-picker clearable v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择比赛开始时间">
           </el-date-picker>
         </el-form-item>
@@ -245,12 +265,12 @@
             <el-option v-for="dict in physical_tournaments_type" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="报名时长" prop="signTime">
+        <!--        <el-form-item label="报名时长" prop="signTime">
           <el-select v-model="form.signTime" placeholder="请选择" :disabled="dialog.mode === 'view'">
             <el-option v-for="dict in tournaments_time" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
           </el-select>
-        </el-form-item>
-        <el-form-item label="报名条件" prop="itemsId">
+        </el-form-item>-->
+        <!--        <el-form-item label="报名条件" prop="itemsId">
           <div style="display: flex; align-items: center; gap: 10px; width: 100%">
             <el-select v-model="form.itemsId" placeholder="请选择道具类型" :disabled="dialog.mode === 'view'" style="flex: 1">
               <el-option v-for="item in itemOptions" :key="item.id" :label="item.label" :value="item.id" />
@@ -260,8 +280,7 @@
               <el-input v-model.number="form.itemsNum" placeholder="数量" :disabled="dialog.mode === 'view'" style="width: 100%" />
             </el-form-item>
           </div>
-        </el-form-item>
-
+        </el-form-item>-->
         <!-- 盲注表 -->
         <el-form-item label="盲注等级" prop="blindStructureId">
           <div style="display: flex; align-items: center">
@@ -291,20 +310,47 @@
           <el-input v-model="form.startingChips" placeholder="请输入起始记分牌数量" :disabled="dialog.mode === 'view'" />
         </el-form-item>
 
-        <el-form-item label="级别持续时间" prop="levelDuration" v-if="dialog.mode === 'edit'">
+        <!--        <el-form-item label="级别持续时间" prop="levelDuration" v-if="dialog.mode === 'edit'">
           <el-input v-model="form.levelDuration" placeholder="请输入级别持续时间" />
-        </el-form-item>
+        </el-form-item>-->
 
-        <el-form-item label="延迟卡时间" prop="delayCardTime">
+        <!--        <el-form-item label="延迟卡时间" prop="delayCardTime">
           <el-input v-model="form.delayCardTime" placeholder="请输入延迟卡时间" :disabled="dialog.mode === 'view'" />
         </el-form-item>
 
         <el-form-item label="延迟卡数量" prop="delayCardNum">
           <el-input v-model="form.delayCardNum" placeholder="请输入延迟卡数量" :disabled="dialog.mode === 'view'" />
+        </el-form-item>-->
+        <el-form-item label="参赛人数" prop="minPlayers">
+          <el-input v-model="form.maxPlayers" placeholder="请输入参赛人数" :disabled="dialog.mode === 'view'" />
         </el-form-item>
         <el-form-item label="最小参赛人数" prop="minPlayers">
           <el-input v-model="form.minPlayers" placeholder="请输入最小参赛人数" :disabled="dialog.mode === 'view'" />
         </el-form-item>
+        <el-form-item label="报名条件" prop="itemsId">
+          <div>
+            <div v-for="(condition, index) in formConditions.conditions" :key="index" style="display: flex; align-items: center; margin-bottom: 8px">
+              <span>第{{ condition.index + 1 }}项</span>
+
+              <!-- 道具选择 -->
+              <el-select v-model="condition.itemId" placeholder="选项" style="width: 120px; margin-right: 8px" :disabled="dialog.mode === 'view'">
+                <el-option v-for="item in itemOptions" :key="item.id" :label="item.label" :value="item.id" />
+              </el-select>
+
+              <!-- 数量输入 -->
+              <el-input
+                v-model="condition.quantity"
+                placeholder="请输入数量"
+                style="width: 100px; margin-right: 8px"
+                :disabled="dialog.mode === 'view'"
+              />
+
+              <!-- 操作按钮 -->
+              <el-button type="primary" @click="addCondition" v-if="index === 0 && dialog.mode !== 'view'">+</el-button>
+              <el-button type="primary" @click="removeCondition(index)" v-if="index !== 0 && dialog.mode !== 'view'">-</el-button>
+            </div>
+          </div>
+        </el-form-item>
         <!-- 奖励内容 -->
         <el-form-item label="奖励内容">
           <div v-for="(reward, index) in formPrize.rewards" :key="index" style="display: flex; align-items: center; margin-bottom: 8px">
@@ -323,11 +369,11 @@
             <el-button type="primary" @click="removeReward(index)" v-if="index !== 0 && dialog.mode !== 'view'">-</el-button>
           </div>
         </el-form-item>
-        <el-form-item label="比赛规则" prop="tournamentsIntroduction">
+        <el-form-item label="赛制详情" prop="tournamentsIntroduction">
           <el-input
             v-model="form.tournamentsIntroduction"
             type="textarea"
-            placeholder="请输入赛事简介(比赛规则)"
+            placeholder="请输入赛制详情(比赛规则)"
             :disabled="dialog.mode === 'view'"
             :rows="4"
           />
@@ -384,8 +430,8 @@ import { LeagueTournamentVO } from '@/api/system/physical/leagueTournament/types
 import { JudgeVO } from '@/api/system/physical/judge/types';
 import { ElSelect } from 'element-plus';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { tournaments_type, tournaments_time, physical_tournaments_type } = toRefs<any>(
-  proxy?.useDict('tournaments_type', 'tournaments_time', 'physical_tournaments_type')
+const { game_variant_type, tournaments_time, physical_tournaments_type } = toRefs<any>(
+  proxy?.useDict('game_variant_type', 'tournaments_time', 'physical_tournaments_type')
 );
 const tournamentsList = ref<TournamentsVO[]>([]);
 const buttonLoading = ref(false);
@@ -487,7 +533,22 @@ const data = reactive<PageData<TournamentsForm, TournamentsQuery>>({
     gameType: [{ required: true, message: '游戏类型不能为空', trigger: 'change' }],
     lateRegistrationLevel: [{ required: true, message: '截止报名级别不能为空', trigger: 'change' }],
     signTime: [{ required: true, message: '报名时间不能为空', trigger: 'change' }],
-    itemsId: [{ required: true, message: '报名条件不能为空', trigger: 'change' }],
+    // ...其他规则
+    itemsId: [
+      {
+        validator: (rule, value, callback) => {
+          // 检查是否至少有一个报名条件被设置
+          const hasValidCondition = formConditions.conditions.some((condition) => condition.itemId && condition.quantity);
+
+          if (!hasValidCondition) {
+            callback(new Error('报名条件不能为空'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'change'
+      }
+    ],
     blindStructureId: [{ required: true, message: '盲注等级不能为空', trigger: 'change' }],
     startingChips: [
       { required: true, message: '起始记分牌数量不能为空', trigger: 'blur' },
@@ -619,12 +680,13 @@ const cancel = () => {
   iconPreviewUrl2.value = '';
   competitionBg.value = '';
   dialog.visible = false;
-};;
+};
 
 /** 表单重置 */
 const reset = () => {
   form.value = { ...initFormData };
   tournamentsFormRef.value?.resetFields();
+  resetFormConditions();
 };
 
 /** 搜索按钮操作 */
@@ -675,8 +737,26 @@ const handleUpdate = async (row?: TournamentsVO, mode: 'edit' | 'view' = 'edit')
   Object.assign(form.value, res.data);
   form.value.signTime = String(res.data.signTime);
   form.value.gameType = String(res.data.gameType); // 转为字符串
+  form.value.gameVariant = String(res.data.gameVariant); // 转为字符串
   competitionIcon.value = res.data.competitionIcon;
   competitionBg.value = res.data.competitionBg;
+  // 处理报名条件数据
+  const conditionItems = res.data.itemsConditionList || [];
+  if (conditionItems.length > 0) {
+    formConditions.conditions = conditionItems.map((item, index) => ({
+      index: index,
+      itemId: Number(item.itemId),
+      quantity: Number(item.quantity)
+    }));
+  } else {
+    formConditions.conditions = [
+      {
+        index: 0,
+        itemId: null,
+        quantity: null
+      }
+    ];
+  }
   // 处理奖励表单数据
   const prizeItems = res.data.itemsPrizeList || [];
   if (prizeItems.length > 0) {
@@ -720,7 +800,15 @@ const submitForm = () => {
           ranking: Number(reward.ranking),
           itemId: Number(reward.itemId),
           quantity: Number(reward.quantity)
-        }))
+        })),
+        // 添加报名条件数据
+        itemsConditionList: formConditions.conditions
+          .filter((condition) => condition.itemId && condition.quantity) // 过滤掉未填写的条件
+          .map((condition) => ({
+            ranking: 0,
+            itemId: Number(condition.itemId),
+            quantity: Number(condition.quantity)
+          }))
       };
       // 提交数据(区分新增/编辑)
       let response;
@@ -1110,4 +1198,54 @@ const loadJudgeOptions = async () => {
     ElMessage.error('请求失败,请检查网络');
   }
 };
+
+// 报名条件数据结构
+const formConditions = reactive({
+  conditions: [
+    {
+      index: 0,
+      itemId: null,
+      quantity: null
+    }
+  ]
+});
+
+// 默认报名条件结构
+const defaultCondition = {
+  index: 0,
+  itemId: null,
+  quantity: null
+};
+
+// 添加报名条件
+const addCondition = () => {
+  const currentLength = formConditions.conditions.length;
+
+  formConditions.conditions.push({
+    index: currentLength,
+    itemId: null,
+    quantity: null
+  });
+};
+
+// 移除报名条件
+const removeCondition = (index: number) => {
+  formConditions.conditions.splice(index, 1);
+
+  // 重新设置索引
+  formConditions.conditions.forEach((c, i) => {
+    c.index = i;
+  });
+};
+
+// 重置报名条件
+const resetFormConditions = () => {
+  formConditions.conditions.splice(0);
+  formConditions.conditions.push({ ...defaultCondition });
+};
+// 获取报名条件提示内容
+const getConditionTooltipContent = (conditions: any[]) => {
+  return conditions.map((condition) => `${condition.itemsName} x ${condition.quantity}`).join('\n');
+};
+
 </script>