Explorar o código

feat(physical): 新增线下赛事管理功能

- 新增赛事列表查询、详情查看、新增、修改、删除接口
- 实现赛事管理页面,支持赛事信息录入与编辑
- 添加赛事图标与背景图上传及预览功能
- 支持赛事报名条件、盲注等级、奖励内容等配置
- 实现赛事数据导出功能
- 集成盲注结构与盲注等级下拉选项联动逻辑
- 添加赛事状态展示与操作权限控制
fugui001 hai 2 meses
pai
achega
4fea65decb

+ 6 - 0
src/api/system/physical/blindLevels/index.ts

@@ -61,3 +61,9 @@ export const delBlindLevels = (id: string | number | Array<string | number>) =>
     method: 'delete'
   });
 };
+export const selectPhysicalBlindLevelsById = (id: number): AxiosPromise<BlindLevelsVO[]> => {
+  return request({
+    url: '/physical/blindLevels/selectPhysicalBlindLevelsById/' + id,
+    method: 'get'
+  });
+};

+ 7 - 0
src/api/system/physical/blindStructures/index.ts

@@ -72,3 +72,10 @@ export const downloadImportTemplate = async () => {
   });
   return res;
 };
+
+export const selectPhysicalBlingStructuresInfo = (): AxiosPromise<BlindStructuresVO> => {
+  return request({
+    url: '/physical/blindStructures/selectPhysicalBlingStructuresInfo',
+    method: 'get'
+  });
+};

