index.vue 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  1. <template>
  2. <div class="p-2">
  3. <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
  4. <div v-show="showSearch" class="mb-[20px]">
  5. <el-card shadow="hover">
  6. <el-form ref="queryFormRef" :model="queryParams" :inline="true">
  7. <el-form-item label="比赛ID" prop="name">
  8. <el-input v-model="queryParams.id" placeholder="请输入比赛ID" clearable @keyup.enter="handleQuery" />
  9. </el-form-item>
  10. <el-form-item label="比赛名称" prop="name">
  11. <el-input v-model="queryParams.name" placeholder="请输入比赛名称" clearable @keyup.enter="handleQuery" />
  12. </el-form-item>
  13. <el-form-item label="开始时间" prop="startTime">
  14. <el-date-picker clearable v-model="queryParams.startTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择比赛开始时间" />
  15. </el-form-item>
  16. <el-form-item>
  17. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  18. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  19. </el-form-item>
  20. </el-form>
  21. </el-card>
  22. </div>
  23. </transition>
  24. <el-card shadow="never">
  25. <template #header>
  26. <el-row :gutter="10" class="mb8">
  27. <el-col :span="1.5">
  28. <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['business:tournaments:add']">创立比赛</el-button>
  29. </el-col>
  30. <el-col :span="1.5">
  31. <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['business:tournaments:edit']"
  32. >编辑</el-button
  33. >
  34. </el-col>
  35. <!-- <el-col :span="1.5">
  36. <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['business:tournaments:remove']"
  37. >删除</el-button
  38. >
  39. </el-col>-->
  40. <!-- <el-col :span="1.5">
  41. <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['business:tournaments:export']">导出</el-button>
  42. </el-col>-->
  43. <!-- 新增的 “盲注管理” 按钮 -->
  44. <el-col :span="1.5">
  45. <el-button type="info" plain icon="Operation">
  46. <router-link to="/business/structures">盲注表管理</router-link>
  47. </el-button>
  48. </el-col>
  49. <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
  50. </el-row>
  51. </template>
  52. <el-table
  53. v-loading="loading"
  54. border
  55. :data="tournamentsList"
  56. @selection-change="handleSelectionChange"
  57. @sort-change="handleSortChange"
  58. :default-sort="{ prop: 'startTime', order: sortData.order }"
  59. >
  60. <el-table-column type="selection" width="55" align="center" />
  61. <el-table-column label="比赛ID" align="center" prop="id" />
  62. <el-table-column label="比赛名" align="center" prop="name" />
  63. <el-table-column label="比赛Logo" align="center">
  64. <template #default="scope">
  65. <el-image
  66. v-if="scope.row.competitionIcon"
  67. :src="scope.row.competitionIcon"
  68. style="width: 40px; height: 40px; border-radius: 4px; cursor: zoom-in"
  69. :preview-src-list="[scope.row.competitionIcon]"
  70. :preview-teleported="true"
  71. fit="cover"
  72. />
  73. <span v-else></span>
  74. </template>
  75. </el-table-column>
  76. <el-table-column label="开始时间" align="center" prop="startTime" width="150" sortable="custom"> </el-table-column>
  77. <el-table-column label="结束时间" align="center" prop="endTime" width="150"> </el-table-column>
  78. <el-table-column label="报名人数" align="center" prop="signNum" width="100"> </el-table-column>
  79. <el-table-column label="报名要求" align="center">
  80. <template #default="scope">
  81. {{
  82. scope.row.itemsName && scope.row.itemsNum
  83. ? `${scope.row.itemsName} x ${scope.row.itemsNum}`
  84. : scope.row.itemsName || scope.row.itemsNum || '—'
  85. }}
  86. </template>
  87. </el-table-column>
  88. <el-table-column label="报名截止盲注等级" align="center" prop="lateRegistrationLevel" />
  89. <el-table-column label="盲注表" align="center" prop="blindStructuresName" />
  90. <el-table-column label="奖励" align="center">
  91. <template #default="scope">
  92. <div v-for="(prize, index) in scope.row.itemsPrizeList" :key="index">
  93. 第{{ prize.ranking }}名:{{ prize.quantity }} {{ prize.itemsName }}
  94. </div>
  95. </template>
  96. </el-table-column>
  97. <el-table-column label="赛事状态" align="center" prop="statusText" />
  98. <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
  99. <template #default="scope">
  100. <div style="display: flex; flex-direction: column; gap: 5px">
  101. <el-tooltip content="查看" placement="top">
  102. <el-button link type="primary" icon="View" @click="openAuditDialog(scope.row.id, 'view')">查看</el-button>
  103. </el-tooltip>
  104. <el-tooltip content="编辑" placement="top">
  105. <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row, 'edit')">编辑</el-button>
  106. </el-tooltip>
  107. <el-tooltip content="复制" placement="top">
  108. <el-button link type="primary" icon="Copy" @click="handleCopy(scope.row)">复制</el-button>
  109. </el-tooltip>
  110. <el-tooltip content="领奖审核" placement="top">
  111. <el-button link type="primary" icon="Edit" @click="openAuditDialog(scope.row.id, 'audit')">领奖审核</el-button>
  112. </el-tooltip>
  113. <!-- <el-tooltip content="分配盲注" placement="top">
  114. <el-button link type="success" icon="Document" @click="openAssignDialog(scope.row)"> 分配 </el-button>
  115. </el-tooltip>-->
  116. <!-- <el-tooltip content="删除" placement="top">
  117. <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['business:tournaments:remove']"></el-button>
  118. </el-tooltip>-->
  119. </div>
  120. </template>
  121. </el-table-column>
  122. </el-table>
  123. <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
  124. </el-card>
  125. <!-- 添加或修改【请填写功能名称】对话框 -->
  126. <!-- <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
  127. <el-form ref="tournamentsFormRef" :model="form" :rules="rules" label-width="80px">
  128. <el-form-item label="赛事名称" prop="name">
  129. <el-input v-model="form.name" placeholder="请输入赛事名称" />
  130. </el-form-item>
  131. <el-form-item label="比赛开始时间" prop="startTime">
  132. <el-date-picker clearable v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择比赛开始时间">
  133. </el-date-picker>
  134. </el-form-item>
  135. <el-form-item label="起始记分牌数量" prop="startingChips">
  136. <el-input v-model="form.startingChips" placeholder="请输入起始记分牌数量" />
  137. </el-form-item>
  138. <el-form-item label="级别持续时间" prop="levelDuration">
  139. <el-input v-model="form.levelDuration" placeholder="请输入级别持续时间" />
  140. </el-form-item>
  141. <el-form-item label="截止报名级别" prop="lateRegistrationLevel">
  142. <el-input v-model="form.lateRegistrationLevel" placeholder="请输入截止报名级别" />
  143. </el-form-item>
  144. <el-form-item label="最大参赛人数" prop="maxPlayers">
  145. <el-input v-model="form.maxPlayers" placeholder="请输入最大参赛人数" />
  146. </el-form-item>
  147. </el-form>
  148. <template #footer>
  149. <div class="dialog-footer">
  150. <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
  151. <el-button @click="cancel">取 消</el-button>
  152. </div>
  153. </template>
  154. </el-dialog>-->
  155. <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="cancel">
  156. <el-form ref="tournamentsFormRef" :model="form" :rules="rules" label-width="120px">
  157. <!-- 赛事名称 -->
  158. <el-form-item label="比赛名称" prop="name">
  159. <el-input v-model="form.name" placeholder="请输入比赛名称" :disabled="dialog.mode === 'view'" />
  160. </el-form-item>
  161. <!-- 比赛图标 -->
  162. <el-form-item label="比赛图标" prop="icon">
  163. <div class="upload-container">
  164. <el-upload
  165. class="upload-icon"
  166. action="#"
  167. :on-change="handleIconChange"
  168. :on-remove="handleIconRemove"
  169. :file-list="fileList"
  170. :auto-upload="false"
  171. :limit="1"
  172. accept="image/*"
  173. :disabled="dialog.mode === 'view'"
  174. >
  175. <template #trigger>
  176. <el-button type="primary" :disabled="dialog.mode === 'view'">点击选择图标</el-button>
  177. </template>
  178. <!-- 预览图区域 -->
  179. <template #default>
  180. <div class="preview-area" @click="handlePreviewClick">
  181. <!-- 只有当 iconPreviewUrl 或 competitionIcon 存在时才显示 img -->
  182. <img
  183. v-if="iconPreviewUrl || competitionIcon"
  184. :src="iconPreviewUrl || competitionIcon"
  185. alt="预览图"
  186. style="max-width: 100px; max-height: 100px; margin-top: 10px; cursor: pointer"
  187. />
  188. <!-- 可选:无图时显示提示文字 -->
  189. <div v-else style="margin-top: 10px; color: #999"></div>
  190. </div>
  191. </template>
  192. <template #tip>
  193. <div class="el-upload__tip">
  194. <span v-if="fileList.length > 0">当前已选文件:{{ fileList[0].name }}</span>
  195. </div>
  196. </template>
  197. </el-upload>
  198. </div>
  199. </el-form-item>
  200. <!-- 开始时间 -->
  201. <el-form-item label="开始时间" prop="startTime">
  202. <el-date-picker
  203. clearable
  204. v-model="form.startTime"
  205. type="datetime"
  206. value-format="YYYY-MM-DD HH:mm:ss"
  207. placeholder="请选择开始时间"
  208. :disabled="dialog.mode === 'view'"
  209. />
  210. </el-form-item>
  211. <el-form-item label="比赛类型" prop="gameType">
  212. <el-select aria-required="true" v-model="form.gameType" placeholder="请选择" :disabled="dialog.mode === 'view'">
  213. <el-option v-for="dict in tournaments_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
  214. </el-select>
  215. </el-form-item>
  216. <el-form-item label="报名时间" prop="signTime">
  217. <el-select v-model="form.signTime" placeholder="请选择" :disabled="dialog.mode === 'view'">
  218. <el-option v-for="dict in tournaments_time" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
  219. </el-select>
  220. </el-form-item>
  221. <!-- 报名条件 -->
  222. <el-form-item label="报名条件" prop="itemsId">
  223. <div style="display: flex; align-items: center; gap: 10px">
  224. <el-select v-model="form.itemsId" placeholder="请选择道具类型" :disabled="dialog.mode === 'view'">
  225. <el-option v-for="item in itemOptions" :key="item.id" :label="item.label" :value="item.id" />
  226. </el-select>
  227. <el-input v-model="form.itemsNum" :min="1" placeholder="数量" :disabled="dialog.mode === 'view'" />
  228. </div>
  229. </el-form-item>
  230. <!-- 盲注表 -->
  231. <el-form-item label="盲注表" prop="blindStructureId">
  232. <div style="display: flex; align-items: center">
  233. <el-select
  234. v-model="form.blindStructureId"
  235. placeholder="选项"
  236. style="width: 200px"
  237. @change="handleBlindStructureChange"
  238. :disabled="dialog.mode === 'view'"
  239. >
  240. <el-option v-for="item in itemOptionsStructures" :key="item.id" :label="item.label" :value="item.id" />
  241. </el-select>
  242. <el-button type="primary" :disabled="dialog.mode === 'view'">上传新盲注</el-button>
  243. <el-button type="primary" @click="handleViewLevels" :disabled="dialog.mode === 'view'">预览</el-button>
  244. </div>
  245. </el-form-item>
  246. <!-- 报名截止等级 -->
  247. <el-form-item label="报名截止等级" prop="lateRegistrationLevel">
  248. <el-select v-model="form.lateRegistrationLevel" placeholder="选项" style="width: 200px" :disabled="dialog.mode === 'view'">
  249. <el-option v-for="item in itemOptionsStructuresLevel" :key="item.id" :label="item.label" :value="item.id" />
  250. </el-select>
  251. </el-form-item>
  252. <!-- 奖励内容 -->
  253. <el-form-item label="奖励内容">
  254. <div v-for="(reward, index) in formPrize.rewards" :key="index" style="display: flex; align-items: center; margin-bottom: 8px">
  255. <span>第{{ reward.ranking }}名</span>
  256. <!-- 道具选择 -->
  257. <el-select v-model="reward.itemId" placeholder="选项" style="width: 120px; margin-right: 8px" :disabled="dialog.mode === 'view'">
  258. <el-option v-for="item in itemOptions" :key="item.id" :label="item.label" :value="item.id" />
  259. </el-select>
  260. <!-- 数量输入 -->
  261. <el-input v-model="reward.quantity" placeholder="请输入数量" style="width: 100px; margin-right: 8px" :disabled="dialog.mode === 'view'" />
  262. <!-- 操作按钮 -->
  263. <el-button type="primary" @click="addReward" v-if="index === 0 && dialog.mode !== 'view'">+</el-button>
  264. <el-button type="primary" @click="removeReward(index)" v-if="index !== 0 && dialog.mode !== 'view'">-</el-button>
  265. </div>
  266. </el-form-item>
  267. </el-form>
  268. <template #footer>
  269. <div class="dialog-footer">
  270. <el-button :loading="buttonLoading" type="primary" @click="submitForm" v-if="dialog.mode !== 'view'">确 定</el-button>
  271. <el-button @click="cancel">取 消</el-button>
  272. </div>
  273. </template>
  274. </el-dialog>
  275. <!-- 分配模态框 -->
  276. <el-dialog title="分配盲注信息" v-model="assignDialog.visible" width="800px" append-to-body destroy-on-close>
  277. <el-form ref="assignFormRef" :model="assignForm" label-width="100px">
  278. <!-- 可勾选的列表 -->
  279. <el-form-item label="">
  280. <div style="display: flex; flex-direction: column; align-items: center; width: 100%">
  281. <div style="width: 100%; height: 400px; overflow-y: auto; padding: 0">
  282. <el-table border ref="assignTable" :data="assignList" :row-key="'blindStructuresId'" style="width: 100%">
  283. <el-table-column label="操作" align="center" width="55">
  284. <template #default="{ row }">
  285. <el-radio v-model="selectedRadio" :label="row.blindStructuresId">&nbsp;</el-radio>
  286. </template>
  287. </el-table-column>
  288. <!-- <el-table-column label="blindStructuresId" align="center" prop="blindStructuresId" width="100" show-overflow-tooltip />-->
  289. <el-table-column prop="blindStructuresName" label="盲注结构名称" align="center" width="150" show-overflow-tooltip />
  290. <el-table-column prop="blindDescription" label="盲注结构描述" align="center" width="200" show-overflow-tooltip />
  291. <el-table-column prop="allocationStatus" label="绑定情况" align="center" width="100">
  292. <template #default="scope">
  293. {{ scope.row.allocationStatus === 0 ? '未绑定' : '已绑定' }}
  294. </template>
  295. </el-table-column>
  296. <el-table-column prop="tournamentsName" label="赛事名称" align="center" width="150" show-overflow-tooltip />
  297. </el-table>
  298. </div>
  299. <!-- 分页(如果数据量大) -->
  300. <div style="display: flex; justify-content: center; margin-top: 20px; width: 100%">
  301. <pagination
  302. v-if="assignTotal > 0"
  303. :total="assignTotal"
  304. v-model:page="assignQuery.pageNum"
  305. v-model:limit="assignQuery.pageSize"
  306. @pagination="getAssignList"
  307. />
  308. </div>
  309. </div>
  310. </el-form-item>
  311. </el-form>
  312. <template #footer>
  313. <div class="dialog-footer" style="text-align: center">
  314. <el-button :loading="buttonLoading" type="primary" @click="submitAssign">保 存</el-button>
  315. <el-button @click="assignDialog.visible = false">取 消</el-button>
  316. </div>
  317. </template>
  318. </el-dialog>
  319. <el-dialog v-model="levelsDialogVisible" title="盲注等级列表" width="80%">
  320. <!-- 使用 component 动态加载目标组件 -->
  321. <levels-index :blind-structure-id="dialogParams.blindStructureId" :name="dialogParams.name" />
  322. </el-dialog>
  323. <!-- 预览弹窗 -->
  324. <el-dialog v-model="dialogVisible" title="图片预览" width="50%">
  325. <img :src="previewSrc || iconPreviewUrl || competitionIcon" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
  326. </el-dialog>
  327. <el-dialog
  328. :title="`${tournamentInfo.name} ${tournamentInfo.tournamentsBiId}`"
  329. v-model="auditDialog.visible"
  330. width="1000px"
  331. append-to-body
  332. @close="cancelAudit"
  333. >
  334. <!-- 查看对局记录按钮 -->
  335. <div class="dialog-header-actions" v-if="auditDialog.mode === 'view'">
  336. <el-button type="primary">查看对局记录</el-button>
  337. </div>
  338. <!-- 比赛信息展示 -->
  339. <div class="tournament-info" v-if="tournamentInfo.id">
  340. <p>比赛时间:{{ tournamentInfo.startTime }} ~ {{ tournamentInfo.endTime }}</p>
  341. <p>参加人数:{{ tournamentInfo.signNum }}人</p>
  342. <p v-if="auditDialog.mode === 'view'">比赛类型:{{ tournamentInfo.id }}</p>
  343. <p v-if="auditDialog.mode === 'view'">报名条件:{{ tournamentInfo.itemsName }} x {{ tournamentInfo.itemsNum }}</p>
  344. <p v-if="auditDialog.mode === 'view'">
  345. 盲注表:{{ tournamentInfo.blindStructuresName }}
  346. <el-button type="primary" @click="handleViewLevelsSee(tournamentInfo)">预览</el-button>
  347. </p>
  348. <p v-if="auditDialog.mode === 'view'">报名截止等级:{{ tournamentInfo.lateRegistrationLevel }}</p>
  349. </div>
  350. <!-- 表格内容 -->
  351. <el-table :data="auditData" border style="width: 100%">
  352. <el-table-column prop="id" label="id" align="center" v-if="false"></el-table-column>
  353. <el-table-column prop="rank" label="排名" align="center"></el-table-column>
  354. <el-table-column prop="playerName" label="用户名" align="center"></el-table-column>
  355. <el-table-column prop="phone" label="手机" align="center"></el-table-column>
  356. <!-- <el-table-column prop="tournamentId" label="tournamentId" align="center"></el-table-column>
  357. <el-table-column prop="playerId" label="playerId" align="center"></el-table-column>-->
  358. <el-table-column label="奖励" align="center">
  359. <template #default="scope">
  360. <div v-for="(prize, index) in scope.row.rewardVoList" :key="index">{{ prize.itemName }} {{ prize.quantity }}</div>
  361. </template>
  362. </el-table-column>
  363. <el-table-column prop="claimedText" label="状态" align="center"></el-table-column>
  364. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  365. <template #default="scope">
  366. <el-tooltip content="查看牌局" placement="top">
  367. <el-button link type="primary" icon="See" @click="handleViewHistory(scope.row)">查看牌局</el-button>
  368. </el-tooltip>
  369. <el-tooltip content="审核" placement="top">
  370. <el-button link type="primary" icon="Audit" @click="handleAudit(scope.row)">审核</el-button>
  371. </el-tooltip>
  372. <el-tooltip content="移除" placement="top">
  373. <el-button link type="primary" icon="Delete" @click="handleClaimsDelete(scope.row)">移除</el-button>
  374. </el-tooltip>
  375. </template>
  376. </el-table-column>
  377. </el-table>
  378. <!-- 分页组件 -->
  379. <div class="pagination-container" style="margin-top: 20px; text-align: center">
  380. <el-pagination
  381. v-model:current-page="auditQueryParams.pageNum"
  382. v-model:page-size="auditQueryParams.pageSize"
  383. :total="auditTotal"
  384. layout="prev, pager, next"
  385. @current-change="getAuditData"
  386. />
  387. </div>
  388. <!-- 对话框底部按钮 -->
  389. <template #footer>
  390. <div class="dialog-footer">
  391. <el-button @click="cancelAudit">取 消</el-button>
  392. </div>
  393. </template>
  394. </el-dialog>
  395. <el-dialog title="移除获奖名额" v-model="removeDialog.visible" width="30%">
  396. <span>是否移除用户:{{ removeDialog.playerName }}{{ removeDialog.phone }} 在本次比赛中的获奖名额?</span>
  397. <template #footer>
  398. <span class="dialog-footer">
  399. <el-button @click="cancelRemove">取消</el-button>
  400. <el-button type="danger" @click="confirmRemove">确认移除</el-button>
  401. </span>
  402. </template>
  403. </el-dialog>
  404. </div>
  405. </template>
  406. <script setup name="Tournaments" lang="ts">
  407. import {
  408. listTournaments,
  409. getTournaments,
  410. delTournaments,
  411. addTournaments,
  412. updateTournaments,
  413. getSelectTournamentBlindStructuresList,
  414. assignTournamentBlindStructures,
  415. uploadTournament
  416. } from '@/api/system/business/tournaments';
  417. import { selectItemsSelList } from '@/api/system/business/items';
  418. import { selectBlindLevelsById } from '@/api/system/business/levels';
  419. import { selectBlingStructuresInfo } from '@/api/system/business/structures';
  420. import { listClaims, deleteClaim, auditSendReward } from '@/api/system/business/claims';
  421. import { TournamentsVO, TournamentsQuery, TournamentsForm, TournamentsBindStructuresVO } from '@/api/system/business/tournaments/types';
  422. import { ref } from 'vue';
  423. import LevelsIndex from '@/views/system/business/levels/index.vue';
  424. import { ClaimsVO } from '@/api/system/business/claims/types';
  425. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  426. const { tournaments_type, tournaments_time } = toRefs<any>(proxy?.useDict('tournaments_type', 'tournaments_time'));
  427. const tournamentsList = ref<TournamentsVO[]>([]);
  428. const buttonLoading = ref(false);
  429. const loading = ref(true);
  430. const showSearch = ref(true);
  431. const ids = ref<Array<string | number>>([]);
  432. const single = ref(true);
  433. const multiple = ref(true);
  434. const total = ref(0);
  435. const queryFormRef = ref<ElFormInstance>();
  436. const tournamentsFormRef = ref<ElFormInstance>();
  437. const iconFile = ref<File | null>(null); // 存储选中的图标文件
  438. const dialog = reactive<{
  439. visible: boolean;
  440. title: string;
  441. mode: 'edit' | 'view'; // 新增字段
  442. }>({
  443. visible: false,
  444. title: '',
  445. mode: 'edit'
  446. });
  447. const assignDialog = reactive({
  448. visible: false,
  449. title: '分配盲注'
  450. });
  451. const assignForm = reactive({
  452. tournamentId: undefined as number | undefined,
  453. selectedIds: [] as number[]
  454. });
  455. const dialogVisible = ref(false);
  456. const previewSrc = ref('');
  457. function openPreview(src: string) {
  458. previewSrc.value = src;
  459. dialogVisible.value = true;
  460. }
  461. // 控制 Dialog 是否显示
  462. const levelsDialogVisible = ref(false);
  463. // 传递给子组件的参数
  464. const dialogParams = ref({
  465. blindStructureId: null,
  466. name: null
  467. });
  468. // 下拉选项数据 selectBlingStructuresInfo
  469. const itemOptions = ref<{ id: number; label: string }[]>([]);
  470. // 加载报名条件选项
  471. const loadItemOptions = async () => {
  472. try {
  473. const res = await selectItemsSelList();
  474. if (res.code === 200) {
  475. // 使用 unknown 中间类型进行类型转换
  476. const data = res.data as unknown as { id: number; name: string }[];
  477. const list = [];
  478. for (let i = 0; i < data.length; i++) {
  479. const item = data[i];
  480. list.push({
  481. id: item.id,
  482. label: item.name
  483. });
  484. }
  485. itemOptions.value = list;
  486. } else {
  487. alert('加载失败:' + res.msg);
  488. }
  489. } catch (error) {
  490. console.error('请求出错:', error);
  491. }
  492. };
  493. // 下拉选项数据 selectBlingStructuresInfo
  494. const itemOptionsStructures = ref<{ id: number; label: string }[]>([]);
  495. // 加载报名条件选项
  496. const loadItemStructuresOptions = async () => {
  497. try {
  498. const res = await selectBlingStructuresInfo();
  499. if (res.code === 200) {
  500. // 使用 unknown 中间类型进行类型转换
  501. const data = res.data as unknown as { id: number; name: string }[];
  502. const list = [];
  503. for (let i = 0; i < data.length; i++) {
  504. const item = data[i];
  505. list.push({
  506. id: item.id,
  507. label: item.name
  508. });
  509. }
  510. itemOptionsStructures.value = list;
  511. } else {
  512. alert('加载失败:' + res.msg);
  513. }
  514. } catch (error) {
  515. console.error('请求出错:', error);
  516. }
  517. };
  518. // 下拉选项数据
  519. const itemOptionsStructuresLevel = ref<{ id: number; label: string }[]>([]);
  520. // 加载报名条件选项
  521. const handleBlindStructureChange = async (value: number) => {
  522. try {
  523. const res = await selectBlindLevelsById(value);
  524. if (res.code === 200) {
  525. // 使用 unknown 中间类型进行类型转换
  526. const data = res.data as unknown as { id: number; levelNumber: number }[];
  527. const list = [];
  528. for (let i = 0; i < data.length; i++) {
  529. const item = data[i];
  530. list.push({
  531. id: item.levelNumber,
  532. label: item.levelNumber
  533. });
  534. }
  535. itemOptionsStructuresLevel.value = list;
  536. } else {
  537. alert('加载失败:' + res.msg);
  538. }
  539. } catch (error) {
  540. console.error('请求出错:', error);
  541. }
  542. };
  543. const formPrize = reactive({
  544. // ...其他表单字段
  545. rewards: [
  546. {
  547. ranking: 1,
  548. itemId: null,
  549. quantity: null
  550. }
  551. ]
  552. });
  553. // 用于保存原始的默认奖励结构
  554. const defaultRewardStructure = {
  555. ranking: 1,
  556. itemId: null,
  557. quantity: null
  558. };
  559. const addReward = () => {
  560. const currentLength = formPrize.rewards.length;
  561. // 判断是否超过 itemOptions 的数量限制
  562. if (currentLength < itemOptions.value.length) {
  563. formPrize.rewards.push({
  564. ranking: currentLength + 1, // 自动生成排名,从 1 开始
  565. itemId: null,
  566. quantity: null
  567. });
  568. } else {
  569. ElMessage.warning(`最多只能添加 ${itemOptions.value.length} 个奖励项`);
  570. }
  571. };
  572. const removeReward = (index: number) => {
  573. formPrize.rewards.splice(index, 1);
  574. // 删除后重新设置排名
  575. formPrize.rewards.forEach((r, i) => {
  576. r.ranking = i + 1;
  577. });
  578. };
  579. //预览图标需要
  580. const iconPreviewUrl = ref('');
  581. const competitionIcon = ref('');
  582. const fileList = ref([]);
  583. // 单选控制变量(用于绑定 el-radio)
  584. const selectedRadio = ref<number | null>(null);
  585. const assignList = ref<TournamentsBindStructuresVO[]>([]);
  586. const assignSelectedList = ref<TournamentsBindStructuresVO[]>([]);
  587. const assignTotal = ref(0);
  588. const assignQuery = reactive<{
  589. pageNum: number;
  590. pageSize: number;
  591. tournamentId: number | undefined;
  592. }>({
  593. pageNum: 1,
  594. pageSize: 10,
  595. tournamentId: undefined
  596. });
  597. const initFormData: TournamentsForm = {
  598. id: undefined,
  599. name: undefined,
  600. startTime: undefined,
  601. gameType: undefined,
  602. startingChips: undefined,
  603. levelDuration: undefined,
  604. lateRegistrationLevel: undefined,
  605. maxPlayers: undefined,
  606. status: undefined,
  607. createdAt: undefined,
  608. updatedAt: undefined,
  609. signTime: undefined,
  610. competitionIcon: null,
  611. itemsId: null,
  612. itemsNum: null,
  613. blindStructureId: null,
  614. itemsPrizeList: []
  615. };
  616. const data = reactive<PageData<TournamentsForm, TournamentsQuery>>({
  617. form: { ...initFormData },
  618. queryParams: {
  619. pageNum: 1,
  620. pageSize: 10,
  621. name: undefined,
  622. startTime: undefined,
  623. gameType: undefined,
  624. startingChips: undefined,
  625. levelDuration: undefined,
  626. lateRegistrationLevel: undefined,
  627. maxPlayers: undefined,
  628. status: undefined,
  629. createdAt: undefined,
  630. updatedAt: undefined,
  631. id: undefined,
  632. params: {}
  633. },
  634. rules: {
  635. id: [{ required: true, message: '不能为空', trigger: 'blur' }],
  636. name: [{ required: true, message: '赛事名称不能为空', trigger: 'blur' }],
  637. startTime: [{ required: true, message: '比赛开始时间不能为空', trigger: 'blur' }],
  638. gameType: [{ required: true, message: '游戏类型不能为空', trigger: 'change' }],
  639. lateRegistrationLevel: [{ required: true, message: '截止报名级别不能为空', trigger: 'change' }],
  640. signTime: [{ required: true, message: '报名时间不能为空', trigger: 'change' }],
  641. itemsId: [{ required: true, message: '报名条件不能为空', trigger: 'change' }],
  642. blindStructureId: [{ required: true, message: '盲注表不能为空', trigger: 'change' }]
  643. }
  644. });
  645. const { queryParams, form, rules } = toRefs(data);
  646. /** 查询【请填写功能名称】列表 */
  647. const getList = async () => {
  648. loading.value = true;
  649. const params = {
  650. ...queryParams.value,
  651. sortBy: sortData.value.prop,
  652. isAsc: sortData.value.order === 'ascending'
  653. };
  654. const res = await listTournaments(params);
  655. tournamentsList.value = res.rows;
  656. total.value = res.total;
  657. loading.value = false;
  658. };
  659. /** 取消按钮 */
  660. const cancel = () => {
  661. reset();
  662. resetFormPrize();
  663. fileList.value = [];
  664. // 清除预览图和临时链接
  665. iconPreviewUrl.value = '';
  666. competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
  667. dialog.visible = false;
  668. };
  669. /** 表单重置 */
  670. const reset = () => {
  671. form.value = { ...initFormData };
  672. tournamentsFormRef.value?.resetFields();
  673. };
  674. /** 搜索按钮操作 */
  675. const handleQuery = () => {
  676. queryParams.value.pageNum = 1;
  677. getList();
  678. };
  679. /** 重置按钮操作 */
  680. const resetQuery = () => {
  681. queryFormRef.value?.resetFields();
  682. handleQuery();
  683. };
  684. /** 多选框选中数据 */
  685. const handleSelectionChange = (selection: TournamentsVO[]) => {
  686. ids.value = selection.map((item) => item.id);
  687. single.value = selection.length != 1;
  688. multiple.value = !selection.length;
  689. };
  690. /** 新增按钮操作 */
  691. const handleAdd = () => {
  692. reset();
  693. resetFormPrize();
  694. fileList.value = [];
  695. // 清除预览图和临时链接
  696. iconPreviewUrl.value = '';
  697. competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
  698. dialog.visible = true;
  699. dialog.title = '创建比赛';
  700. dialog.mode = 'edit'; // 设置模式
  701. };
  702. // 重置函数
  703. const resetFormPrize = () => {
  704. // 清空 rewards 并添加默认项
  705. formPrize.rewards.splice(0); // 清空数组
  706. formPrize.rewards.push({ ...defaultRewardStructure }); // 添加初始项
  707. };
  708. /** 修改按钮操作 */
  709. const handleUpdate = async (row?: TournamentsVO, mode: 'edit' | 'view' = 'edit') => {
  710. reset(); // 重置表单
  711. const _id = row?.id || ids.value[0];
  712. const res = await getTournaments(_id);
  713. // 确保 gameType 是 number 类型
  714. const gameType = Number(res.data.gameType);
  715. const signTime = Number(res.data.signTime);
  716. // 设置表单数据
  717. Object.assign(form.value, res.data);
  718. form.value.gameType = gameType; // 确保赋值正确
  719. form.value.signTime = signTime;
  720. competitionIcon.value = res.data.competitionIcon;
  721. // 处理奖励表单数据
  722. const prizeItems = res.data.itemsPrizeList || [];
  723. if (prizeItems.length > 0) {
  724. formPrize.rewards = prizeItems.map((item, index) => ({
  725. ranking: item.ranking || index + 1,
  726. itemId: Number(item.itemId),
  727. quantity: Number(item.quantity)
  728. }));
  729. } else {
  730. formPrize.rewards = [
  731. {
  732. ranking: 1,
  733. itemId: null,
  734. quantity: null
  735. }
  736. ];
  737. }
  738. dialog.visible = true;
  739. dialog.title = mode === 'view' ? '查看比赛' : '编辑比赛';
  740. dialog.mode = mode; // 设置模式
  741. };
  742. /** 提交按钮 */
  743. const submitForm = () => {
  744. tournamentsFormRef.value?.validate(async (valid: boolean) => {
  745. // 校验奖励内容是否至少填写了一项
  746. if (!formPrize.rewards.some((r) => r.itemId && r.quantity)) {
  747. ElMessage.warning('请至少填写一项奖励内容');
  748. return;
  749. }
  750. if (!valid) return;
  751. try {
  752. buttonLoading.value = true;
  753. // 构造最终要提交的数据对象
  754. const formData: TournamentsForm = {
  755. ...data.form,
  756. competitionIcon: data.form.competitionIcon, // 确保 iconUrl 存在
  757. itemsPrizeList: formPrize.rewards.map((reward) => ({
  758. ranking: Number(reward.ranking),
  759. itemId: Number(reward.itemId),
  760. quantity: Number(reward.quantity)
  761. }))
  762. };
  763. // 提交数据(区分新增/编辑)
  764. let response;
  765. if (formData.id) {
  766. response = await updateTournaments(formData);
  767. } else {
  768. response = await addTournaments(formData);
  769. }
  770. proxy?.$modal.msgSuccess('操作成功');
  771. dialog.visible = false;
  772. await getList();
  773. } catch (error) {
  774. console.error('提交失败:', error);
  775. proxy?.$modal.msgError('提交失败,请重试');
  776. } finally {
  777. buttonLoading.value = false;
  778. }
  779. });
  780. };
  781. /** 删除按钮操作 */
  782. const handleDelete = async (row?: TournamentsVO) => {
  783. const _ids = row?.id || ids.value;
  784. await proxy?.$modal.confirm('是否确认删除【赛事信息】编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
  785. await delTournaments(_ids);
  786. proxy?.$modal.msgSuccess('删除成功');
  787. await getList();
  788. };
  789. /** 导出按钮操作 */
  790. const handleExport = () => {
  791. proxy?.download(
  792. 'business/tournaments/export',
  793. {
  794. ...queryParams.value
  795. },
  796. `赛事列表_${new Date().getTime()}.xlsx`
  797. );
  798. };
  799. onMounted(() => {
  800. getList();
  801. loadItemOptions();
  802. loadItemStructuresOptions();
  803. });
  804. async function getAssignList() {
  805. const res = await getSelectTournamentBlindStructuresList(assignQuery);
  806. assignList.value = res.rows;
  807. assignTotal.value = res.total;
  808. }
  809. async function openAssignDialog(row: TournamentsVO) {
  810. assignForm.tournamentId = Number(row.id);
  811. assignQuery.tournamentId = Number(row.id); // 这里明确转成 number
  812. // 加载盲注列表
  813. assignDialog.visible = true;
  814. // 请求分配盲注结构列表
  815. getAssignList().then(() => {
  816. nextTick(() => {
  817. initSelectedRows(); // 等待 DOM 完全渲染后再初始化选中状态
  818. });
  819. });
  820. }
  821. function handleAssignSelectionChange(selection: TournamentsBindStructuresVO[]) {
  822. assignSelectedList.value = selection;
  823. assignForm.selectedIds = selection.map((item) => item.blindStructuresId);
  824. }
  825. async function submitAssign() {
  826. if (!assignForm.tournamentId || assignForm.selectedIds.length === 0) {
  827. proxy?.$modal.msgError('请选择至少一个盲注结构');
  828. return;
  829. }
  830. buttonLoading.value = true;
  831. try {
  832. await assignTournamentBlindStructures({
  833. tournamentId: assignForm.tournamentId,
  834. blindStructureIds: assignForm.selectedIds
  835. });
  836. proxy?.$modal.msgSuccess('分配成功');
  837. assignDialog.visible = false;
  838. } catch (err) {
  839. proxy?.$modal.msgError('分配失败');
  840. } finally {
  841. buttonLoading.value = false;
  842. }
  843. }
  844. const assignTable = ref(); // 对应 el-table 的 ref
  845. // 初始化选中行
  846. function initSelectedRows() {
  847. const selected = assignList.value.find((item) => item.allocationStatus === 1);
  848. if (selected) {
  849. selectedRadio.value = selected.blindStructuresId;
  850. assignForm.selectedIds = [selected.blindStructuresId];
  851. }
  852. }
  853. watch(
  854. () => assignList.value,
  855. (newVal) => {
  856. if (newVal.length > 0) {
  857. nextTick(() => {
  858. initSelectedRows();
  859. });
  860. }
  861. },
  862. { deep: true, immediate: true }
  863. );
  864. watch(
  865. () => selectedRadio.value,
  866. (newVal) => {
  867. if (newVal !== null) {
  868. assignForm.selectedIds = [newVal];
  869. } else {
  870. assignForm.selectedIds = [];
  871. }
  872. }
  873. );
  874. const handleIconChange = async (file) => {
  875. const index = fileList.value.findIndex((f) => f.uid === file.uid);
  876. fileList.value = [];
  877. if (file.raw) {
  878. iconPreviewUrl.value = URL.createObjectURL(file.raw);
  879. }
  880. if (index === -1) {
  881. // 如果文件不在列表中,则添加进去
  882. fileList.value.push(file);
  883. }
  884. try {
  885. const rawFile = file.raw;
  886. const res = await uploadTournament(rawFile);
  887. if (res.code === 200) {
  888. // 更新文件状态为成功
  889. fileList.value[index] = {
  890. ...file,
  891. status: 'success',
  892. response: res.data.url
  893. };
  894. data.form.competitionIcon = fileList.value[index].response;
  895. iconPreviewUrl.value = fileList.value[index].response;
  896. ElMessage.success('上传成功');
  897. } else {
  898. throw new Error(res.msg);
  899. }
  900. } catch (error) {
  901. // 更新文件状态为失败
  902. fileList.value[index] = {
  903. ...file,
  904. status: 'fail',
  905. error: '上传失败'
  906. };
  907. ElMessage.error('上传失败,请重试');
  908. }
  909. };
  910. const handleViewLevels = () => {
  911. const blindStructureId = data.form.blindStructureId;
  912. if (blindStructureId === null || blindStructureId === undefined) {
  913. ElMessage.warning('请先选择一个盲注表');
  914. return;
  915. }
  916. dialogParams.value.blindStructureId = blindStructureId;
  917. /* dialogParams.value.name = row.name;*/
  918. levelsDialogVisible.value = true;
  919. };
  920. // 点击预览图触发放大
  921. const handlePreviewClick = () => {
  922. const currentSrc = iconPreviewUrl.value || competitionIcon.value;
  923. if (currentSrc) {
  924. dialogVisible.value = true;
  925. }
  926. };
  927. // 删除文件处理函数
  928. const handleIconRemove = (file, updatedFileList) => {
  929. fileList.value = updatedFileList;
  930. // 清除预览图和临时链接
  931. iconPreviewUrl.value = '';
  932. competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
  933. };
  934. // 排序字段和顺序
  935. const sortData = ref({
  936. prop: 'startTime',
  937. order: 'descending' as 'ascending' | 'descending' | null
  938. });
  939. /**
  940. * 处理排序变化
  941. */
  942. const handleSortChange = ({ prop, order }) => {
  943. if (prop === 'startTime') {
  944. sortData.value = {
  945. prop,
  946. order
  947. };
  948. // 触发重新加载数据
  949. getList();
  950. }
  951. };
  952. const auditDialog = ref({
  953. visible: false,
  954. title: '',
  955. mode: 'view' as 'view' | 'audit'
  956. });
  957. const tournamentInfo = ref<Partial<TournamentsVO>>({
  958. id: '',
  959. startTime: '',
  960. endTime: '',
  961. itemsNum: 0,
  962. itemsName: '',
  963. blindStructureId: 0,
  964. blindStructuresName: '',
  965. lateRegistrationLevel: 0,
  966. tournamentsBiId: '',
  967. signNum: 0
  968. });
  969. const auditData = ref<ClaimsVO[]>([]);
  970. // 创建新的查询参数对象,不影响原始 queryParams
  971. const auditQueryParams = {
  972. pageNum: 1,
  973. pageSize: 10,
  974. tournamentId: null as number | null
  975. };
  976. const auditTotal = ref(0);
  977. // 模拟打开对话框并获取数据
  978. const openAuditDialog = async (row: TournamentsVO | number, mode: 'view' | 'audit' = 'view') => {
  979. auditDialog.value.visible = true;
  980. auditDialog.value.mode = mode; // 设置模式
  981. let tournamentId: number;
  982. if (typeof row === 'number') {
  983. tournamentId = row;
  984. }
  985. auditQueryParams.tournamentId = tournamentId;
  986. await getAuditData(1);
  987. try {
  988. const res = await getTournaments(tournamentId);
  989. if (res.code === 200) {
  990. tournamentInfo.value = res.data;
  991. }
  992. } catch (error) {
  993. console.error('获取数据失败', error);
  994. }
  995. };
  996. const getAuditData = async (pageNum: number) => {
  997. try {
  998. auditQueryParams.pageNum = pageNum;
  999. const res = await listClaims(auditQueryParams);
  1000. auditData.value = res.rows;
  1001. auditTotal.value = res.total;
  1002. } catch (error) {
  1003. console.error('获取审核数据失败', error);
  1004. }
  1005. };
  1006. // 关闭对话框
  1007. const cancelAudit = () => {
  1008. auditDialog.value.visible = false;
  1009. /*tournamentInfo.value = {};
  1010. auditData.value = [];*/
  1011. };
  1012. const removeDialog = ref({
  1013. visible: false,
  1014. id: null,
  1015. tournamentId: null,
  1016. playerName: '',
  1017. phone: ''
  1018. });
  1019. const handleClaimsDelete = (row: ClaimsVO) => {
  1020. removeDialog.value.visible = true;
  1021. removeDialog.value.id = row.id; // 假设 row 中有 id 字段
  1022. removeDialog.value.tournamentId = row.tournamentId; // 假设 row 中有 tournamentId 字段
  1023. removeDialog.value.playerName = row.playerName; // 假设 row 中有 playerName 字段
  1024. removeDialog.value.phone = row.phone;
  1025. };
  1026. const cancelRemove = () => {
  1027. removeDialog.value.visible = false;
  1028. };
  1029. const confirmRemove = async () => {
  1030. try {
  1031. const res = await deleteClaim({
  1032. id: removeDialog.value.id,
  1033. tournamentId: removeDialog.value.tournamentId
  1034. });
  1035. if (res.code === 200) {
  1036. ElMessage.success('移除成功');
  1037. // 重新加载审核数据
  1038. const auditQueryParams = {
  1039. pageNum: 1,
  1040. pageSize: 10,
  1041. tournamentId: tournamentInfo.value.id
  1042. };
  1043. const res2 = await listClaims(auditQueryParams);
  1044. auditData.value = res2.rows;
  1045. } else {
  1046. ElMessage.error(res.msg);
  1047. }
  1048. } catch (error) {
  1049. ElMessage.error('移除失败,请重试');
  1050. } finally {
  1051. removeDialog.value.visible = false;
  1052. }
  1053. };
  1054. // 审核操作
  1055. const handleAudit = async (row: ClaimsVO) => {
  1056. try {
  1057. await ElMessageBox.confirm('确定要审核该记录吗?', '提示', {
  1058. confirmButtonText: '确定',
  1059. cancelButtonText: '取消',
  1060. type: 'warning'
  1061. });
  1062. // 用户点击了【确定】
  1063. await auditSendReward(row); // 调用接口,假设接口接受 id 参数
  1064. ElMessage.success('审核成功');
  1065. getList(); // 刷新列表
  1066. } catch (error) {
  1067. if (error === 'cancel') {
  1068. ElMessage.info('已取消审核');
  1069. } else {
  1070. ElMessage.error('审核失败');
  1071. }
  1072. }
  1073. };
  1074. const handleViewLevelsSee = (tournamentsVO: TournamentsVO | undefined | null) => {
  1075. if (!tournamentsVO || !tournamentsVO.blindStructureId) {
  1076. proxy?.$modal.msgWarning('该赛事未绑定盲注表');
  1077. return;
  1078. }
  1079. dialogParams.value.blindStructureId = tournamentsVO.blindStructureId;
  1080. dialogParams.value.name = tournamentsVO.blindStructuresName ?? '';
  1081. levelsDialogVisible.value = true;
  1082. };
  1083. /** 修改按钮操作 */
  1084. const handleCopy = async (row?: TournamentsVO) => {
  1085. reset(); // 重置表单
  1086. const _id = row?.id || ids.value[0];
  1087. const res = await getTournaments(_id);
  1088. // 确保 gameType 是 number 类型
  1089. const gameType = Number(res.data.gameType);
  1090. const signTime = Number(res.data.signTime);
  1091. debugger;
  1092. // 设置表单数据
  1093. res.data.id = null;
  1094. Object.assign(form.value, res.data);
  1095. form.value.gameType = gameType; // 确保赋值正确
  1096. form.value.signTime = signTime;
  1097. competitionIcon.value = res.data.competitionIcon;
  1098. // 处理奖励表单数据
  1099. const prizeItems = res.data.itemsPrizeList || [];
  1100. if (prizeItems.length > 0) {
  1101. formPrize.rewards = prizeItems.map((item, index) => ({
  1102. ranking: item.ranking || index + 1,
  1103. itemId: Number(item.itemId),
  1104. quantity: Number(item.quantity)
  1105. }));
  1106. } else {
  1107. formPrize.rewards = [
  1108. {
  1109. ranking: 1,
  1110. itemId: null,
  1111. quantity: null
  1112. }
  1113. ];
  1114. }
  1115. dialog.visible = true;
  1116. dialog.title = '创建比赛';
  1117. };
  1118. const handleViewHistory = (row: ClaimsVO) => {
  1119. const tournamentId = row.tournamentId;
  1120. const playerId = row.playerId;
  1121. proxy?.$router.push({
  1122. path: '/business/history',
  1123. query: { tournamentId: String(tournamentId), playerId: String(playerId) }
  1124. });
  1125. auditDialog.value.visible = false
  1126. };
  1127. </script>