edit.vue 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. <template>
  2. <div class="game-event-edit">
  3. <el-card shadow="never">
  4. <template #header>
  5. <div class="page-header">
  6. <el-button @click="goBack" icon="ArrowLeft">返回</el-button>
  7. <span class="page-title">{{ isEdit ? '编辑赛事' : '新增赛事' }}</span>
  8. </div>
  9. </template>
  10. <el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
  11. <!-- 基本信息标签页 -->
  12. <el-tab-pane label="基本信息" name="basic">
  13. <el-form ref="basicFormRef" :model="basicForm" :rules="basicRules" label-width="120px">
  14. <el-row :gutter="20">
  15. <el-col :span="12">
  16. <el-form-item label="赛事编号" prop="eventCode">
  17. <el-input v-model="basicForm.eventCode" placeholder="请输入赛事编号" />
  18. </el-form-item>
  19. </el-col>
  20. <el-col :span="12">
  21. <el-form-item label="赛事名称" prop="eventName">
  22. <el-input v-model="basicForm.eventName" placeholder="请输入赛事名称" />
  23. </el-form-item>
  24. </el-col>
  25. </el-row>
  26. <el-row :gutter="20">
  27. <el-col :span="12">
  28. <el-form-item label="赛事类型" prop="eventType">
  29. <el-select v-model="basicForm.eventType" placeholder="请选择赛事类型" style="width: 100%">
  30. <el-option v-for="dict in game_event_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  31. </el-select>
  32. </el-form-item>
  33. </el-col>
  34. <el-col :span="12">
  35. <el-form-item label="用途" prop="purpose">
  36. <el-select v-model="basicForm.purpose" placeholder="请选择用途" style="width: 100%">
  37. <el-option v-for="dict in game_event_purpose" :key="dict.value" :label="dict.label" :value="dict.value" />
  38. </el-select>
  39. </el-form-item>
  40. </el-col>
  41. </el-row>
  42. <el-row :gutter="20">
  43. <el-col :span="12">
  44. <el-form-item label="开始时间" prop="startTime">
  45. <el-date-picker
  46. v-model="basicForm.startTime"
  47. type="datetime"
  48. value-format="YYYY-MM-DD HH:mm:ss"
  49. placeholder="请选择开始时间"
  50. style="width: 100%"
  51. />
  52. </el-form-item>
  53. </el-col>
  54. <el-col :span="12">
  55. <el-form-item label="结束时间" prop="endTime">
  56. <el-date-picker
  57. v-model="basicForm.endTime"
  58. type="datetime"
  59. value-format="YYYY-MM-DD HH:mm:ss"
  60. placeholder="请选择结束时间"
  61. style="width: 100%"
  62. />
  63. </el-form-item>
  64. </el-col>
  65. </el-row>
  66. <el-row :gutter="20">
  67. <el-col :span="12">
  68. <el-form-item label="举办地点" prop="location">
  69. <el-input v-model="basicForm.location" placeholder="请输入举办地点" />
  70. </el-form-item>
  71. </el-col>
  72. <el-col :span="12">
  73. <el-form-item label="举办单位" prop="unit">
  74. <el-input v-model="basicForm.unit" placeholder="请输入举办单位" />
  75. </el-form-item>
  76. </el-col>
  77. </el-row>
  78. <el-row :gutter="20">
  79. <el-col :span="12">
  80. <el-form-item label="是否默认赛事" prop="isDefault">
  81. <el-radio-group v-model="basicForm.isDefault" >
  82. <el-radio
  83. v-for="dict in sys_yes_no"
  84. :key="dict.value"
  85. :value="dict.value"
  86. >{{ dict.label }}</el-radio>
  87. </el-radio-group>
  88. </el-form-item>
  89. </el-col>
  90. <el-col :span="12">
  91. <el-form-item label="状态" prop="status">
  92. <el-radio-group v-model="basicForm.status">
  93. <el-radio v-for="dict in game_event_status" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
  94. </el-radio-group>
  95. </el-form-item>
  96. </el-col>
  97. </el-row>
  98. <el-row :gutter="20">
  99. <el-col :span="12">
  100. <el-form-item label="赛事链接" prop="eventUrl">
  101. <image-upload-cropper v-model="basicForm.eventUrl" />
  102. </el-form-item>
  103. </el-col>
  104. <el-col :span="12">
  105. <el-form-item label="裁判码" prop="refereeUrl">
  106. <image-upload-cropper v-model="basicForm.refereeUrl" />
  107. </el-form-item>
  108. </el-col>
  109. </el-row>
  110. <el-row :gutter="20">
  111. <el-col :span="12" v-for="item in imageConfigItems" :key="item.configKey">
  112. <el-form-item :label="item.configDesc">
  113. <image-upload-cropper v-model="item.configValue" />
  114. </el-form-item>
  115. </el-col>
  116. </el-row>
  117. <el-form-item label="备注" prop="remark">
  118. <el-input v-model="basicForm.remark" type="textarea" placeholder="请输入备注" :rows="3" />
  119. </el-form-item>
  120. </el-form>
  121. </el-tab-pane>
  122. <!-- 赛事菜单标签页 -->
  123. <el-tab-pane label="赛事菜单" name="menu">
  124. <div class="menu-config">
  125. <div class="menu-header">
  126. <h3>赛事菜单</h3>
  127. <el-button type="primary" @click="addMenuItem" icon="Plus">添加菜单项</el-button>
  128. </div>
  129. <el-table :data="menuItems" border style="width: 100%">
  130. <el-table-column label="菜单名称" prop="menuName">
  131. <template #default="scope">
  132. <el-input v-model="scope.row.menuName" placeholder="请输入菜单名称" />
  133. </template>
  134. </el-table-column>
  135. <el-table-column label="活动类型" prop="activityStatus">
  136. <template #default="scope">
  137. <el-radio-group v-model="scope.row.activityStatus">
  138. <el-radio :value="0">赛前</el-radio>
  139. <el-radio :value="1">赛中</el-radio>
  140. <el-radio :value="2">赛后</el-radio>
  141. </el-radio-group>
  142. </template>
  143. </el-table-column>
  144. <el-table-column label="图标" prop="icon">
  145. <template #default="scope">
  146. <el-input v-model="scope.row.icon" placeholder="请输入图标" />
  147. </template>
  148. </el-table-column>
  149. <el-table-column label="颜色" prop="color">
  150. <template #default="scope">
  151. <el-input v-model="scope.row.color" placeholder="请输入颜色" />
  152. </template>
  153. </el-table-column>
  154. <el-table-column label="是否站外" prop="isFrame">
  155. <template #default="scope">
  156. <el-radio-group v-model="scope.row.isFrame">
  157. <el-radio :value="1">站内</el-radio>
  158. <el-radio :value="0">站外</el-radio>
  159. </el-radio-group>
  160. </template>
  161. </el-table-column>
  162. <el-table-column label="链接" prop="siteLink">
  163. <template #default="scope">
  164. <el-input v-model="scope.row.siteLink" placeholder="请输入链接" />
  165. </template>
  166. </el-table-column>
  167. <el-table-column label="排序" prop="orderNum">
  168. <template #default="scope">
  169. <el-input-number v-model="scope.row.orderNum" :min="1" :max="999" />
  170. </template>
  171. </el-table-column>
  172. <el-table-column label="操作">
  173. <template #default="scope">
  174. <el-button type="danger" size="small" @click="removeMenuItem(scope.$index)" icon="Delete">删除 </el-button>
  175. </template>
  176. </el-table-column>
  177. </el-table>
  178. </div>
  179. </el-tab-pane>
  180. <!-- 赛事项目标签页 -->
  181. <el-tab-pane label="赛事项目" name="projects">
  182. <div class="project-config">
  183. <div class="project-header">
  184. <h3>赛事项目配置</h3>
  185. <el-button type="primary" @click="addProjectItem" icon="Plus">添加项目</el-button>
  186. </div>
  187. <el-table :data="projectItems" border style="width: 100%">
  188. <el-table-column label="項目类型" prop="projectType">
  189. <template #default="scope">
  190. <el-select v-model="scope.row.projectType" placeholder="请选择項目类型" style="width: 100%">
  191. <el-option v-for="dict in game_project_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  192. </el-select>
  193. </template>
  194. </el-table-column>
  195. <el-table-column label="项目名称" prop="projectName">
  196. <template #default="scope">
  197. <el-input v-model="scope.row.projectName" placeholder="请输入项目名称" />
  198. </template>
  199. </el-table-column>
  200. <el-table-column label="项目组别" prop="groupType">
  201. <template #default="scope">
  202. <el-select v-model="scope.row.groupType" placeholder="请选择项目组别" style="width: 100%">
  203. <el-option v-for="group in groupItems" :key="group.groupId" :label="group.groupName" :value="group.groupName" />
  204. </el-select>
  205. </template>
  206. </el-table-column>
  207. <el-table-column label="排序" prop="orderType">
  208. <template #default="scope">
  209. <el-radio-group v-model="scope.row.orderType">
  210. <el-radio value="0">降序</el-radio>
  211. <el-radio value="1">升序</el-radio>
  212. </el-radio-group>
  213. </template>
  214. </el-table-column>
  215. <el-table-column label="比赛地点" prop="location">
  216. <template #default="scope">
  217. <el-input v-model="scope.row.location" placeholder="请输入比赛地点" />
  218. </template>
  219. </el-table-column>
  220. <el-table-column label="录取名次" prop="roundType">
  221. <template #default="scope">
  222. <el-input v-model="scope.row.roundType" placeholder="请输入录取名次" />
  223. </template>
  224. </el-table-column>
  225. <el-table-column label="分数" prop="scoreValue">
  226. <template #default="scope">
  227. <el-input v-model="scope.row.scoreValue" placeholder="请输入分数" />
  228. </template>
  229. </el-table-column>
  230. <el-table-column label="操作">
  231. <template #default="scope">
  232. <el-button type="danger" size="small" @click="removeProjectItem(scope.$index)" icon="Delete">移除 </el-button>
  233. </template>
  234. </el-table-column>
  235. </el-table>
  236. </div>
  237. </el-tab-pane>
  238. <!-- 配置信息标签页 -->
  239. <el-tab-pane label="配置信息" name="config">
  240. <div class="config-info">
  241. <div class="config-header">
  242. <h3>赛事配置信息</h3>
  243. <el-button type="primary" @click="addConfigItem" icon="Plus">添加配置</el-button>
  244. </div>
  245. <el-table :data="configItems" border style="width: 100%">
  246. <el-table-column label="配置类型" prop="configType" width="150">
  247. <template #default="scope">
  248. <el-select v-model="scope.row.configType" placeholder="请选择配置类型" style="width: 100%">
  249. <el-option
  250. v-for="type in configTypes"
  251. :key="type.typeCode"
  252. :label="type.typeName"
  253. :value="type.typeCode"
  254. />
  255. </el-select>
  256. </template>
  257. </el-table-column>
  258. <el-table-column label="配置键" prop="configKey" width="200">
  259. <template #default="scope">
  260. <el-input v-model="scope.row.configKey" placeholder="请输入配置键" />
  261. </template>
  262. </el-table-column>
  263. <el-table-column label="配置值" prop="configValue">
  264. <template #default="scope">
  265. <image-or-url-input v-model="scope.row.configValue" />
  266. </template>
  267. </el-table-column>
  268. <el-table-column label="配置描述" prop="configDesc">
  269. <template #default="scope">
  270. <el-input v-model="scope.row.configDesc" placeholder="请输入配置描述" />
  271. </template>
  272. </el-table-column>
  273. <!-- <el-table-column label="是否启用" prop="isEnabled">
  274. <template #default="scope">
  275. <el-switch v-model="scope.row.isEnabled" />
  276. </template>
  277. </el-table-column> -->
  278. <el-table-column label="操作">
  279. <template #default="scope">
  280. <el-button type="danger" size="small" @click="removeConfigItem(scope.$index)" icon="Delete">删除 </el-button>
  281. </template>
  282. </el-table-column>
  283. </el-table>
  284. </div>
  285. </el-tab-pane>
  286. <!-- 参赛队伍标签页 -->
  287. <el-tab-pane label="参赛队伍" name="teams">
  288. <div class="team-config">
  289. <div class="team-header">
  290. <h3>参赛队伍</h3>
  291. <el-button type="primary" @click="addTeamItem" icon="Plus">新增队伍</el-button>
  292. </div>
  293. <el-table :data="teamItems" border style="width: 100%">
  294. <el-table-column label="单位名称" prop="teamName">
  295. <template #default="scope">
  296. <el-input v-model="scope.row.teamName" placeholder="请输入单位名称" />
  297. </template>
  298. </el-table-column>
  299. <el-table-column label="编号" prop="teamCode">
  300. <template #default="scope">
  301. <el-input v-model="scope.row.teamCode" placeholder="请输入编号" />
  302. </template>
  303. </el-table-column>
  304. <el-table-column label="团队描述" prop="teamDescribe">
  305. <template #default="scope">
  306. <el-input v-model="scope.row.teamDescribe" placeholder="请描述一下你的团队吧~" />
  307. </template>
  308. </el-table-column>
  309. <el-table-column label="操作">
  310. <template #default="scope">
  311. <el-button type="danger" size="small" @click="removeTeamItem(scope.$index)" icon="Delete">删除 </el-button>
  312. </template>
  313. </el-table-column>
  314. </el-table>
  315. </div>
  316. </el-tab-pane>
  317. <!-- 赛事分组标签页 -->
  318. <el-tab-pane label="赛事分组" name="groups">
  319. <div class="group-config">
  320. <div class="group-header">
  321. <h3>赛事分组</h3>
  322. <el-button type="primary" @click="addGroupItem" icon="Plus">添加分组</el-button>
  323. </div>
  324. <el-table :data="groupItems" border style="width: 100%">
  325. <el-table-column label="分组名称" prop="groupName">
  326. <template #default="scope">
  327. <el-input v-model="scope.row.groupName" placeholder="请输入分组名称" />
  328. </template>
  329. </el-table-column>
  330. <el-table-column label="成员性别" prop="memberGender">
  331. <template #default="scope">
  332. <el-radio-group v-model="scope.row.memberGender">
  333. <el-radio value="0">不分男女</el-radio>
  334. <el-radio value="1">男</el-radio>
  335. <el-radio value="2">女</el-radio>
  336. </el-radio-group>
  337. </template>
  338. </el-table-column>
  339. <el-table-column label="操作">
  340. <template #default="scope">
  341. <el-button type="danger" size="small" @click="removeGroupItem(scope.$index)" icon="Delete">删除 </el-button>
  342. </template>
  343. </el-table-column>
  344. </el-table>
  345. </div>
  346. </el-tab-pane>
  347. </el-tabs>
  348. <!-- 底部操作按钮 -->
  349. <div class="form-actions">
  350. <el-button @click="goBack">取消</el-button>
  351. <el-button type="primary" @click="saveEvent" :loading="saveLoading">保存</el-button>
  352. </div>
  353. </el-card>
  354. </div>
  355. </template>
  356. <script setup name="GameEventEdit" lang="ts">
  357. import { getGameEvent, addGameEvent, updateGameEvent,changeEventDefault } from '@/api/system/gameEvent';
  358. import { GameEventVO, GameEventForm } from '@/api/system/gameEvent/types';
  359. import { listGameEventConfig, addGameEventConfig, updateGameEventConfig, delGameEventConfig } from '@/api/system/gameEventConfig';
  360. import { GameEventConfigVO, GameEventConfigForm } from '@/api/system/gameEventConfig/types';
  361. import { listGameEventConfigType } from '@/api/system/gameEventConfigType'; // 添加导入
  362. import { GameEventConfigTypeVO } from '@/api/system/gameEventConfigType/types'; // 添加导入
  363. import { useRoute, useRouter } from 'vue-router';
  364. import { listGameEventMenu, addGameEventMenu, updateGameEventMenu, delGameEventMenu } from '@/api/system/gameEventMenu';
  365. import { GameEventMenuVO, GameEventMenuForm } from '@/api/system/gameEventMenu/types';
  366. import { listGameTeam, addGameTeam, updateGameTeam, delGameTeam } from '@/api/system/gameTeam';
  367. import { GameTeamVO, GameTeamForm } from '@/api/system/gameTeam/types';
  368. import { listGameEventGroup, addGameEventGroup, updateGameEventGroup, delGameEventGroup } from '@/api/system/gameEventGroup';
  369. import { GameEventGroupForm, GameEventGroupVO } from '@/api/system/gameEventGroup/types';
  370. import { addGameEventProject, listGameEventProject, updateGameEventProject, delGameEventProject } from '@/api/system/gameEventProject';
  371. import { GameEventProjectForm, GameEventProjectVO } from '@/api/system/gameEventProject/types';
  372. const route = useRoute();
  373. const router = useRouter();
  374. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  375. const { game_event_type, game_event_purpose, game_event_status, sys_yes_no, game_project_type } = toRefs<any>(
  376. proxy?.useDict('game_event_type', 'game_event_purpose', 'game_event_status', 'sys_yes_no', 'game_project_type')
  377. );
  378. // 页面状态
  379. const isEdit = ref(false);
  380. const activeTab = ref('basic');
  381. const saveLoading = ref(false);
  382. // 表单引用
  383. const basicFormRef = ref<ElFormInstance>();
  384. // 基本信息表单
  385. const basicForm = ref<GameEventForm>({
  386. eventCode: '',
  387. eventName: '',
  388. eventType: '',
  389. location: '',
  390. purpose: '',
  391. startTime: '',
  392. endTime: '',
  393. eventUrl: '',
  394. refereeUrl: '',
  395. registerUrl: '',
  396. unit: '',
  397. isDefault: '0',
  398. status: '0',
  399. remark: ''
  400. });
  401. // 表单验证规则
  402. const basicRules = {
  403. eventCode: [{ required: true, message: '请输入赛事编号', trigger: 'blur' }],
  404. eventName: [{ required: true, message: '请输入赛事名称', trigger: 'blur' }],
  405. eventType: [{ required: true, message: '请选择赛事类型', trigger: 'change' }],
  406. startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  407. endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
  408. };
  409. // 初始化页面
  410. onMounted(async () => {
  411. const eventId = route.params.id as string;
  412. if (eventId) {
  413. isEdit.value = true;
  414. await loadEventData(eventId);
  415. } else {
  416. // 新增模式,加载图片配置模板
  417. // await loadImageConfigTemplates();
  418. }
  419. // 加载配置类型选项
  420. await loadConfigTypes();
  421. // console.log('Initial image configs:', imageConfigItems.value); // 添加调试信息
  422. });
  423. // 加载赛事数据
  424. const loadEventData = async (eventId: string | number) => {
  425. try {
  426. const res = await getGameEvent(eventId);
  427. const data = res.data;
  428. // 填充基本信息
  429. Object.assign(basicForm.value, data);
  430. // 加载图片配置数据
  431. try {
  432. await loadImageConfigData(eventId);
  433. } catch (error) {
  434. console.warn('加载图片配置数据失败,继续加载其他数据:', error);
  435. // 不中断其他数据的加载
  436. }
  437. await loadMenuData(eventId); // 加载菜单数据
  438. await loadGroupData(eventId); // 加载分组数据
  439. await loadProjectData(eventId); // 加载项目数据
  440. await loadTeamData(eventId); //// 加载队伍数据
  441. await loadConfigData(eventId); // 加载配置数据
  442. // TODO: 加载菜单、项目、配置数据
  443. // 这里需要调用相应的API来加载数据
  444. } catch (error) {
  445. proxy?.$modal.msgError('加载赛事数据失败');
  446. }
  447. };
  448. /** 状态修改 */
  449. const handleStatusChange = async (row: GameEventVO) => {
  450. const text = row.isDefault === '0' ? '启用' : '停用';
  451. try {
  452. await proxy?.$modal.confirm('确认要"' + text + '""' + row.eventName + '"为默认赛事吗?');
  453. await changeEventDefault(row.eventId, row.isDefault);
  454. await loadEventData(row.eventId);
  455. proxy?.$modal.msgSuccess(text + '成功');
  456. } catch {
  457. return;
  458. } finally {
  459. row.isDefault = row.isDefault === '0' ? '1' : '0';
  460. }
  461. };
  462. // 赛事分组数据
  463. const groupItems = ref<GameEventGroupForm[]>([]);
  464. // 加载项目分组数据
  465. const loadGroupData = async (eventId: string | number) => {
  466. try {
  467. const res = await listGameEventGroup({
  468. eventId,
  469. pageNum: 1,
  470. pageSize: 1000,
  471. orderByColumn: '',
  472. isAsc: ''
  473. });
  474. groupItems.value = Array.isArray(res.rows) ? res.rows : [];
  475. } catch (error) {
  476. proxy?.$modal.msgError('加载项目分组数据失败');
  477. }
  478. };
  479. // 添加赛事分组项
  480. const addGroupItem = () => {
  481. groupItems.value.push({
  482. groupId: '',
  483. eventId: '',
  484. groupName: '',
  485. memberGender: ''
  486. });
  487. };
  488. // 删除赛事分组项
  489. const removeGroupItem = (index: number) => {
  490. const groupItem = groupItems.value[index];
  491. if (groupItem.groupId) {
  492. delGameEventGroup(groupItem.groupId).then(() => {
  493. groupItems.value.splice(index, 1);
  494. });
  495. } else {
  496. groupItems.value.splice(index, 1);
  497. }
  498. };
  499. // 保存赛事分组数据
  500. const saveGroupData = async (eventId: string) => {
  501. try {
  502. for (const item of groupItems.value) {
  503. // 确保eventId被正确设置
  504. const groupData = { ...item, eventId };
  505. if (item.groupId) {
  506. // 更新现有分组
  507. await updateGameEventGroup(groupData);
  508. } else if (item.groupName) {
  509. // 只有有名称的新分组才保存
  510. await addGameEventGroup(groupData);
  511. }
  512. }
  513. } catch (error) {
  514. console.error('保存赛事分组数据失败:', error);
  515. throw new Error('保存赛事分组失败');
  516. }
  517. };
  518. // 菜单列表数据
  519. const menuItems = ref<GameEventMenuForm[]>([]);
  520. // 加载菜单数据
  521. const loadMenuData = async (eventId: string | number) => {
  522. try {
  523. const res = await listGameEventMenu({
  524. eventId,
  525. pageNum: 1,
  526. pageSize: 1000,
  527. orderByColumn: '',
  528. isAsc: ''
  529. });
  530. menuItems.value = Array.isArray(res.rows) ? res.rows : [];
  531. } catch (error) {
  532. proxy?.$modal.msgError('加载菜单数据失败');
  533. }
  534. };
  535. // 添加菜单项
  536. const addMenuItem = () => {
  537. menuItems.value.push({
  538. menuId: '',
  539. parentId: 0,
  540. menuName: '',
  541. activityStatus: 0,
  542. icon: '',
  543. color: '',
  544. isFrame: 1,
  545. siteLink: '',
  546. orderNum: menuItems.value.length + 1,
  547. eventId: isEdit.value ? (route.params.id as string) : '' // 关联赛事ID
  548. });
  549. };
  550. // 删除菜单项
  551. const removeMenuItem = (index: number) => {
  552. const menuItem = menuItems.value[index];
  553. if (menuItem.menuId) {
  554. delGameEventMenu(menuItem.menuId).then(() => {
  555. menuItems.value.splice(index, 1);
  556. });
  557. } else {
  558. menuItems.value.splice(index, 1);
  559. }
  560. };
  561. // 保存菜单数据
  562. const saveMenuData = async (eventId: string) => {
  563. try {
  564. for (const item of menuItems.value) {
  565. // 确保eventId被正确设置
  566. const menuData = { ...item, eventId };
  567. if (item.menuId) {
  568. // 更新现有菜单
  569. await updateGameEventMenu(menuData);
  570. } else if (item.menuName) {
  571. // 只有有名称的新菜单才保存
  572. await addGameEventMenu(menuData);
  573. }
  574. }
  575. } catch (error) {
  576. console.error('保存菜单数据失败:', error);
  577. throw new Error('保存菜单失败');
  578. }
  579. };
  580. // 图片配置数据
  581. const imageConfigItems = ref<GameEventConfigForm[]>([]);
  582. // 加载图片配置数据(用于编辑模式)
  583. const loadImageConfigData = async (eventId: string | number) => {
  584. try {
  585. // 先查询所有image类型的配置模板(不限制eventId,获取所有可用的图片配置项)
  586. const templateRes = await listGameEventConfig({
  587. configType: 'IMAGE',
  588. pageNum: 1,
  589. pageSize: 100,
  590. orderByColumn: '',
  591. isAsc: ''
  592. });
  593. // 获取所有图片配置模板
  594. const allImageConfigs = Array.isArray(templateRes.rows) ? templateRes.rows : [];
  595. // 查询当前赛事的图片配置数据
  596. const eventRes = await listGameEventConfig({
  597. eventId: eventId,
  598. configType: 'IMAGE',
  599. pageNum: 1,
  600. pageSize: 100,
  601. orderByColumn: '',
  602. isAsc: ''
  603. });
  604. const eventImageConfigs = Array.isArray(eventRes.rows) ? eventRes.rows : [];
  605. // 合并配置:以模板为基础,用当前赛事的数据覆盖
  606. imageConfigItems.value = allImageConfigs.map((template) => {
  607. const existingConfig = eventImageConfigs.find((item) => item.configKey === template.configKey);
  608. return {
  609. ...template,
  610. eventId: eventId,
  611. configValue: existingConfig?.configValue || '',
  612. isEnabled: existingConfig?.isEnabled || template.isEnabled || '0',
  613. status: existingConfig?.status || template.status || '0',
  614. configId: existingConfig?.configId
  615. };
  616. });
  617. } catch (error) {
  618. console.error('加载图片配置数据失败:', error);
  619. proxy?.$modal.msgError('加载图片配置数据失败: ' + (error as Error).message);
  620. imageConfigItems.value = [];
  621. }
  622. };
  623. // 刷新图片配置
  624. const refreshImageConfigs = async () => {
  625. const eventId = route.params.id as string;
  626. if (eventId && eventId !== 'add') {
  627. await loadImageConfigData(eventId);
  628. } else {
  629. // 新增模式,只加载配置模板
  630. // await loadImageConfigTemplates();
  631. }
  632. };
  633. // 加载图片配置模板(用于新增模式)
  634. // const loadImageConfigTemplates = async () => {
  635. // try {
  636. // const res = await listGameEventConfig({
  637. // eventId: '',
  638. // configType: 'IMAGE',
  639. // pageNum: 1,
  640. // pageSize: 100
  641. // });
  642. // // console.log('Template response (new mode):', res);
  643. // const imageData = Array.isArray(res.rows) ? res.rows : []; // 修改此处,确保正确访问数组数据
  644. // // console.log('Image data (new mode):', imageData);
  645. // imageConfigItems.value = imageData.map(item => ({
  646. // ...item,
  647. // configValue: item.configValue || '', // 确保 configValue 初始化正确
  648. // isEnabled: item.isEnabled || '0', // 确保 isEnabled 初始化正确
  649. // status: item.status || '0' // 确保 status 初始化正确
  650. // }));
  651. // // console.log('Image config items (new mode):', imageConfigItems.value);
  652. // } catch (error) {
  653. // console.error('加载图片配置模板失败:', error);
  654. // proxy?.$modal.msgError('加载图片配置模板失败: ' + (error as Error).message);
  655. // imageConfigItems.value = [];
  656. // }
  657. // };
  658. // 保存图片配置数据
  659. const saveImageConfigData = async (eventId?: string | number) => {
  660. try {
  661. const targetEventId = eventId || (route.params.id as string);
  662. const updates: GameEventConfigForm[] = [];
  663. const adds: GameEventConfigForm[] = [];
  664. // 收集需要更新和添加的数据
  665. for (const item of imageConfigItems.value) {
  666. if (item.configKey) {
  667. const configData: GameEventConfigForm = {
  668. ...item,
  669. eventId: targetEventId,
  670. configType: 'IMAGE'
  671. };
  672. if (item.configId && item.configValue) {
  673. updates.push(configData);
  674. } else if (!item.configId && item.configValue) {
  675. adds.push(configData);
  676. }
  677. }
  678. }
  679. // 批量更新
  680. if (updates.length > 0) {
  681. await Promise.all(updates.map(updateGameEventConfig));
  682. }
  683. // 批量添加
  684. if (adds.length > 0) {
  685. await Promise.all(adds.map(addGameEventConfig));
  686. }
  687. } catch (error) {
  688. console.error('保存图片配置数据失败:', error);
  689. throw new Error('保存图片配置失败');
  690. }
  691. };
  692. // 处理标签页切换
  693. const handleTabClick = () => {
  694. // 可以在这里添加标签页切换的逻辑
  695. };
  696. // 项目数据
  697. const projectItems = ref<GameEventProjectForm[]>([]);
  698. // 加载项目数据
  699. const loadProjectData = async (eventId: string | number) => {
  700. try {
  701. const res = await listGameEventProject({
  702. eventId,
  703. pageNum: 1,
  704. pageSize: 1000,
  705. orderByColumn: '',
  706. isAsc: ''
  707. });
  708. projectItems.value = Array.isArray(res.rows) ? res.rows : [];
  709. } catch (error) {
  710. proxy?.$modal.msgError('加载项目数据失败');
  711. }
  712. };
  713. // 添加项目项
  714. const addProjectItem = () => {
  715. projectItems.value.push({
  716. eventId: '',
  717. projectId: '',
  718. projectName: '',
  719. projectType: '',
  720. remark: '',
  721. location: '',
  722. scoreValue: '',
  723. groupType: ''
  724. });
  725. };
  726. // 删除项目项
  727. const removeProjectItem = (index: number) => {
  728. const projectItem = projectItems.value[index];
  729. if (projectItem.projectId) {
  730. delGameEventProject(projectItem.projectId).then(() => {
  731. projectItems.value.splice(index, 1);
  732. });
  733. } else {
  734. projectItems.value.splice(index, 1);
  735. }
  736. };
  737. //保存项目数据
  738. const saveProjectData = async (eventId: string | number) => {
  739. try {
  740. for (const item of projectItems.value) {
  741. // 确保eventId被正确设置
  742. const projectData = { ...item, eventId };
  743. if (item.projectId) {
  744. // 更新现有队伍
  745. await updateGameEventProject(projectData);
  746. } else if (item.projectName) {
  747. // 只有有名称的新队伍才保存
  748. await addGameEventProject(projectData);
  749. }
  750. }
  751. } catch (error) {
  752. console.error('保存赛事项目数据失败:', error);
  753. throw new Error('保存赛事项目失败');
  754. }
  755. };
  756. // 配置信息数据
  757. const configItems = ref<GameEventConfigForm[]>([]);
  758. // 配置类型选项
  759. const configTypes = ref<GameEventConfigTypeVO[]>([]);
  760. // 加载配置类型数据
  761. const loadConfigTypes = async () => {
  762. try {
  763. const res = await listGameEventConfigType({
  764. pageNum: 1,
  765. pageSize: 1000,
  766. orderByColumn: '',
  767. isAsc: ''
  768. });
  769. configTypes.value = Array.isArray(res.rows) ? res.rows : [];
  770. } catch (error) {
  771. proxy?.$modal.msgError('加载配置类型数据失败');
  772. }
  773. };
  774. // 加载赛事配置数据
  775. const loadConfigData = async (eventId: string | number) => {
  776. try {
  777. const res = await listGameEventConfig({
  778. eventId,
  779. pageNum: 1,
  780. pageSize: 10,
  781. orderByColumn: '',
  782. isAsc: ''
  783. });
  784. configItems.value = Array.isArray(res.rows) ? res.rows : [];
  785. } catch (error) {
  786. console.error('加载赛事配置数据失败:', error);
  787. proxy?.$modal.msgError('加载赛事配置数据失败: ' + (error as Error).message);
  788. }
  789. };
  790. // 添加配置项
  791. const addConfigItem = () => {
  792. configItems.value.push({
  793. configKey: '',
  794. configValue: '',
  795. configDesc: '',
  796. configType: '', // 添加配置类型字段
  797. });
  798. };
  799. // 删除配置项
  800. const removeConfigItem = (index: number) => {
  801. const configItem = configItems.value[index];
  802. if (configItem.configId) {
  803. delGameEventConfig(configItem.configId).then(() => {
  804. configItems.value.splice(index, 1);
  805. });
  806. } else {
  807. configItems.value.splice(index, 1);
  808. }
  809. };
  810. // 保存赛事配置数据
  811. const saveconfigData = async (eventId: string | number) => {
  812. try {
  813. for (const item of configItems.value) {
  814. // 确保eventId被正确设置
  815. const configData = { ...item, eventId };
  816. if (item.configId) {
  817. // 更新现有队伍
  818. await updateGameEventConfig(configData);
  819. } else if (item.configDesc) {
  820. // 只有有名称的新队伍才保存
  821. await addGameEventConfig(configData);
  822. }
  823. }
  824. } catch (error) {
  825. console.error('保存赛事配置信息失败:', error);
  826. throw new Error('保存赛事配置信息失败');
  827. }
  828. };
  829. // 参赛队伍数据
  830. const teamItems = ref<GameTeamForm[]>([]);
  831. // 加载队伍数据
  832. const loadTeamData = async (eventId: string | number) => {
  833. try {
  834. const res = await listGameTeam({
  835. eventId,
  836. pageNum: 1,
  837. pageSize: 1000,
  838. orderByColumn: '',
  839. isAsc: ''
  840. });
  841. teamItems.value = Array.isArray(res.rows) ? res.rows : [];
  842. } catch (error) {
  843. proxy?.$modal.msgError('加载队伍数据失败');
  844. }
  845. };
  846. // 添加参赛队伍项
  847. const addTeamItem = () => {
  848. teamItems.value.push({
  849. teamName: '',
  850. teamCode: '',
  851. eventId: '',
  852. teamDescribe: '',
  853. remark: ''
  854. });
  855. };
  856. // 删除参赛队伍项
  857. const removeTeamItem = (index: number) => {
  858. const teamItem = teamItems.value[index];
  859. if (teamItem.teamId) {
  860. delGameTeam(teamItem.teamId).then(() => {
  861. teamItems.value.splice(index, 1);
  862. });
  863. } else {
  864. teamItems.value.splice(index, 1);
  865. }
  866. };
  867. // 保存参赛队伍数据
  868. const saveTeamData = async (eventId: string | number) => {
  869. try {
  870. for (const item of teamItems.value) {
  871. // 确保eventId被正确设置
  872. const teamData = { ...item, eventId };
  873. if (item.teamId) {
  874. // 更新现有队伍
  875. await updateGameTeam(teamData);
  876. } else if (item.teamName) {
  877. // 只有有名称的新队伍才保存
  878. await addGameTeam(teamData);
  879. }
  880. }
  881. } catch (error) {
  882. console.error('保存参赛队伍数据失败:', error);
  883. throw new Error('保存参赛队伍失败');
  884. }
  885. };
  886. // 保存赛事信息
  887. const saveEvent = async () => {
  888. try {
  889. // 验证基本信息表单
  890. await basicFormRef.value?.validate();
  891. saveLoading.value = true;
  892. // 合并表单数据
  893. const formData: GameEventForm = {
  894. ...basicForm.value
  895. };
  896. let savedEventId: string;
  897. // 如果设置为默认赛事,则取消其他赛事的默认状态
  898. if(basicForm.value.isDefault === '0'){
  899. handleStatusChange({ eventId: basicForm.value.eventId, isDefault: basicForm.value.isDefault } as GameEventVO);
  900. }
  901. // 保存基本信息
  902. if (isEdit.value) {
  903. await updateGameEvent(formData);
  904. savedEventId = route.params.id as string;
  905. } else {
  906. const addRes = await addGameEvent(formData);
  907. // 假设返回的数据中包含新创建的赛事ID
  908. savedEventId = addRes?.data as string;
  909. }
  910. // 保存图片配置数据
  911. await saveImageConfigData(savedEventId);
  912. // 保存赛事配置项数据
  913. await saveconfigData(savedEventId);
  914. // 保存菜单数据
  915. await saveMenuData(savedEventId);
  916. // 保存参赛队伍数据
  917. await saveTeamData(savedEventId);
  918. // 保存赛事项目分组数据
  919. await saveGroupData(savedEventId);
  920. // 保存赛事项目数据
  921. await saveProjectData(savedEventId);
  922. proxy?.$modal.msgSuccess('保存成功');
  923. // 保存成功后,设置一个标识,表示需要刷新列表数据
  924. sessionStorage.setItem('needRefreshGameEventList', 'true');
  925. goBack();
  926. } catch (error) {
  927. console.error('保存失败:', error);
  928. proxy?.$modal.msgError('保存失败');
  929. } finally {
  930. saveLoading.value = false;
  931. }
  932. };
  933. // 返回上一页
  934. const goBack = () => {
  935. router.go(-1);
  936. };
  937. </script>
  938. <style lang="scss" scoped>
  939. .game-event-edit {
  940. .page-header {
  941. display: flex;
  942. align-items: center;
  943. gap: 16px;
  944. .page-title {
  945. font-size: 18px;
  946. font-weight: 600;
  947. }
  948. }
  949. .menu-config,
  950. .project-config,
  951. .config-info,
  952. .image-config {
  953. .menu-header,
  954. .project-header,
  955. .config-header,
  956. .image-header {
  957. display: flex;
  958. justify-content: space-between;
  959. align-items: center;
  960. margin-bottom: 16px;
  961. h3 {
  962. margin: 0;
  963. font-size: 16px;
  964. font-weight: 600;
  965. }
  966. }
  967. }
  968. .image-config {
  969. .config-info {
  970. margin-top: 8px;
  971. display: flex;
  972. align-items: center;
  973. justify-content: space-between;
  974. }
  975. }
  976. .team-config {
  977. .team-header {
  978. display: flex;
  979. justify-content: space-between;
  980. align-items: center;
  981. margin-bottom: 16px;
  982. h3 {
  983. margin: 0;
  984. font-size: 16px;
  985. font-weight: 600;
  986. }
  987. }
  988. }
  989. .group-config {
  990. .group-header {
  991. display: flex;
  992. justify-content: space-between;
  993. align-items: center;
  994. margin-bottom: 16px;
  995. h3 {
  996. margin: 0;
  997. font-size: 16px;
  998. font-weight: 600;
  999. }
  1000. }
  1001. }
  1002. .form-actions {
  1003. display: flex;
  1004. justify-content: center;
  1005. gap: 16px;
  1006. margin-top: 32px;
  1007. padding-top: 24px;
  1008. border-top: 1px solid #ebeef5;
  1009. }
  1010. }
  1011. :deep(.el-tabs__content) {
  1012. padding: 20px 0;
  1013. }
  1014. :deep(.el-form-item) {
  1015. margin-bottom: 20px;
  1016. }
  1017. </style>