edit.vue 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  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 isDefaultEventOperation = () => {
  384. return basicForm.value.isDefault === '0';
  385. };
  386. // 获取有效的赛事ID(默认赛事返回空字符串,否则返回实际ID)
  387. const getEffectiveEventId = (eventId?: string | number): string => {
  388. if (isDefaultEventOperation()) {
  389. return '';
  390. }
  391. return String(eventId || (route.params.id as string) || '');
  392. };
  393. // 表单引用
  394. const basicFormRef = ref<ElFormInstance>();
  395. // 基本信息表单
  396. const basicForm = ref<GameEventForm>({
  397. eventCode: '',
  398. eventName: '',
  399. eventType: '',
  400. location: '',
  401. purpose: '',
  402. startTime: '',
  403. endTime: '',
  404. eventUrl: '',
  405. refereeUrl: '',
  406. registerUrl: '',
  407. unit: '',
  408. isDefault: '0',
  409. status: '0',
  410. remark: ''
  411. });
  412. // 表单验证规则
  413. const basicRules = {
  414. eventCode: [{ required: true, message: '请输入赛事编号', trigger: 'blur' }],
  415. eventName: [{ required: true, message: '请输入赛事名称', trigger: 'blur' }],
  416. eventType: [{ required: true, message: '请选择赛事类型', trigger: 'change' }],
  417. startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  418. endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
  419. };
  420. // 初始化页面
  421. onMounted(async () => {
  422. const eventId = route.params.id as string;
  423. if (eventId) {
  424. isEdit.value = true;
  425. await loadEventData(eventId);
  426. } else {
  427. // 新增模式,加载图片配置模板
  428. // await loadImageConfigTemplates();
  429. }
  430. // 加载配置类型选项
  431. await loadConfigTypes();
  432. // console.log('Initial image configs:', imageConfigItems.value); // 添加调试信息
  433. });
  434. // 加载赛事数据
  435. const loadEventData = async (eventId: string | number) => {
  436. try {
  437. const res = await getGameEvent(eventId);
  438. const data = res.data;
  439. // 填充基本信息
  440. Object.assign(basicForm.value, data);
  441. // 获取有效的赛事ID
  442. const effectiveEventId = getEffectiveEventId(eventId);
  443. // 加载图片配置数据
  444. try {
  445. await loadImageConfigData(effectiveEventId);
  446. } catch (error) {
  447. console.warn('加载图片配置数据失败,继续加载其他数据:', error);
  448. // 不中断其他数据的加载
  449. }
  450. await loadMenuData(effectiveEventId); // 加载菜单数据
  451. await loadGroupData(effectiveEventId); // 加载分组数据
  452. await loadProjectData(effectiveEventId); // 加载项目数据
  453. await loadTeamData(effectiveEventId); // 加载队伍数据
  454. await loadConfigData(effectiveEventId); // 加载配置数据
  455. } catch (error) {
  456. proxy?.$modal.msgError('加载赛事数据失败');
  457. }
  458. };
  459. /** 状态修改 */
  460. const handleStatusChange = async (row: GameEventVO) => {
  461. const text = row.isDefault === '0' ? '启用' : '停用';
  462. try {
  463. await proxy?.$modal.confirm('确认要"' + text + '""' + row.eventName + '"为默认赛事吗?');
  464. await changeEventDefault(row.eventId, row.isDefault);
  465. await loadEventData(row.eventId);
  466. proxy?.$modal.msgSuccess(text + '成功');
  467. } catch {
  468. return;
  469. } finally {
  470. row.isDefault = row.isDefault === '0' ? '1' : '0';
  471. }
  472. };
  473. // 赛事分组数据
  474. const groupItems = ref<GameEventGroupForm[]>([]);
  475. // 加载项目分组数据
  476. const loadGroupData = async (eventId: string | number) => {
  477. try {
  478. const res = await listGameEventGroup({
  479. eventId: eventId === '' ? '' : eventId,
  480. pageNum: 1,
  481. pageSize: 1000,
  482. orderByColumn: '',
  483. isAsc: ''
  484. });
  485. groupItems.value = Array.isArray(res.rows) ? res.rows : [];
  486. } catch (error) {
  487. proxy?.$modal.msgError('加载项目分组数据失败');
  488. }
  489. };
  490. // 添加赛事分组项
  491. const addGroupItem = () => {
  492. groupItems.value.push({
  493. groupId: '',
  494. eventId: getEffectiveEventId(),
  495. groupName: '',
  496. memberGender: ''
  497. });
  498. };
  499. // 删除赛事分组项
  500. const removeGroupItem = (index: number) => {
  501. const groupItem = groupItems.value[index];
  502. if (groupItem.groupId) {
  503. delGameEventGroup(groupItem.groupId).then(() => {
  504. groupItems.value.splice(index, 1);
  505. });
  506. } else {
  507. groupItems.value.splice(index, 1);
  508. }
  509. };
  510. // 保存赛事分组数据
  511. const saveGroupData = async (eventId: string | number) => {
  512. try {
  513. for (const item of groupItems.value) {
  514. // 确保eventId被正确设置
  515. const groupData = { ...item, eventId };
  516. if (item.groupId) {
  517. // 更新现有分组
  518. await updateGameEventGroup(groupData);
  519. } else if (item.groupName) {
  520. // 只有有名称的新分组才保存
  521. await addGameEventGroup(groupData);
  522. }
  523. }
  524. } catch (error) {
  525. console.error('保存赛事分组数据失败:', error);
  526. throw new Error('保存赛事分组失败');
  527. }
  528. };
  529. // 菜单列表数据
  530. const menuItems = ref<GameEventMenuForm[]>([]);
  531. // 加载菜单数据
  532. const loadMenuData = async (eventId: string | number) => {
  533. try {
  534. const res = await listGameEventMenu({
  535. eventId: eventId === '' ? '' : eventId,
  536. pageNum: 1,
  537. pageSize: 10,
  538. orderByColumn: undefined,
  539. isAsc: undefined,
  540. });
  541. menuItems.value = Array.isArray(res.rows) ? res.rows : [];
  542. } catch (error) {
  543. proxy?.$modal.msgError('加载菜单数据失败');
  544. }
  545. };
  546. // 添加菜单项
  547. const addMenuItem = () => {
  548. menuItems.value.push({
  549. menuId: '',
  550. parentId: 0,
  551. menuName: '',
  552. activityStatus: 0,
  553. icon: '',
  554. color: '',
  555. isFrame: 1,
  556. siteLink: '',
  557. orderNum: menuItems.value.length + 1,
  558. eventId: getEffectiveEventId() // 使用有效的赛事ID
  559. });
  560. };
  561. // 删除菜单项
  562. const removeMenuItem = (index: number) => {
  563. const menuItem = menuItems.value[index];
  564. if (menuItem.menuId) {
  565. delGameEventMenu(menuItem.menuId).then(() => {
  566. menuItems.value.splice(index, 1);
  567. });
  568. } else {
  569. menuItems.value.splice(index, 1);
  570. }
  571. };
  572. // 保存菜单数据
  573. const saveMenuData = async (eventId: string | number) => {
  574. try {
  575. for (const item of menuItems.value) {
  576. // 确保eventId被正确设置
  577. const menuData = { ...item, eventId };
  578. if (item.menuId) {
  579. // 更新现有菜单
  580. await updateGameEventMenu(menuData);
  581. } else if (item.menuName) {
  582. // 只有有名称的新菜单才保存
  583. await addGameEventMenu(menuData);
  584. }
  585. }
  586. } catch (error) {
  587. console.error('保存菜单数据失败:', error);
  588. throw new Error('保存菜单失败');
  589. }
  590. };
  591. // 图片配置数据
  592. const imageConfigItems = ref<GameEventConfigForm[]>([]);
  593. // 加载图片配置数据(用于编辑模式)
  594. const loadImageConfigData = async (eventId: string | number) => {
  595. try {
  596. // 先查询所有image类型的配置模板(不限制eventId,获取所有可用的图片配置项)
  597. const templateRes = await listGameEventConfig({
  598. configType: 'IMAGE',
  599. pageNum: 1,
  600. pageSize: 100,
  601. orderByColumn: '',
  602. isAsc: ''
  603. });
  604. // 获取所有图片配置模板
  605. const allImageConfigs = Array.isArray(templateRes.rows) ? templateRes.rows : [];
  606. // 查询当前赛事的图片配置数据(如果是默认赛事,eventId为空字符串)
  607. const eventRes = await listGameEventConfig({
  608. eventId: eventId === '' ? '' : eventId,
  609. configType: 'IMAGE',
  610. pageNum: 1,
  611. pageSize: 100,
  612. orderByColumn: '',
  613. isAsc: ''
  614. });
  615. const eventImageConfigs = Array.isArray(eventRes.rows) ? eventRes.rows : [];
  616. // 合并配置:以模板为基础,用当前赛事的数据覆盖
  617. imageConfigItems.value = allImageConfigs.map((template) => {
  618. const existingConfig = eventImageConfigs.find((item) => item.configKey === template.configKey);
  619. return {
  620. ...template,
  621. eventId: eventId,
  622. configValue: existingConfig?.configValue || '',
  623. isEnabled: existingConfig?.isEnabled || template.isEnabled || '0',
  624. status: existingConfig?.status || template.status || '0',
  625. configId: existingConfig?.configId
  626. };
  627. });
  628. } catch (error) {
  629. console.error('加载图片配置数据失败:', error);
  630. proxy?.$modal.msgError('加载图片配置数据失败: ' + (error as Error).message);
  631. imageConfigItems.value = [];
  632. }
  633. };
  634. // 刷新图片配置
  635. const refreshImageConfigs = async () => {
  636. const eventId = route.params.id as string;
  637. if (eventId && eventId !== 'add') {
  638. await loadImageConfigData(getEffectiveEventId(eventId));
  639. } else {
  640. // 新增模式,只加载配置模板
  641. // await loadImageConfigTemplates();
  642. }
  643. };
  644. // 加载图片配置模板(用于新增模式)
  645. // const loadImageConfigTemplates = async () => {
  646. // try {
  647. // const res = await listGameEventConfig({
  648. // eventId: '',
  649. // configType: 'IMAGE',
  650. // pageNum: 1,
  651. // pageSize: 100
  652. // });
  653. // // console.log('Template response (new mode):', res);
  654. // const imageData = Array.isArray(res.rows) ? res.rows : []; // 修改此处,确保正确访问数组数据
  655. // // console.log('Image data (new mode):', imageData);
  656. // imageConfigItems.value = imageData.map(item => ({
  657. // ...item,
  658. // configValue: item.configValue || '', // 确保 configValue 初始化正确
  659. // isEnabled: item.isEnabled || '0', // 确保 isEnabled 初始化正确
  660. // status: item.status || '0' // 确保 status 初始化正确
  661. // }));
  662. // // console.log('Image config items (new mode):', imageConfigItems.value);
  663. // } catch (error) {
  664. // console.error('加载图片配置模板失败:', error);
  665. // proxy?.$modal.msgError('加载图片配置模板失败: ' + (error as Error).message);
  666. // imageConfigItems.value = [];
  667. // }
  668. // };
  669. // 保存图片配置数据
  670. const saveImageConfigData = async (eventId?: string | number) => {
  671. try {
  672. // 如果是默认赛事操作,使用空字符串作为eventId
  673. const targetEventId = eventId === '' ? '' : (eventId || (route.params.id as string));
  674. const updates: GameEventConfigForm[] = [];
  675. const adds: GameEventConfigForm[] = [];
  676. // 收集需要更新和添加的数据
  677. for (const item of imageConfigItems.value) {
  678. if (item.configKey) {
  679. const configData: GameEventConfigForm = {
  680. ...item,
  681. eventId: targetEventId,
  682. configType: 'IMAGE'
  683. };
  684. if (item.configId && item.configValue) {
  685. updates.push(configData);
  686. } else if (!item.configId && item.configValue) {
  687. adds.push(configData);
  688. }
  689. }
  690. }
  691. // 批量更新
  692. if (updates.length > 0) {
  693. await Promise.all(updates.map(updateGameEventConfig));
  694. }
  695. // 批量添加
  696. if (adds.length > 0) {
  697. await Promise.all(adds.map(addGameEventConfig));
  698. }
  699. } catch (error) {
  700. console.error('保存图片配置数据失败:', error);
  701. throw new Error('保存图片配置失败');
  702. }
  703. };
  704. // 处理标签页切换
  705. const handleTabClick = () => {
  706. // 可以在这里添加标签页切换的逻辑
  707. };
  708. // 项目数据
  709. const projectItems = ref<GameEventProjectForm[]>([]);
  710. // 加载项目数据
  711. const loadProjectData = async (eventId: string | number) => {
  712. try {
  713. const res = await listGameEventProject({
  714. eventId: eventId === '' ? '' : eventId,
  715. pageNum: 1,
  716. pageSize: 1000,
  717. orderByColumn: '',
  718. isAsc: ''
  719. });
  720. projectItems.value = Array.isArray(res.rows) ? res.rows : [];
  721. } catch (error) {
  722. proxy?.$modal.msgError('加载项目数据失败');
  723. }
  724. };
  725. // 添加项目项
  726. const addProjectItem = () => {
  727. projectItems.value.push({
  728. eventId: getEffectiveEventId(),
  729. projectId: '',
  730. projectName: '',
  731. projectType: '',
  732. remark: '',
  733. location: '',
  734. scoreValue: '',
  735. groupType: ''
  736. });
  737. };
  738. // 删除项目项
  739. const removeProjectItem = (index: number) => {
  740. const projectItem = projectItems.value[index];
  741. if (projectItem.projectId) {
  742. delGameEventProject(projectItem.projectId).then(() => {
  743. projectItems.value.splice(index, 1);
  744. });
  745. } else {
  746. projectItems.value.splice(index, 1);
  747. }
  748. };
  749. //保存项目数据
  750. const saveProjectData = async (eventId: string | number) => {
  751. try {
  752. for (const item of projectItems.value) {
  753. // 确保eventId被正确设置
  754. const projectData = { ...item, eventId };
  755. if (item.projectId) {
  756. // 更新现有队伍
  757. await updateGameEventProject(projectData);
  758. } else if (item.projectName) {
  759. // 只有有名称的新队伍才保存
  760. await addGameEventProject(projectData);
  761. }
  762. }
  763. } catch (error) {
  764. console.error('保存赛事项目数据失败:', error);
  765. throw new Error('保存赛事项目失败');
  766. }
  767. };
  768. // 配置信息数据
  769. const configItems = ref<GameEventConfigForm[]>([]);
  770. // 配置类型选项
  771. const configTypes = ref<GameEventConfigTypeVO[]>([]);
  772. // 加载配置类型数据
  773. const loadConfigTypes = async () => {
  774. try {
  775. const res = await listGameEventConfigType({
  776. pageNum: 1,
  777. pageSize: 1000,
  778. orderByColumn: '',
  779. isAsc: ''
  780. });
  781. configTypes.value = Array.isArray(res.rows) ? res.rows : [];
  782. } catch (error) {
  783. proxy?.$modal.msgError('加载配置类型数据失败');
  784. }
  785. };
  786. // 加载赛事配置数据
  787. const loadConfigData = async (eventId: string | number) => {
  788. try {
  789. const res = await listGameEventConfig({
  790. eventId: eventId === '' ? '' : eventId,
  791. pageNum: 1,
  792. pageSize: 10,
  793. orderByColumn: '',
  794. isAsc: ''
  795. });
  796. configItems.value = Array.isArray(res.rows) ? res.rows : [];
  797. } catch (error) {
  798. console.error('加载赛事配置数据失败:', error);
  799. proxy?.$modal.msgError('加载赛事配置数据失败: ' + (error as Error).message);
  800. }
  801. };
  802. // 添加配置项
  803. const addConfigItem = () => {
  804. configItems.value.push({
  805. configKey: '',
  806. configValue: '',
  807. configDesc: '',
  808. configType: '', // 添加配置类型字段
  809. });
  810. };
  811. // 删除配置项
  812. const removeConfigItem = (index: number) => {
  813. const configItem = configItems.value[index];
  814. if (configItem.configId) {
  815. delGameEventConfig(configItem.configId).then(() => {
  816. configItems.value.splice(index, 1);
  817. });
  818. } else {
  819. configItems.value.splice(index, 1);
  820. }
  821. };
  822. // 保存赛事配置数据
  823. const saveconfigData = async (eventId: string | number) => {
  824. try {
  825. for (const item of configItems.value) {
  826. // 确保eventId被正确设置
  827. const configData = { ...item, eventId };
  828. if (item.configId) {
  829. // 更新现有队伍
  830. await updateGameEventConfig(configData);
  831. } else if (item.configDesc) {
  832. // 只有有名称的新队伍才保存
  833. await addGameEventConfig(configData);
  834. }
  835. }
  836. } catch (error) {
  837. console.error('保存赛事配置信息失败:', error);
  838. throw new Error('保存赛事配置信息失败');
  839. }
  840. };
  841. // 参赛队伍数据
  842. const teamItems = ref<GameTeamForm[]>([]);
  843. // 加载队伍数据
  844. const loadTeamData = async (eventId: string | number) => {
  845. try {
  846. const res = await listGameTeam({
  847. eventId: eventId === '' ? '' : eventId,
  848. pageNum: 1,
  849. pageSize: 1000,
  850. orderByColumn: '',
  851. isAsc: ''
  852. });
  853. teamItems.value = Array.isArray(res.rows) ? res.rows : [];
  854. } catch (error) {
  855. proxy?.$modal.msgError('加载队伍数据失败');
  856. }
  857. };
  858. // 添加参赛队伍项
  859. const addTeamItem = () => {
  860. teamItems.value.push({
  861. teamName: '',
  862. teamCode: '',
  863. eventId: getEffectiveEventId(),
  864. teamDescribe: '',
  865. remark: ''
  866. });
  867. };
  868. // 删除参赛队伍项
  869. const removeTeamItem = (index: number) => {
  870. const teamItem = teamItems.value[index];
  871. if (teamItem.teamId) {
  872. delGameTeam(teamItem.teamId).then(() => {
  873. teamItems.value.splice(index, 1);
  874. });
  875. } else {
  876. teamItems.value.splice(index, 1);
  877. }
  878. };
  879. // 保存参赛队伍数据
  880. const saveTeamData = async (eventId: string | number) => {
  881. try {
  882. for (const item of teamItems.value) {
  883. // 确保eventId被正确设置
  884. const teamData = { ...item, eventId };
  885. if (item.teamId) {
  886. // 更新现有队伍
  887. await updateGameTeam(teamData);
  888. } else if (item.teamName) {
  889. // 只有有名称的新队伍才保存
  890. await addGameTeam(teamData);
  891. }
  892. }
  893. } catch (error) {
  894. console.error('保存参赛队伍数据失败:', error);
  895. throw new Error('保存参赛队伍失败');
  896. }
  897. };
  898. // 保存赛事信息
  899. const saveEvent = async () => {
  900. try {
  901. // 验证基本信息表单
  902. await basicFormRef.value?.validate();
  903. saveLoading.value = true;
  904. // 合并表单数据
  905. const formData: GameEventForm = {
  906. ...basicForm.value
  907. };
  908. let savedEventId: string;
  909. // 如果设置为默认赛事,则取消其他赛事的默认状态
  910. if(basicForm.value.isDefault === '0'){
  911. handleStatusChange({
  912. eventId: basicForm.value.eventId,
  913. isDefault: basicForm.value.isDefault,
  914. eventName: basicForm.value.eventName
  915. } as GameEventVO);
  916. }
  917. // 保存基本信息
  918. if (isEdit.value) {
  919. await updateGameEvent(formData);
  920. savedEventId = route.params.id as string;
  921. } else {
  922. const addRes = await addGameEvent(formData);
  923. // 假设返回的数据中包含新创建的赛事ID
  924. savedEventId = addRes?.data as string;
  925. }
  926. // 获取有效的赛事ID用于保存相关数据
  927. const effectiveEventId = getEffectiveEventId(savedEventId);
  928. // 保存图片配置数据
  929. await saveImageConfigData(effectiveEventId);
  930. // 保存赛事配置项数据
  931. await saveconfigData(effectiveEventId);
  932. // 保存菜单数据
  933. await saveMenuData(effectiveEventId);
  934. // 保存参赛队伍数据
  935. await saveTeamData(effectiveEventId);
  936. // 保存赛事项目分组数据
  937. await saveGroupData(effectiveEventId);
  938. // 保存赛事项目数据
  939. await saveProjectData(effectiveEventId);
  940. proxy?.$modal.msgSuccess('保存成功');
  941. // 保存成功后,设置一个标识,表示需要刷新列表数据
  942. sessionStorage.setItem('needRefreshGameEventList', 'true');
  943. goBack();
  944. } catch (error) {
  945. console.error('保存失败:', error);
  946. proxy?.$modal.msgError('保存失败');
  947. } finally {
  948. saveLoading.value = false;
  949. }
  950. };
  951. // 返回上一页
  952. const goBack = () => {
  953. router.go(-1);
  954. };
  955. </script>
  956. <style lang="scss" scoped>
  957. .game-event-edit {
  958. .page-header {
  959. display: flex;
  960. align-items: center;
  961. gap: 16px;
  962. .page-title {
  963. font-size: 18px;
  964. font-weight: 600;
  965. }
  966. }
  967. .menu-config,
  968. .project-config,
  969. .config-info,
  970. .image-config {
  971. .menu-header,
  972. .project-header,
  973. .config-header,
  974. .image-header {
  975. display: flex;
  976. justify-content: space-between;
  977. align-items: center;
  978. margin-bottom: 16px;
  979. h3 {
  980. margin: 0;
  981. font-size: 16px;
  982. font-weight: 600;
  983. }
  984. }
  985. }
  986. .image-config {
  987. .config-info {
  988. margin-top: 8px;
  989. display: flex;
  990. align-items: center;
  991. justify-content: space-between;
  992. }
  993. }
  994. .team-config {
  995. .team-header {
  996. display: flex;
  997. justify-content: space-between;
  998. align-items: center;
  999. margin-bottom: 16px;
  1000. h3 {
  1001. margin: 0;
  1002. font-size: 16px;
  1003. font-weight: 600;
  1004. }
  1005. }
  1006. }
  1007. .group-config {
  1008. .group-header {
  1009. display: flex;
  1010. justify-content: space-between;
  1011. align-items: center;
  1012. margin-bottom: 16px;
  1013. h3 {
  1014. margin: 0;
  1015. font-size: 16px;
  1016. font-weight: 600;
  1017. }
  1018. }
  1019. }
  1020. .form-actions {
  1021. display: flex;
  1022. justify-content: center;
  1023. gap: 16px;
  1024. margin-top: 32px;
  1025. padding-top: 24px;
  1026. border-top: 1px solid #ebeef5;
  1027. }
  1028. }
  1029. :deep(.el-tabs__content) {
  1030. padding: 20px 0;
  1031. }
  1032. :deep(.el-form-item) {
  1033. margin-bottom: 20px;
  1034. }
  1035. </style>