+ 63 - 0
src/api/system/physical/tournaments/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { TournamentsVO, TournamentsForm, TournamentsQuery } from '@/api/system/physical/tournaments/types';
+
+/**
+ * 查询线下赛事列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listTournaments = (query?: TournamentsQuery): AxiosPromise<TournamentsVO[]> => {
+  return request({
+    url: '/physical/tournaments/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询线下赛事详细
+ * @param id
+ */
+export const getTournaments = (id: string | number): AxiosPromise<TournamentsVO> => {
+  return request({
+    url: '/physical/tournaments/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增线下赛事
+ * @param data
+ */
+export const addTournaments = (data: TournamentsForm) => {
+  return request({
+    url: '/physical/tournaments',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改线下赛事
+ * @param data
+ */
+export const updateTournaments = (data: TournamentsForm) => {
+  return request({
+    url: '/physical/tournaments',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除线下赛事
+ * @param id
+ */
+export const delTournaments = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/physical/tournaments/' + id,
+    method: 'delete'
+  });
+};

+ 494 - 0
src/api/system/physical/tournaments/types.ts

@@ -0,0 +1,494 @@
+export interface TournamentsVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 赛事名称
+   */
+  name: string;
+
+  /**
+   * 比赛开始时间
+   */
+  startTime: string;
+
+  /**
+   * 比赛结束时间
+   */
+  endTime: string;
+
+  /**
+   * 比赛暂停时间
+   */
+  pauseTime: string;
+
+  /**
+   * 游戏类型
+   */
+  gameType: number;
+
+  /**
+   * 游戏玩法类型:0=德州扑克, 1=奥马哈, 2=短牌
+   */
+  gameVariant: number;
+
+  /**
+   * 起始记分牌数量
+   */
+  startingChips: number;
+
+  /**
+   * 级别持续时间(分钟)
+   */
+  levelDuration: number;
+
+  /**
+   * 截止报名级别
+   */
+  lateRegistrationLevel: number;
+
+  /**
+   * 最大参赛人数
+   */
+  maxPlayers: number;
+
+  /**
+   *
+   */
+  minPlayers: number;
+
+  /**
+   * 奖励人数
+   */
+  rewardPlayers: number;
+
+  /**
+   * 总手数,包括rebuy
+   */
+  totalSignup: number;
+
+  /**
+   * 机器人报名默认数量,0-为不开启机器人报名
+   */
+  robotCount: number;
+
+  /**
+   * 赛事状态 0 未开始 1 进行中 2 同步发牌 3 普通暂停 4 完成 5 异常
+   */
+  status: number;
+
+  /**
+   * 报名时间
+   */
+  signTime: number;
+
+  /**
+   * 比赛图标
+   */
+  competitionIcon: string;
+  itemsPrizeList?: ItemsPrize[];
+
+  /**
+   * 赛事背景图
+   */
+  competitionBg: string;
+
+  /**
+   * 比赛ID
+   */
+  tournamentsBiId: string | number;
+
+  /**
+   * 修改人
+   */
+  updateUserId: string | number;
+
+  /**
+   *
+   */
+  updatedAt: string;
+
+  /**
+   * 创建人
+   */
+  createUserId: string | number;
+
+  /**
+   *
+   */
+  createdAt: string;
+
+  /**
+   * 是否删除
+   */
+  isDelete: number;
+
+  /**
+   * 延迟卡时间
+   */
+  delayCardTime: number;
+
+  /**
+   * 延迟卡数量
+   */
+  delayCardNum: number;
+
+  /**
+   * 行动时间(使用卡延时)
+   */
+  actionTime: number;
+
+  /**
+   * 目标锦标赛ID(预赛晋级到的决赛ID)
+   */
+  targetTournamentId: string | number;
+
+  /**
+   * 晋级条件类型:0=无晋级,1=按级别晋级,2=按人数比例晋级
+   */
+  qualifierType: number;
+
+  /**
+   * 晋级条件值:级别数或人数比例(1-100)
+   */
+  qualifierValue: number;
+}
+
+export interface TournamentsForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 赛事名称
+   */
+  name?: string;
+
+  /**
+   * 赛事位置
+   */
+  competitionLocation?: string;
+
+  /**
+   * 比赛开始时间
+   */
+  startTime?: string;
+
+  /**
+   * 比赛结束时间
+   */
+  endTime?: string;
+
+  /**
+   * 比赛暂停时间
+   */
+  pauseTime?: string;
+
+  /**
+   * 游戏类型
+   */
+  gameType?: string;
+
+  /**
+   * 游戏玩法类型:0=德州扑克, 1=奥马哈, 2=短牌
+   */
+  gameVariant?: number;
+
+  /**
+   * 起始记分牌数量
+   */
+  startingChips?: number;
+
+  /**
+   * 级别持续时间(分钟)
+   */
+  levelDuration?: number;
+
+  /**
+   * 截止报名级别
+   */
+  lateRegistrationLevel?: number;
+
+  /**
+   * 最大参赛人数
+   */
+  maxPlayers?: number;
+
+  /**
+   *
+   */
+  minPlayers?: number;
+
+  /**
+   * 奖励人数
+   */
+  rewardPlayers?: number;
+
+  /**
+   * 总手数,包括rebuy
+   */
+  totalSignup?: number;
+
+  /**
+   * 机器人报名默认数量,0-为不开启机器人报名
+   */
+  robotCount?: number;
+
+  /**
+   * 赛事状态 0 未开始 1 进行中 2 同步发牌 3 普通暂停 4 完成 5 异常
+   */
+  status?: number;
+
+  /**
+   * 报名时间
+   */
+  signTime?: string;
+
+  /**
+   * 道具ID
+   */
+  itemsId?: number;
+
+  itemsNum?: number;
+
+  blindStructureId?: number;
+
+  /**
+   * 比赛图标
+   */
+  competitionIcon?: string;
+
+  /**
+   * 赛事背景图
+   */
+  competitionBg?: string;
+
+  /**
+   * 比赛ID
+   */
+  tournamentsBiId?: string | number;
+
+  /**
+   * 修改人
+   */
+  updateUserId?: string | number;
+
+  /**
+   *
+   */
+  updatedAt?: string;
+
+  /**
+   * 创建人
+   */
+  createUserId?: string | number;
+
+  /**
+   *
+   */
+  createdAt?: string;
+
+  /**
+   * 是否删除
+   */
+  isDelete?: number;
+
+  /**
+   * 延迟卡时间
+   */
+  delayCardTime?: number;
+
+  /**
+   * 延迟卡数量
+   */
+  delayCardNum?: number;
+
+  /**
+   * 行动时间(使用卡延时)
+   */
+  actionTime?: number;
+
+  /**
+   * 目标锦标赛ID(预赛晋级到的决赛ID)
+   */
+  targetTournamentId?: string | number;
+
+  /**
+   * 晋级条件类型:0=无晋级,1=按级别晋级,2=按人数比例晋级
+   */
+  qualifierType?: number;
+
+  /**
+   * 晋级条件值:级别数或人数比例(1-100)
+   */
+  qualifierValue?: number;
+  itemsPrizeList?: ItemsPrize[];
+}
+export interface ItemsPrize {
+  ranking: number;
+  itemId: number;
+  quantity: number;
+}
+export interface TournamentsQuery extends PageQuery {
+  /**
+   * 赛事名称
+   */
+  id?: string;
+  /**
+   * 赛事名称
+   */
+  name?: string;
+
+  /**
+   * 比赛开始时间
+   */
+  startTime?: string;
+
+  /**
+   * 比赛结束时间
+   */
+  endTime?: string;
+
+  /**
+   * 比赛暂停时间
+   */
+  pauseTime?: string;
+
+  /**
+   * 游戏类型
+   */
+  gameType?: number;
+
+  /**
+   * 游戏玩法类型:0=德州扑克, 1=奥马哈, 2=短牌
+   */
+  gameVariant?: number;
+
+  /**
+   * 起始记分牌数量
+   */
+  startingChips?: number;
+
+  /**
+   * 级别持续时间(分钟)
+   */
+  levelDuration?: number;
+
+  /**
+   * 截止报名级别
+   */
+  lateRegistrationLevel?: number;
+
+  /**
+   * 最大参赛人数
+   */
+  maxPlayers?: number;
+
+  /**
+   *
+   */
+  minPlayers?: number;
+
+  /**
+   * 奖励人数
+   */
+  rewardPlayers?: number;
+
+  /**
+   * 总手数,包括rebuy
+   */
+  totalSignup?: number;
+
+  /**
+   * 机器人报名默认数量,0-为不开启机器人报名
+   */
+  robotCount?: number;
+
+  /**
+   * 赛事状态 0 未开始 1 进行中 2 同步发牌 3 普通暂停 4 完成 5 异常
+   */
+  status?: number;
+
+  /**
+   * 报名时间
+   */
+  signTime?: number;
+
+  /**
+   * 比赛图标
+   */
+  competitionIcon?: string;
+
+  /**
+   * 赛事背景图
+   */
+  competitionBg?: string;
+
+  /**
+   * 比赛ID
+   */
+  tournamentsBiId?: string | number;
+
+  /**
+   * 修改人
+   */
+  updateUserId?: string | number;
+
+  /**
+   *
+   */
+  updatedAt?: string;
+
+  /**
+   * 创建人
+   */
+  createUserId?: string | number;
+
+  /**
+   *
+   */
+  createdAt?: string;
+
+  /**
+   * 是否删除
+   */
+  isDelete?: number;
+
+  /**
+   * 延迟卡时间
+   */
+  delayCardTime?: number;
+
+  /**
+   * 延迟卡数量
+   */
+  delayCardNum?: number;
+
+  /**
+   * 行动时间(使用卡延时)
+   */
+  actionTime?: number;
+
+  /**
+   * 目标锦标赛ID(预赛晋级到的决赛ID)
+   */
+  targetTournamentId?: string | number;
+
+  /**
+   * 晋级条件类型:0=无晋级,1=按级别晋级,2=按人数比例晋级
+   */
+  qualifierType?: number;
+
+  /**
+   * 晋级条件值:级别数或人数比例(1-100)
+   */
+  qualifierValue?: number;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 994 - 0
src/views/system/physical/tournaments/index.vue

@@ -0,0 +1,994 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="赛事ID" prop="name">
+              <el-input v-model="queryParams.id" placeholder="请输入赛事ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="赛事名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入赛事名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="开始时间" prop="startTime">
+              <el-date-picker clearable v-model="queryParams.startTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择比赛开始时间" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['physical:tournaments:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['physical:tournaments:edit']"
+              >修改</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['physical:tournaments:remove']"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['physical:tournaments:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="tournamentsList">
+        <el-table-column label="比赛ID" align="center" prop="id" width="70" />
+        <el-table-column label="比赛名" align="center" prop="name" />
+        <el-table-column label="比赛Logo" align="center" width="90">
+          <template #default="scope">
+            <el-image
+              v-if="scope.row.competitionIcon"
+              :src="scope.row.competitionIcon"
+              style="width: 40px; height: 40px; border-radius: 4px; cursor: zoom-in"
+              :preview-src-list="[scope.row.competitionIcon]"
+              :preview-teleported="true"
+              fit="cover"
+            />
+            <span v-else></span>
+          </template>
+        </el-table-column>
+        <el-table-column label="比赛背景" align="center" width="90">
+          <template #default="scope">
+            <el-image
+              v-if="scope.row.competitionBg"
+              :src="scope.row.competitionBg"
+              style="width: 40px; height: 40px; border-radius: 4px; cursor: zoom-in"
+              :preview-src-list="[scope.row.competitionBg]"
+              :preview-teleported="true"
+              fit="cover"
+            />
+            <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="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 || '—'
+            }}
+          </template>
+        </el-table-column>
+
+        <el-table-column label="截止盲注等级" align="center" prop="lateRegistrationLevel" />
+        <el-table-column label="盲注表" align="center" prop="blindStructuresName" />
+
+        <el-table-column label="奖励" align="center">
+          <template #default="scope">
+            <div v-if="scope.row.itemsPrizeList && scope.row.itemsPrizeList.length > 0">
+              <!-- 显示第一名奖励 -->
+              <div>
+                第{{ scope.row.itemsPrizeList[0].ranking }}名:{{ scope.row.itemsPrizeList[0].quantity }} {{ scope.row.itemsPrizeList[0].itemsName }}
+              </div>
+
+              <!-- 如果有更多奖励,用 tooltip 显示 -->
+              <el-tooltip v-if="scope.row.itemsPrizeList.length > 1" :content="getRewardTooltipContent(scope.row.itemsPrizeList)" placement="top">
+                <span class="more-rewards">更多</span>
+              </el-tooltip>
+            </div>
+            <span v-else>—</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="最小参赛人数" align="center" prop="minPlayers" />
+        <el-table-column label="状态" align="center">
+          <template #default="scope">
+            <span
+              :style="{
+                color: scope.row.isDelete ? 'red' : ''
+              }"
+            >
+              {{ scope.row.isDelete ? '已删除' : scope.row.statusText || '' }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <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-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改线下赛事对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="cancel">
+      <el-form ref="tournamentsFormRef" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="赛事名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入赛事名称" />
+        </el-form-item>
+        <el-form-item label="赛事位置" prop="name">
+          <el-input v-model="form.competitionLocation" placeholder="请输入赛事位置" />
+        </el-form-item>
+        <!-- 比赛图标 -->
+        <el-form-item label="比赛图标" prop="icon">
+          <div class="upload-container">
+            <el-upload
+              class="upload-icon"
+              action="#"
+              :on-change="handleIconChange"
+              :on-remove="handleIconRemove"
+              :file-list="fileList"
+              :auto-upload="false"
+              :limit="1"
+              accept="image/*"
+              :disabled="dialog.mode === 'view'"
+            >
+              <template #trigger>
+                <el-button type="primary" :disabled="dialog.mode === 'view'">点击选择图标</el-button>
+              </template>
+
+              <!-- 预览图区域 -->
+              <template #default>
+                <div class="preview-area" @click="handlePreviewClick">
+                  <!-- 只有当 iconPreviewUrl 或 competitionIcon 存在时才显示 img -->
+                  <img
+                    v-if="iconPreviewUrl || competitionIcon"
+                    :src="iconPreviewUrl || competitionIcon"
+                    alt="预览图"
+                    style="max-width: 100px; max-height: 100px; margin-top: 10px; cursor: pointer"
+                  />
+                  <!-- 可选:无图时显示提示文字 -->
+                  <div v-else style="margin-top: 10px; color: #999"></div>
+                </div>
+              </template>
+
+              <template #tip>
+                <div class="el-upload__tip">
+                  <span v-if="fileList.length > 0">当前已选文件:{{ fileList[0].name }}</span>
+                </div>
+              </template>
+            </el-upload>
+          </div>
+        </el-form-item>
+        <!-- 比赛图标 -->
+        <el-form-item label="比赛背景" prop="icon">
+          <div class="upload-container">
+            <el-upload
+              class="upload-icon"
+              action="#"
+              :on-change="handleIconChange2"
+              :on-remove="handleIconRemove2"
+              :file-list="fileList2"
+              :auto-upload="false"
+              :limit="1"
+              accept="image/*"
+              :disabled="dialog.mode === 'view'"
+            >
+              <template #trigger>
+                <el-button type="primary" :disabled="dialog.mode === 'view'">点击选择背景</el-button>
+              </template>
+
+              <!-- 预览图区域 -->
+              <template #default>
+                <div class="preview-area" @click="handlePreviewClick2">
+                  <!-- 只有当 iconPreviewUrl 或 competitionIcon 存在时才显示 img -->
+                  <img
+                    v-if="iconPreviewUrl2 || competitionBg"
+                    :src="iconPreviewUrl2 || competitionBg"
+                    alt="预览图"
+                    style="max-width: 100px; max-height: 100px; margin-top: 10px; cursor: pointer"
+                  />
+                  <!-- 可选:无图时显示提示文字 -->
+                  <div v-else style="margin-top: 10px; color: #999"></div>
+                </div>
+              </template>
+
+              <template #tip>
+                <div class="el-upload__tip">
+                  <span v-if="fileList2.length > 0">当前已选文件:{{ fileList2[0].name }}</span>
+                </div>
+              </template>
+            </el-upload>
+          </div>
+        </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>
+        <el-form-item label="比赛类型" prop="gameType">
+          <el-select aria-required="true" v-model="form.gameType" placeholder="请选择">
+            <el-option
+              v-for="dict in tournaments_type.filter((item) => item.value !== '1')"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <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">
+          <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" />
+            </el-select>
+
+            <el-form-item prop="itemsNum" style="margin-bottom: 0; flex: 1">
+              <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 label="盲注等级" prop="blindStructureId">
+          <div style="display: flex; align-items: center">
+            <el-select
+              v-model="form.blindStructureId"
+              placeholder="选项"
+              style="width: 200px"
+              @change="handleBlindStructureChange"
+              :disabled="dialog.mode === 'view'"
+            >
+              <el-option v-for="item in itemOptionsStructures" :key="item.id" :label="item.label" :value="item.id" />
+            </el-select>
+
+            <el-button type="primary" :disabled="dialog.mode === 'view'" @click="handleGoToStructures"> 上传新盲注 </el-button>
+            <el-button type="primary" @click="handleViewLevels" :disabled="dialog.mode === 'view'">预览</el-button>
+          </div>
+        </el-form-item>
+
+        <!-- 报名截止至 -->
+        <el-form-item label="报名截止至" prop="lateRegistrationLevel">
+          <el-select v-model="form.lateRegistrationLevel" placeholder="选项" style="width: 200px" :disabled="dialog.mode === 'view'">
+            <el-option v-for="item in itemOptionsStructuresLevel" :key="item.id" :label="item.label" :value="item.id" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="起始记分牌数量" prop="startingChips">
+          <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-input v-model="form.levelDuration" placeholder="请输入级别持续时间" />
+        </el-form-item>
+
+        <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.minPlayers" placeholder="请输入最小参赛人数" :disabled="dialog.mode === 'view'" />
+        </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">
+            <span>第{{ reward.ranking }}名</span>
+
+            <!-- 道具选择 -->
+            <el-select v-model="reward.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="reward.quantity" placeholder="请输入数量" style="width: 100px; margin-right: 8px" :disabled="dialog.mode === 'view'" />
+
+            <!-- 操作按钮 -->
+            <el-button type="primary" @click="addReward" v-if="index === 0 && dialog.mode !== 'view'">+</el-button>
+            <el-button type="primary" @click="removeReward(index)" v-if="index !== 0 && dialog.mode !== 'view'">-</el-button>
+          </div>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="levelsDialogVisible" title="盲注等级列表" width="80%">
+      <!-- 使用 component 动态加载目标组件 -->
+      <levels-index :blind-structure-id="dialogParams.blindStructureId" :name="dialogParams.name" />
+    </el-dialog>
+    <!-- 预览弹窗 -->
+    <el-dialog v-model="dialogVisible" title="图片预览" width="50%">
+      <img :src="previewSrc || iconPreviewUrl || competitionIcon" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
+    </el-dialog>
+    <el-dialog v-model="dialogVisible2" title="图片预览" width="50%">
+      <img :src="previewSrc2 || iconPreviewUrl2 || competitionBg" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Tournaments" lang="ts">
+import { listTournaments, getTournaments, delTournaments, addTournaments, updateTournaments } from '@/api/system/physical/tournaments';
+import { TournamentsVO, TournamentsQuery, TournamentsForm } from '@/api/system/physical/tournaments/types';
+import { selectItemsSelList } from '@/api/system/business/items';
+import { selectPhysicalBlingStructuresInfo } from '@/api/system/physical/blindStructures';
+import { selectPhysicalBlindLevelsById } from '@/api/system/physical/blindLevels';
+import { uploadTournament } from '@/api/system/business/tournaments';
+import { parseTime } from '@/utils/dateUtils';
+import LevelsIndex from '@/views/system/physical/blindLevels/index.vue';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { tournaments_type, tournaments_time, tournaments_status } = toRefs<any>(
+  proxy?.useDict('tournaments_type', 'tournaments_time', 'tournaments_status')
+);
+const tournamentsList = ref<TournamentsVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const tournamentsFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<{
+  visible: boolean;
+  title: string;
+  mode: 'edit' | 'view' | 'add'; // 新增字段
+}>({
+  visible: false,
+  title: '',
+  mode: 'edit'
+});
+
+const initFormData: TournamentsForm = {
+  id: undefined,
+  name: undefined,
+  startTime: undefined,
+  endTime: undefined,
+  pauseTime: undefined,
+  gameType: undefined,
+  gameVariant: undefined,
+  startingChips: undefined,
+  levelDuration: undefined,
+  lateRegistrationLevel: undefined,
+  maxPlayers: undefined,
+  minPlayers: undefined,
+  rewardPlayers: undefined,
+  totalSignup: undefined,
+  robotCount: undefined,
+  status: undefined,
+  signTime: undefined,
+  competitionIcon: undefined,
+  competitionBg: undefined,
+  tournamentsBiId: undefined,
+  updateUserId: undefined,
+  updatedAt: undefined,
+  createUserId: undefined,
+  createdAt: undefined,
+  isDelete: undefined,
+  delayCardTime: undefined,
+  delayCardNum: undefined,
+  actionTime: undefined,
+  targetTournamentId: undefined,
+  qualifierType: undefined,
+  qualifierValue: undefined
+};
+const data = reactive<PageData<TournamentsForm, TournamentsQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    startTime: undefined,
+    endTime: undefined,
+    pauseTime: undefined,
+    gameType: undefined,
+    gameVariant: undefined,
+    startingChips: undefined,
+    levelDuration: undefined,
+    lateRegistrationLevel: undefined,
+    maxPlayers: undefined,
+    minPlayers: undefined,
+    rewardPlayers: undefined,
+    totalSignup: undefined,
+    robotCount: undefined,
+    status: undefined,
+    signTime: undefined,
+    competitionIcon: undefined,
+    competitionBg: undefined,
+    tournamentsBiId: undefined,
+    updateUserId: undefined,
+    updatedAt: undefined,
+    createUserId: undefined,
+    createdAt: undefined,
+    isDelete: undefined,
+    delayCardTime: undefined,
+    delayCardNum: undefined,
+    actionTime: undefined,
+    targetTournamentId: undefined,
+    qualifierType: undefined,
+    qualifierValue: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '不能为空', trigger: 'blur' }],
+    name: [{ required: true, message: '赛事名称不能为空', trigger: 'blur' }],
+    startTime: [{ required: true, message: '比赛开始时间不能为空', trigger: 'blur' }],
+    gameType: [{ required: true, message: '游戏类型不能为空', trigger: 'change' }],
+    lateRegistrationLevel: [{ required: true, message: '截止报名级别不能为空', trigger: 'change' }],
+    signTime: [{ required: true, message: '报名时间不能为空', trigger: 'change' }],
+    itemsId: [{ required: true, message: '报名条件不能为空', trigger: 'change' }],
+    blindStructureId: [{ required: true, message: '盲注等级不能为空', trigger: 'change' }],
+    startingChips: [
+      { required: true, message: '起始记分牌数量不能为空', trigger: 'blur' },
+      {
+        validator: (rule, value, callback) => {
+          // 检查是否为字符串类型且包含非数字字符
+          if (typeof value === 'string' && !/^\d+$/.test(value)) {
+            callback(new Error('起始记分牌数量必须为数字'));
+            return;
+          }
+
+          const num = Number(value);
+          if (isNaN(num) || num <= 0) {
+            callback(new Error('起始记分牌数量必须大于0'));
+          } else if (!Number.isInteger(num)) {
+            callback(new Error('起始记分牌数量必须为整数'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur'
+      }
+    ],
+    itemsNum: [
+      { required: true, message: '数量不能为空', trigger: 'blur' },
+      {
+        validator: (rule, value, callback) => {
+          // 检查是否为字符串类型且包含非数字字符
+          if (typeof value === 'string' && !/^\d+$/.test(value)) {
+            callback(new Error('数量必须为正整数'));
+            return;
+          }
+
+          const num = Number(value);
+          if (isNaN(num) || num <= 0) {
+            callback(new Error('数量必须大于0'));
+          } else if (!Number.isInteger(num)) {
+            callback(new Error('数量必须为整数'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur'
+      }
+    ],
+    delayCardTime: [
+      { required: false, message: '延迟卡时间不能为空', trigger: 'blur' },
+      {
+        validator: (rule: any, value: any, callback: any) => {
+          const num = Number(value);
+          if (!value) {
+            callback(new Error('请输入延迟卡时间'));
+          } else if (!/^\d+$/.test(value)) {
+            callback(new Error('只能输入正整数'));
+          } else if (num < 10 || num > 15) {
+            callback(new Error('延迟卡时间必须在10-15秒之间'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur'
+      }
+    ],
+    delayCardNum: [
+      { required: false, message: '延迟卡数量不能为空', trigger: 'blur' },
+      {
+        validator: (rule: any, value: any, callback: any) => {
+          const num = Number(value);
+          if (!value) {
+            callback(new Error('请输入延迟卡数量'));
+          } else if (!/^\d+$/.test(value)) {
+            callback(new Error('只能输入正整数'));
+          } else if (num < 1 || num > 99) {
+            callback(new Error('延迟卡数量必须在1-99之间'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur'
+      }
+    ],
+    minPlayers: [
+      { required: false, message: '最小参赛人数不能为空', trigger: 'blur' },
+      {
+        validator: (rule: any, value: any, callback: any) => {
+          const num = Number(value);
+          if (!/^\d+$/.test(value)) {
+            callback(new Error('只能输入正整数'));
+          } else if (num < 0 || num > 999) {
+            callback(new Error('最小参赛人数必须在0-999之间'));
+          } else {
+            callback();
+          }
+        },
+        trigger: 'blur'
+      }
+    ]
+  }
+});
+// 控制 Dialog 是否显示
+const levelsDialogVisible = ref(false);
+// 传递给子组件的参数
+const dialogParams = ref({
+  blindStructureId: null,
+  name: null
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询线下赛事列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listTournaments(queryParams.value);
+  tournamentsList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  resetFormPrize();
+  fileList.value = [];
+  fileList2.value = [];
+  // 清除预览图和临时链接
+  iconPreviewUrl.value = '';
+  competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+
+  iconPreviewUrl2.value = '';
+  competitionBg.value = '';
+  dialog.visible = false;
+};;
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  tournamentsFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: TournamentsVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  resetFormPrize();
+  fileList.value = [];
+  fileList2.value = [];
+  // 清除预览图和临时链接
+  iconPreviewUrl.value = '';
+  competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+  iconPreviewUrl2.value = '';
+  competitionBg.value = '';
+  dialog.visible = true;
+  dialog.title = '创建比赛';
+  dialog.mode = 'add'; // 设置模式
+};
+const resetFormPrize = () => {
+  // 清空 rewards 并添加默认项
+  formPrize.rewards.splice(0); // 清空数组
+  formPrize.rewards.push({ ...defaultRewardStructure }); // 添加初始项
+};
+/** 修改按钮操作 */
+const handleUpdate = async (row?: TournamentsVO, mode: 'edit' | 'view' = 'edit') => {
+  reset(); // 重置表单
+  const _id = row?.id || ids.value[0];
+  const res = await getTournaments(_id);
+  // 设置表单数据
+  Object.assign(form.value, res.data);
+  form.value.signTime = String(res.data.signTime);
+  form.value.gameType = String(res.data.gameType); // 转为字符串
+  competitionIcon.value = res.data.competitionIcon;
+  competitionBg.value = res.data.competitionBg;
+  // 处理奖励表单数据
+  const prizeItems = res.data.itemsPrizeList || [];
+  if (prizeItems.length > 0) {
+    formPrize.rewards = prizeItems.map((item, index) => ({
+      ranking: item.ranking || index + 1,
+      itemId: Number(item.itemId),
+      quantity: Number(item.quantity)
+    }));
+  } else {
+    formPrize.rewards = [
+      {
+        ranking: 1,
+        itemId: null,
+        quantity: null
+      }
+    ];
+  }
+  dialog.visible = true;
+  dialog.title = mode === 'view' ? '查看比赛' : '编辑比赛';
+  dialog.mode = mode; // 设置模式
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  tournamentsFormRef.value?.validate(async (valid: boolean) => {
+    // 校验奖励内容是否至少填写了一项
+    if (!formPrize.rewards.some((r) => r.itemId && r.quantity)) {
+      ElMessage.warning('请至少填写一项奖励内容');
+      return;
+    }
+
+    if (!valid) return;
+    try {
+      buttonLoading.value = true;
+
+      // 构造最终要提交的数据对象
+      const formData: TournamentsForm = {
+        ...data.form,
+        competitionIcon: data.form.competitionIcon, // 确保 iconUrl 存在
+        itemsPrizeList: formPrize.rewards.map((reward) => ({
+          ranking: Number(reward.ranking),
+          itemId: Number(reward.itemId),
+          quantity: Number(reward.quantity)
+        }))
+      };
+      // 提交数据(区分新增/编辑)
+      let response;
+      if (formData.id) {
+        response = await updateTournaments(formData);
+      } else {
+        response = await addTournaments(formData);
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    } catch (error) {
+      console.error('提交失败:', error);
+      proxy?.$modal.msgError('提交失败,请重试');
+    } finally {
+      buttonLoading.value = false;
+    }
+  });
+};
+/** 删除按钮操作 */
+const handleDelete = async (row?: TournamentsVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除赛事ID为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delTournaments(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'physical/tournaments/export',
+    {
+      ...queryParams.value
+    },
+    `大奖赛${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+  loadItemOptions();
+  loadItemStructuresOptions();
+});
+const itemOptions = ref<{ id: number; label: string }[]>([]);
+// 加载报名条件选项
+const loadItemOptions = async () => {
+  try {
+    const res = await selectItemsSelList();
+    if (res.code === 200) {
+      // 类型断言
+      const data = res.data as unknown as { id: number; name: string }[];
+
+      // 过滤掉 id === 2 的项,并映射为 { id, label }
+      itemOptions.value = data
+        .filter((item) => item.id !== 2) // ✅ 过滤 id 为 2 的
+        .map((item) => ({
+          id: item.id,
+          label: item.name
+        }));
+    } else {
+      ElMessage.error('加载失败:' + res.msg);
+    }
+  } catch (error) {
+    console.error('请求出错:', error);
+    ElMessage.error('请求失败,请检查网络');
+  }
+};
+// 下拉选项数据 selectPhysicalBlingStructuresInfo
+const itemOptionsStructures = ref<{ id: number; label: string }[]>([]);
+const loadItemStructuresOptions = async () => {
+  try {
+    const res = await selectPhysicalBlingStructuresInfo();
+    if (res.code === 200) {
+      // 使用 unknown 中间类型进行类型转换
+      const data = res.data as unknown as { id: number; name: string }[];
+      const list = [];
+      for (let i = 0; i < data.length; i++) {
+        const item = data[i];
+        list.push({
+          id: item.id,
+          label: item.name
+        });
+      }
+      itemOptionsStructures.value = list;
+    } else {
+      alert('加载失败:' + res.msg);
+    }
+  } catch (error) {
+    console.error('请求出错:', error);
+  }
+};
+
+// 下拉选项数据
+const itemOptionsStructuresLevel = ref<{ id: number; label: string }[]>([]);
+
+// 加载报名条件选项
+const handleBlindStructureChange = async (value: number) => {
+  //data.form.lateRegistrationLevel = null;
+  try {
+    const res = await selectPhysicalBlindLevelsById(value);
+    if (res.code === 200) {
+      // 使用 unknown 中间类型进行类型转换
+      const data2 = res.data as unknown as { id: number; levelNumber: number }[];
+      const list = [];
+      for (let i = 0; i < data2.length; i++) {
+        const item = data2[i];
+        list.push({
+          id: item.levelNumber,
+          label: item.levelNumber
+        });
+      }
+      itemOptionsStructuresLevel.value = list;
+      // 判断当前选择的 lateRegistrationLevel 是否在新列表中
+      const currentLevel = data.form.lateRegistrationLevel;
+      if (currentLevel && !list.some((item) => item.id === currentLevel)) {
+        data.form.lateRegistrationLevel = null;
+      }
+    } else {
+      alert('加载失败:' + res.msg);
+    }
+  } catch (error) {
+    console.error('请求出错:', error);
+  }
+};
+// 跳转到盲注表页面,并关闭当前弹窗
+const handleGoToStructures = () => {
+  dialog.visible = false; // 关闭弹窗
+  // 使用 nextTick 确保关闭动画完成后跳转(可选)
+  nextTick(() => {
+    proxy?.$router.push('/physical/blindStructures');
+  });
+};
+const handleViewLevels = () => {
+  const blindStructureId = data.form.blindStructureId;
+  if (blindStructureId === null || blindStructureId === undefined) {
+    ElMessage.warning('请先选择一个盲注等级');
+    return;
+  }
+  dialogParams.value.blindStructureId = blindStructureId;
+  /*  dialogParams.value.name = row.name;*/
+  levelsDialogVisible.value = true;
+};
+
+const formPrize = reactive({
+  // ...其他表单字段
+  rewards: [
+    {
+      ranking: 1,
+      itemId: null,
+      quantity: null
+    }
+  ]
+});
+// 用于保存原始的默认奖励结构
+const defaultRewardStructure = {
+  ranking: 1,
+  itemId: null,
+  quantity: null
+};
+
+const addReward = () => {
+  const currentLength = formPrize.rewards.length;
+
+  // 判断是否超过 itemOptions 的数量限制
+  /*  if (currentLength < itemOptions.value.length) {*/
+  formPrize.rewards.push({
+    ranking: currentLength + 1, // 自动生成排名,从 1 开始
+    itemId: null,
+    quantity: null
+  });
+  /*  } else {
+    ElMessage.warning(`最多只能添加 ${itemOptions.value.length} 个奖励项`);
+  }*/
+};
+
+const removeReward = (index: number) => {
+  formPrize.rewards.splice(index, 1);
+  // 删除后重新设置排名
+  formPrize.rewards.forEach((r, i) => {
+    r.ranking = i + 1;
+  });
+};
+// 获取奖励提示内容
+const getRewardTooltipContent = (rewards: any[]) => {
+  return rewards.map((prize) => `第${prize.ranking}名:${prize.quantity} ${prize.itemsName}`).join('\n');
+};
+//预览图标需要
+const iconPreviewUrl = ref('');
+const competitionIcon = ref('');
+const fileList = ref([]);
+const dialogVisible = ref(false);
+const previewSrc = ref('');
+const handleIconChange = async (file) => {
+  const index = fileList.value.findIndex((f) => f.uid === file.uid);
+  fileList.value = [];
+
+  if (file.raw) {
+    iconPreviewUrl.value = URL.createObjectURL(file.raw);
+  }
+
+  if (index === -1) {
+    // 如果文件不在列表中,则添加进去
+    fileList.value.push(file);
+  }
+
+  try {
+    const rawFile = file.raw;
+    const res = await uploadTournament(rawFile);
+    if (res.code === 200) {
+      // 更新文件状态为成功
+      fileList.value[index] = {
+        ...file,
+        status: 'success',
+        response: res.data.url
+      };
+      data.form.competitionIcon = fileList.value[index].response;
+      iconPreviewUrl.value = fileList.value[index].response;
+      ElMessage.success('上传成功');
+    } else {
+      throw new Error(res.msg);
+    }
+  } catch (error) {
+    // 更新文件状态为失败
+    fileList.value[index] = {
+      ...file,
+      status: 'fail',
+      error: '上传失败'
+    };
+    ElMessage.error('上传失败,请重试');
+  }
+};
+// 删除文件处理函数
+const handleIconRemove = (file, updatedFileList) => {
+  fileList.value = updatedFileList;
+  // 清除预览图和临时链接
+  iconPreviewUrl.value = '';
+  competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+};
+// 点击预览图触发放大
+const handlePreviewClick = () => {
+  const currentSrc = iconPreviewUrl.value || competitionIcon.value;
+  if (currentSrc) {
+    dialogVisible.value = true;
+  }
+};
+//预览图标需要
+const iconPreviewUrl2 = ref('');
+const competitionBg = ref('');
+const fileList2 = ref([]);
+const dialogVisible2 = ref(false);
+const previewSrc2 = ref('');
+const handleIconChange2 = async (file) => {
+  const index = fileList2.value.findIndex((f) => f.uid === file.uid);
+  fileList2.value = [];
+
+  if (file.raw) {
+    iconPreviewUrl2.value = URL.createObjectURL(file.raw);
+  }
+
+  if (index === -1) {
+    // 如果文件不在列表中,则添加进去
+    fileList2.value.push(file);
+  }
+
+  try {
+    const rawFile = file.raw;
+    const res = await uploadTournament(rawFile);
+    if (res.code === 200) {
+      // 更新文件状态为成功
+      fileList2.value[index] = {
+        ...file,
+        status: 'success',
+        response: res.data.url
+      };
+      data.form.competitionBg = fileList2.value[index].response;
+      iconPreviewUrl2.value = fileList2.value[index].response;
+      ElMessage.success('上传成功');
+    } else {
+      throw new Error(res.msg);
+    }
+  } catch (error) {
+    // 更新文件状态为失败
+    fileList2.value[index] = {
+      ...file,
+      status: 'fail',
+      error: '上传失败'
+    };
+    ElMessage.error('上传失败,请重试');
+  }
+};
+// 删除文件处理函数
+const handleIconRemove2 = (file, updatedFileList) => {
+  fileList2.value = updatedFileList;
+  // 清除预览图和临时链接
+  iconPreviewUrl2.value = '';
+  competitionBg.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+};
+// 点击预览图触发放大
+const handlePreviewClick2 = () => {
+  const currentSrc = iconPreviewUrl2.value || competitionBg.value;
+  if (currentSrc) {
+    dialogVisible2.value = true;
+  }
+};
+</script>