Pārlūkot izejas kodu

Merge branch 'dev' of http://192.168.1.121/yp_web/game_event_admin into dev

zhou 2 mēneši atpakaļ
vecāks
revīzija
1121946885

+ 1 - 1
package.json

@@ -29,7 +29,7 @@
     "await-to-js": "3.0.0",
     "axios": "1.8.4",
     "crypto-js": "4.2.0",
-    "echarts": "5.6.0",
+    "echarts": "^5.6.0",
     "element-plus": "2.9.8",
     "file-saver": "2.0.5",
     "highlight.js": "11.9.0",

+ 8 - 0
src/api/monitor/online/index.ts

@@ -34,3 +34,11 @@ export function delOnline(tokenId: string) {
     method: 'delete'
   });
 }
+
+// 获取当前登录用户数量
+export function countOnlineUser() {
+  return request({
+    url: '/monitor/online/count',
+    method: 'get'
+  });
+}

+ 11 - 0
src/api/system/article/index.ts

@@ -61,3 +61,14 @@ export const delArticle = (id: string | number | Array<string | number>) => {
     method: 'delete'
   });
 };
+
+/**
+ * 查询文章数量
+ * @returns {*}
+ */
+export const getArticleCount = () => {
+  return request({
+    url: '/system/article/count',
+    method: 'get'
+  });
+};

+ 1 - 0
src/api/system/gameAthlete/index.ts

@@ -61,3 +61,4 @@ export const delGameAthlete = (athleteId: string | number | Array<string | numbe
     method: 'delete'
   });
 };
+

+ 12 - 0
src/api/system/gameEvent/index.ts

@@ -101,3 +101,15 @@ export function changeEventDefault(eventId: string | number, isDefault: string)
     data: data
   });
 }
+
+/**
+ * 查询赛事数量
+ * @returns {*}
+ */
+
+export const getEventCount = (type: number) => {
+  return request({
+    url: '/system/gameEvent/count/' + type,
+    method: 'get'
+  });
+};

+ 11 - 0
src/api/system/gameEventProject/index.ts

@@ -61,3 +61,14 @@ export const delGameEventProject = (projectId: string | number | Array<string |
     method: 'delete'
   });
 };
+
+/**
+ * 查询赛事项目数量
+ * @returns {*}
+ */
+export const getEventProjectCount = () => {
+  return request({
+    url: '/system/gameEventProject/count',
+    method: 'get'
+  });
+};

+ 6 - 6
src/api/system/gameEventProject/types.ts

@@ -102,7 +102,6 @@ export interface GameEventProjectVO {
    * 备注
    */
   remark: string;
-
 }
 
 export interface GameEventProjectForm extends BaseEntity {
@@ -210,15 +209,19 @@ export interface GameEventProjectForm extends BaseEntity {
    * 备注
    */
   remark?: string;
-
 }
 
 export interface GameEventProjectQuery extends PageQuery {
-
   /**
    * 赛事ID
    */
   eventId?: string | number;
+
+  /**
+   * 赛事名称
+   */
+  eventName?: string;
+
   /**
    * 项目ID
    */
@@ -259,6 +262,3 @@ export interface GameEventProjectQuery extends PageQuery {
    */
   params?: any;
 }
-
-
-

+ 11 - 0
src/api/system/gameReferee/index.ts

@@ -61,3 +61,14 @@ export const delGameReferee = (refereeId: string | number | Array<string | numbe
     method: 'delete'
   });
 };
+
+/**
+ * 查询裁判数量
+ * @returns {*}
+ */
+export const getRefereeCount = () => {
+  return request({
+    url: '/system/gameReferee/count',
+    method: 'get'
+  });
+};

+ 11 - 0
src/api/system/gameTeam/index.ts

@@ -61,3 +61,14 @@ export const delGameTeam = (teamId: string | number | Array<string | number>) =>
     method: 'delete'
   });
 };
+
+/**
+ * 查询参赛队伍数量
+ * @returns {*}
+ */
+export const getTeamCount = () => {
+  return request({
+    url: '/system/gameTeam/count',
+    method: 'get'
+  });
+};

+ 12 - 8
src/api/system/gameTeam/types.ts

@@ -1,13 +1,18 @@
 export interface GameTeamVO {
   /**
- * 队伍ID
- */
+   * 队伍ID
+   */
   teamId: string | number;
   /**
    * 赛事ID
    */
   eventId: string | number;
 
+  /**
+   * 赛事名称
+   */
+  eventName: string;
+
   /**
    * 队伍名称
    */
@@ -57,7 +62,6 @@ export interface GameTeamVO {
    * 备注
    */
   remark: string;
-
 }
 
 export interface GameTeamForm extends BaseEntity {
@@ -119,16 +123,19 @@ export interface GameTeamForm extends BaseEntity {
    * 备注
    */
   remark?: string;
-
 }
 
 export interface GameTeamQuery extends PageQuery {
-
   /**
    * 赛事ID
    */
   eventId?: string | number;
 
+  /**
+   * 赛事名称
+   */
+  eventName?: string;
+
   /**
    * 队伍名称
    */
@@ -179,6 +186,3 @@ export interface GameTeamQuery extends PageQuery {
    */
   params?: any;
 }
-
-
-

+ 4 - 4
src/components/Editor/index.vue

@@ -59,7 +59,7 @@ const upload = reactive<UploadOption>({
 const quillEditorRef = ref();
 const uploadRef = ref<HTMLDivElement>();
 
-const options = ref<any>({
+const options = computed(() => ({
   theme: 'snow',
   bounds: document.body,
   debug: 'warn',
@@ -84,15 +84,15 @@ const options = ref<any>({
             // 调用element图片上传
             uploadRef.value.click();
           } else {
-            Quill.format('image', true);
+            // Quill.format('image', true);
           }
         }
       }
     }
   },
-  placeholder: '请输入内容',
+  placeholder: props.readOnly ? '' : '请输入内容',
   readOnly: props.readOnly
-});
+}));
 
 const styles = computed(() => {
   const style: any = {};

+ 20 - 16
src/components/ImageOrUrlInput/index.vue

@@ -19,6 +19,7 @@
             :file-list="fileList"
             :on-preview="handlePictureCardPreview"
             :class="{ hide: fileList.length >= 1 }"
+            style="width: 300px"
           >
             <el-icon class="avatar-uploader-icon">
               <plus />
@@ -36,22 +37,16 @@
           </div>
         </div>
       </el-tab-pane>
-      
+
       <el-tab-pane label="输入链接" name="url">
-         <div class="url-container">
-           <el-input
-             v-model="urlValue"
-             placeholder="请输入链接地址"
-             clearable
-             @input="handleUrlInput"
-             @blur="handleUrlBlur"
-           >
-             <template #prepend>URL</template>
-           </el-input>
-           <div class="url-error" v-if="urlValue && !isValidUrl">
-             <el-alert title="请输入有效的链接地址" type="warning" :closable="false" />
-           </div>
-         </div>
+        <div class="url-container">
+          <el-input v-model="urlValue" placeholder="请输入链接地址" clearable @input="handleUrlInput" @blur="handleUrlBlur">
+            <template #prepend>URL</template>
+          </el-input>
+          <div class="url-error" v-if="urlValue && !isValidUrl">
+            <el-alert title="请输入有效的链接地址" type="warning" :closable="false" />
+          </div>
+        </div>
       </el-tab-pane>
     </el-tabs>
 
@@ -67,6 +62,7 @@ import { OssVO } from '@/api/system/oss/types';
 import { propTypes } from '@/utils/propTypes';
 import { globalHeaders } from '@/utils/request';
 import { compressAccurately } from 'image-conversion';
+import { Plus } from '@element-plus/icons-vue';
 
 const props = defineProps({
   modelValue: {
@@ -324,4 +320,12 @@ const listToString = (list: any[], separator?: string) => {
 :deep(.hide .el-upload--picture-card) {
   display: none;
 }
-</style> 
+
+.el-upload-list--picture-card {
+  --el-upload-list-picture-card-size: 148px;
+  display: inline-flex;
+  flex-wrap: wrap;
+  margin: auto 0;
+  width: 150px;
+}
+</style>

+ 5 - 0
src/types/global.d.ts

@@ -53,6 +53,7 @@ declare global {
     /** 其他参数 */
     [key: string]: any;
   }
+
   /**
    * 字典数据  数据配置
    */
@@ -81,13 +82,17 @@ declare global {
     queryParams: D;
     rules: ElFormRules;
   }
+
   /**
    * 分页查询参数
    */
   declare interface PageQuery {
     pageNum: number;
     pageSize: number;
+    orderByColumn: string;
+    isAsc: string;
   }
+
   declare interface LayoutSetting {
     /**
      * 是否显示顶部导航

+ 791 - 131
src/views/index.vue

@@ -1,165 +1,825 @@
 <template>
-  <div class="app-container home">
-    <el-row :gutter="20">
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Vue-Plus多租户管理系统</h2>
-        <p>
-          RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
-          <br />
-          * 前端开发框架 Vue3、TS、Element Plus<br />
-          * 后端开发框架 Spring Boot<br />
-          * 容器框架 Undertow 基于 Netty 的高性能容器<br />
-          * 权限认证框架 Sa-Token 支持多终端认证系统<br />
-          * 关系数据库 MySQL 适配 8.X 最低 5.7<br />
-          * 缓存数据库 Redis 适配 6.X 最低 4.X<br />
-          * 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
-          * 数据库框架 p6spy 更强劲的 SQL 分析<br />
-          * 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
-          * 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
-          * Redis客户端 Redisson 性能强劲、API丰富<br />
-          * 分布式限流 Redisson 全局、请求IP、集群ID 多种限流<br />
-          * 分布式锁 Lock4j 注解锁、工具锁 多种多样<br />
-          * 分布式幂等 Lock4j 基于分布式锁实现<br />
-          * 分布式链路追踪 SkyWalking 支持链路追踪、网格分析、度量聚合、可视化<br />
-          * 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
-          * 文件存储 Minio 本地存储<br />
-          * 文件存储 七牛、阿里、腾讯 云存储<br />
-          * 监控框架 SpringBoot-Admin 全方位服务监控<br />
-          * 校验框架 Validation 增强接口安全性 严谨性<br />
-          * Excel框架 FastExcel(原Alibaba EasyExcel) 性能优异 扩展性强<br />
-          * 文档框架 SpringDoc、javadoc 无注解零入侵基于java注释<br />
-          * 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性<br />
-          * 代码生成器 适配MP、SpringDoc规范化代码 一键生成前后端代码<br />
-          * 部署方式 Docker 容器编排 一键部署业务集群<br />
-          * 国际化 SpringMessage Spring标准国际化方案<br />
-        </p>
-        <p><b>当前版本:</b> <span>v5.4.1</span></p>
-        <p>
-          <el-tag type="danger">&yen;免费开源</el-tag>
-        </p>
-        <p>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
-            >更新日志</el-button
-          >
-        </p>
+  <div class="dashboard-container">
+    <!-- 顶部标题区域 -->
+    <div class="dashboard-header">
+      <h1 class="dashboard-title">
+        <el-icon class="title-icon">
+          <TrendCharts />
+        </el-icon>
+        赛事管理系统仪表板
+      </h1>
+      <p class="dashboard-subtitle">实时监控赛事数据,洞察运营状况</p>
+    </div>
+
+    <!-- 统计卡片区域 -->
+    <div class="stats-grid">
+      <div class="stat-card" v-for="(item, index) in statsData" :key="index">
+        <div class="stat-icon" :style="{ backgroundColor: item.color }">
+          <el-icon :size="24">
+            <component :is="item.icon" />
+          </el-icon>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ item.loading ? '-' : item.value }}</div>
+          <div class="stat-label">{{ item.label }}</div>
+        </div>
+      </div>
+    </div>
+    <!-- 快速操作区域 -->
+    <el-card class="quick-actions-card">
+      <template #header>
+        <div class="card-header">
+          <span>快速操作</span>
+          <el-icon>
+            <Operation />
+          </el-icon>
+        </div>
+      </template>
+      <div class="quick-actions">
+        <el-button
+          v-for="action in quickActions"
+          :key="action.name"
+          :type="action.type"
+          :icon="action.icon"
+          @click="handleQuickAction(action.action)"
+          class="action-btn"
+        >
+          {{ action.name }}
+        </el-button>
+      </div>
+    </el-card>
+    <!-- 图表和详细信息区域 -->
+    <el-row :gutter="20" class="charts-section">
+      <!-- 数据分布饼图 -->
+      <el-col :xs="24" :sm="24" :md="12" :lg="8">
+        <el-card class="chart-card">
+          <template #header>
+            <div class="card-header">
+              <span>数据分布</span>
+              <el-icon>
+                <PieChart />
+              </el-icon>
+            </div>
+          </template>
+          <div id="pieChart" class="chart-container"></div>
+        </el-card>
+      </el-col>
+
+      <!-- 赛事状态监控 -->
+      <el-col :xs="24" :sm="24" :md="12" :lg="8">
+        <el-card class="chart-card">
+          <template #header>
+            <div class="card-header">
+              <span>赛事状态</span>
+              <el-icon>
+                <Trophy />
+              </el-icon>
+            </div>
+          </template>
+          <div class="event-status">
+            <div class="status-item">
+              <div class="status-icon upcoming">
+                <el-icon :size="20">
+                  <Clock />
+                </el-icon>
+              </div>
+              <div class="status-content">
+                <div class="status-count">{{ eventStatus.upcoming }}</div>
+                <div class="status-label">未开始</div>
+              </div>
+            </div>
+            <div class="status-item">
+              <div class="status-icon ongoing">
+                <el-icon :size="20">
+                  <VideoPlay />
+                </el-icon>
+              </div>
+              <div class="status-content">
+                <div class="status-count">{{ eventStatus.ongoing }}</div>
+                <div class="status-label">进行中</div>
+              </div>
+            </div>
+            <div class="status-item">
+              <div class="status-icon completed">
+                <el-icon :size="20">
+                  <CircleCheck />
+                </el-icon>
+              </div>
+              <div class="status-content">
+                <div class="status-count">{{ eventStatus.completed }}</div>
+                <div class="status-label">已结束</div>
+              </div>
+            </div>
+            <div class="status-item">
+              <div class="status-icon total">
+                <el-icon :size="20">
+                  <DataBoard />
+                </el-icon>
+              </div>
+              <div class="status-content">
+                <div class="status-count">{{ eventStatus.total }}</div>
+                <div class="status-label">总计</div>
+              </div>
+            </div>
+          </div>
+        </el-card>
       </el-col>
 
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Cloud-Plus多租户微服务管理系统</h2>
-        <p>
-          RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
-          <br />
-          * 前端开发框架 Vue3、TS、Element UI<br />
-          * 后端开发框架 Spring Boot<br />
-          * 微服务开发框架 Spring Cloud、Spring Cloud Alibaba<br />
-          * 容器框架 Undertow 基于 XNIO 的高性能容器<br />
-          * 权限认证框架 Sa-Token、Jwt 支持多终端认证系统<br />
-          * 关系数据库 MySQL 适配 8.X 最低 5.7<br />
-          * 关系数据库 Oracle 适配 11g 12c<br />
-          * 关系数据库 PostgreSQL 适配 13 14<br />
-          * 关系数据库 SQLServer 适配 2017 2019<br />
-          * 缓存数据库 Redis 适配 6.X 最低 5.X<br />
-          * 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
-          * 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
-          * 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
-          * 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
-          * RPC远程调用 Apache Dubbo 原生态使用体验、高性能<br />
-          * 分布式限流熔断 Alibaba Sentinel 无侵入、高扩展<br />
-          * 分布式事务 Alibaba Seata 无侵入、高扩展 支持 四种模式<br />
-          * 分布式消息队列 Apache Kafka 高性能高速度<br />
-          * 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
-          * 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
-          * 分布式搜索引擎 ElasticSearch 业界知名<br />
-          * 分布式链路追踪 Apache SkyWalking 链路追踪、网格分析、度量聚合、可视化<br />
-          * 分布式日志中心 ELK 业界成熟解决方案<br />
-          * 分布式监控 Prometheus、Grafana 全方位性能监控<br />
-          * 其余与 Vue 版本一致<br />
-        </p>
-        <p><b>当前版本:</b> <span>v2.4.1</span></p>
-        <p>
-          <el-tag type="danger">&yen;免费开源</el-tag>
-        </p>
-        <p>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
-            >更新日志</el-button
-          >
-        </p>
+      <!-- 系统信息 -->
+      <el-col :xs="24" :sm="24" :md="24" :lg="8">
+        <el-card class="chart-card">
+          <template #header>
+            <div class="card-header">
+              <span>系统信息</span>
+              <el-icon>
+                <Monitor />
+              </el-icon>
+            </div>
+          </template>
+          <div class="system-info">
+            <div class="info-item">
+              <span class="info-label">系统版本</span>
+              <span class="info-value">v5.4.1</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">运行状态</span>
+              <el-tag type="success">正常运行</el-tag>
+            </div>
+            <div class="info-item">
+              <span class="info-label">在线用户</span>
+              <span class="info-value">{{ onlineUsers }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">数据更新</span>
+              <span class="info-value">{{ lastUpdateTime }}</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 最新动态 -->
+    <el-row :gutter="20" class="activity-section">
+      <el-col :xs="24" :sm="24" :md="12">
+        <el-card class="activity-card">
+          <template #header>
+            <div class="card-header">
+              <span>最新赛事</span>
+              <el-icon>
+                <Calendar />
+              </el-icon>
+            </div>
+          </template>
+          <div class="activity-list">
+            <div v-for="event in recentEvents" :key="event.id" class="activity-item">
+              <div class="activity-dot"></div>
+              <div class="activity-content">
+                <div class="activity-title">{{ event.eventName }}</div>
+                <div class="activity-time">{{ event.updateTime }}</div>
+              </div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :xs="24" :sm="24" :md="12">
+        <el-card class="activity-card">
+          <template #header>
+            <div class="card-header">
+              <span>系统公告</span>
+              <el-icon>
+                <Bell />
+              </el-icon>
+            </div>
+          </template>
+          <div class="activity-list">
+            <div v-for="notice in systemNotices" :key="notice.id" class="activity-item">
+              <div class="activity-dot notice"></div>
+              <div class="activity-content">
+                <div class="activity-title">{{ notice.noticeTitle }}</div>
+                <div class="activity-time">{{ notice.createTime }}</div>
+              </div>
+            </div>
+          </div>
+        </el-card>
       </el-col>
     </el-row>
-    <el-divider />
   </div>
 </template>
 
 <script setup name="Index" lang="ts">
-const goTarget = (url: string) => {
-  window.open(url, '__blank');
+import { ref, nextTick, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import { TrendCharts, PieChart, Monitor, Operation, Calendar, Bell, Trophy, Clock, VideoPlay, CircleCheck, DataBoard } from '@element-plus/icons-vue';
+import * as echarts from 'echarts';
+import { getArticleCount } from '@/api/system/article';
+import { getEventCount, listGameEvent } from '@/api/system/gameEvent';
+import { getEventProjectCount } from '@/api/system/gameEventProject';
+import { getRefereeCount } from '@/api/system/gameReferee';
+import { getTeamCount } from '@/api/system/gameTeam';
+import { listNotice } from '@/api/system/notice';
+import { countOnlineUser } from '@/api/monitor/online';
+
+// 响应式数据
+const statsData = ref([
+  {
+    label: '文章数量',
+    value: 0,
+    icon: 'DataBoard',
+    color: '#409EFF',
+    loading: true
+  },
+  {
+    label: '参赛队伍',
+    value: 0,
+    icon: 'User',
+    color: '#67C23A',
+    loading: true
+  },
+  {
+    label: '赛事',
+    value: 0,
+    icon: 'Trophy',
+    color: '#E6A23C',
+    loading: true
+  },
+  {
+    label: '赛事项目',
+    value: 0,
+    icon: 'Promotion',
+    color: '#F56C6C',
+    loading: true
+  },
+  {
+    label: '裁判',
+    value: 0,
+    icon: 'Flag',
+    color: '#909399',
+    loading: true
+  }
+]);
+const router = useRouter();
+const onlineUsers = ref(1);
+const lastUpdateTime = ref('');
+
+const quickActions = ref([
+  { name: '新增赛事', type: 'primary' as const, icon: 'Plus', action: 'addEvent' },
+  { name: '参赛队伍管理', type: 'success' as const, icon: 'User', action: 'manageTeam' },
+  { name: '裁判管理', type: 'warning' as const, icon: 'Flag', action: 'manageReferees' },
+  { name: '数据导出', type: 'info' as const, icon: 'Download', action: 'exportData' },
+  { name: '刷新数据', type: 'default' as const, icon: 'Refresh', action: 'refresh' }
+]);
+
+const recentEvents = ref([]);
+
+const systemNotices = ref([]);
+
+const eventStatus = ref({
+  upcoming: 0,
+  ongoing: 0,
+  completed: 0,
+  total: 0
+});
+
+/**
+ * 获取最新赛事
+ */
+const loadRecentEvents = async () => {
+  try {
+    const res = await listGameEvent({
+      pageNum: 1,
+      pageSize: 4,
+      orderByColumn: 'update_time',
+      isAsc: 'desc'
+    });
+    if (res.code === 200 && res.total > 0) {
+      recentEvents.value = res.rows;
+    }
+  } catch (error) {
+    console.error('加载最新赛事失败:', error);
+  }
+};
+
+/**
+ * 获取公告
+ */
+const loadNotice = async () => {
+  try {
+    const res = await listNotice({
+      pageNum: 1,
+      pageSize: 4,
+      noticeTitle: undefined,
+      createByName: undefined,
+      status: undefined,
+      noticeType: undefined,
+      orderByColumn: 'update_time',
+      isAsc: 'desc'
+    });
+    if (res.code === 200 && res.total > 0) {
+      systemNotices.value = res.rows;
+    }
+  } catch (error) {
+    console.error('加载公告失败:', error);
+  }
+};
+
+/**
+ * 加载赛事状态统计
+ */
+const loadEventStatus = async () => {
+  try {
+    let upcoming = 0,
+      ongoing = 0,
+      completed = 0,
+      total = 0;
+    let res = await getEventCount(0);
+    total = res.data;
+    res = await getEventCount(1);
+    upcoming = res.data;
+    res = await getEventCount(2);
+    ongoing = res.data;
+    res = await getEventCount(3);
+    completed = res.data;
+
+    eventStatus.value = {
+      upcoming,
+      ongoing,
+      completed,
+      total
+    };
+  } catch (error) {
+    console.error('加载赛事状态失败:', error);
+  }
+};
+
+/**
+ * 获取统计数量
+ */
+const loadStatistics = async () => {
+  try {
+    // 并行请求所有统计接口
+    const results = await Promise.allSettled([getArticleCount(), getTeamCount(), getEventCount(0), getEventProjectCount(), getRefereeCount()]);
+
+    let hasError = false;
+
+    // 处理文章数量
+    if (results[0].status === 'fulfilled') {
+      statsData.value[0].value = results[0].value.data || 0;
+    } else {
+      console.error('获取文章数量失败:', results[0].reason);
+      statsData.value[0].value = 0;
+      hasError = true;
+    }
+    statsData.value[0].loading = false;
+
+    // 处理参赛队伍数量
+    if (results[1].status === 'fulfilled') {
+      statsData.value[1].value = results[1].value.data || 0;
+    } else {
+      console.error('获取参赛队伍数量失败:', results[1].reason);
+      statsData.value[1].value = 0;
+      hasError = true;
+    }
+    statsData.value[1].loading = false;
+
+    // 处理赛事数量
+    if (results[2].status === 'fulfilled') {
+      statsData.value[2].value = results[2].value.data || 0;
+    } else {
+      console.error('获取赛事数量失败:', results[2].reason);
+      statsData.value[2].value = 0;
+      hasError = true;
+    }
+    statsData.value[2].loading = false;
+
+    // 处理赛事项目数量
+    if (results[3].status === 'fulfilled') {
+      statsData.value[3].value = results[3].value.data || 0;
+    } else {
+      console.error('获取赛事项目数量失败:', results[3].reason);
+      statsData.value[3].value = 0;
+      hasError = true;
+    }
+    statsData.value[3].loading = false;
+
+    // 处理裁判数量
+    if (results[4].status === 'fulfilled') {
+      statsData.value[4].value = results[4].value.data || 0;
+    } else {
+      console.error('获取裁判数量失败:', results[4].reason);
+      statsData.value[4].value = 0;
+      hasError = true;
+    }
+    statsData.value[4].loading = false;
+
+    // 如果有接口错误,显示提示
+    if (hasError) {
+      ElMessage({
+        message: '部分统计数据获取失败,请检查网络连接或稍后重试',
+        type: 'warning',
+        duration: 3000
+      });
+    }
+
+    // 初始化图表
+    nextTick(() => {
+      initCharts();
+    });
+  } catch (error) {
+    console.error('加载统计数据失败:', error);
+    ElMessage({
+      message: '统计数据加载失败,请检查网络连接',
+      type: 'error',
+      duration: 5000
+    });
+    // 设置所有统计数据为0并停止加载状态
+    statsData.value.forEach((item) => {
+      item.value = 0;
+      item.loading = false;
+    });
+  }
+};
+
+const initCharts = () => {
+  initPieChart();
 };
+
+const initPieChart = () => {
+  const chartDom = document.getElementById('pieChart');
+  if (!chartDom) return;
+
+  const myChart = echarts.init(chartDom);
+  const option = {
+    tooltip: {
+      trigger: 'item'
+    },
+    legend: {
+      orient: 'vertical',
+      left: 'left'
+    },
+    series: [
+      {
+        name: '数据分布',
+        type: 'pie',
+        radius: '50%',
+        data: [
+          { value: statsData.value[0].value, name: '文章' },
+          { value: statsData.value[1].value, name: '参赛队伍' },
+          { value: statsData.value[2].value, name: '赛事' },
+          { value: statsData.value[3].value, name: '项目' },
+          { value: statsData.value[4].value, name: '裁判' }
+        ],
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowOffsetX: 0,
+            shadowColor: 'rgba(0, 0, 0, 0.5)'
+          }
+        }
+      }
+    ]
+  };
+  myChart.setOption(option);
+};
+
+const handleQuickAction = (action: string) => {
+  switch (action) {
+    case 'addEvent':
+      // 跳转到新增赛事页面
+      router.push(`/system/gameEvent/add`);
+      break;
+    case 'manageAthletes':
+      // 跳转到参赛队伍管理页面
+      router.push(`/game/gameTeam`);
+      break;
+    case 'manageReferees':
+      // 跳转到裁判管理页面
+      router.push(`/game/gameReferee`);
+      break;
+    case 'exportData':
+      // 导出数据
+      ElMessage({
+        message: '请到具体的管理页面进行导出',
+        type: 'info'
+      });
+      break;
+    case 'refresh':
+      // 刷新统计数据和最新赛事
+      getAllData();
+      ElMessage({
+        message: '正在刷新数据...',
+        type: 'info'
+      });
+      break;
+  }
+};
+
+const getOnlineUser = async () => {
+  const res = await countOnlineUser();
+  onlineUsers.value = res.data;
+};
+
+const updateTime = () => {
+  const now = new Date();
+  lastUpdateTime.value = now.toLocaleTimeString();
+};
+
+const getAllData = async () => {
+  loadStatistics();
+  loadRecentEvents();
+  loadNotice();
+  loadEventStatus();
+  getOnlineUser();
+};
+
+onMounted(() => {
+  getAllData();
+  updateTime();
+  // 每分钟更新一次时间
+  setInterval(updateTime, 60000);
+});
 </script>
 
 <style lang="scss" scoped>
-.home {
-  blockquote {
-    padding: 10px 20px;
-    margin: 0 0 20px;
-    font-size: 17.5px;
-    border-left: 5px solid #eee;
+.dashboard-container {
+  padding: 20px;
+  background: #f5f7fa;
+  min-height: calc(100vh - 84px);
+}
+
+.dashboard-header {
+  text-align: center;
+  margin-bottom: 30px;
+
+  .dashboard-title {
+    font-size: 28px;
+    color: #303133;
+    margin: 0 0 10px 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 10px;
+
+    .title-icon {
+      color: #409eff;
+    }
   }
-  hr {
-    margin-top: 20px;
-    margin-bottom: 20px;
-    border: 0;
-    border-top: 1px solid #eee;
+
+  .dashboard-subtitle {
+    font-size: 16px;
+    color: #909399;
+    margin: 0;
   }
-  .col-item {
-    margin-bottom: 20px;
+}
+
+.stats-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+  gap: 20px;
+  margin-bottom: 30px;
+}
+
+.stat-card {
+  background: white;
+  border-radius: 12px;
+  padding: 24px;
+  display: flex;
+  align-items: center;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
+  transition: all 0.3s ease;
+
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.12);
   }
+}
 
-  ul {
-    padding: 0;
-    margin: 0;
+.stat-icon {
+  width: 60px;
+  height: 60px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 16px;
+  color: white;
+}
+
+.stat-content {
+  flex: 1;
+}
+
+.stat-value {
+  font-size: 24px;
+  font-weight: bold;
+  color: #303133;
+  line-height: 1;
+  margin-bottom: 4px;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.stat-trend {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  font-size: 12px;
+
+  &.up {
+    color: #67c23a;
   }
 
-  font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-  font-size: 13px;
-  color: #676a6c;
-  overflow-x: hidden;
+  &.down {
+    color: #f56c6c;
+  }
+}
+
+.charts-section {
+  margin-bottom: 30px;
+}
 
-  ul {
-    list-style-type: none;
+.chart-card {
+  height: 400px;
+
+  .card-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-weight: bold;
   }
 
-  h4 {
-    margin-top: 0px;
+  .chart-container {
+    height: 320px;
   }
+}
 
-  h2 {
-    margin-top: 10px;
-    font-size: 26px;
-    font-weight: 100;
+.system-info {
+  .info-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 12px 0;
+    border-bottom: 1px solid #f0f0f0;
+
+    &:last-child {
+      border-bottom: none;
+    }
   }
 
-  p {
-    margin-top: 10px;
+  .info-label {
+    color: #909399;
+    font-size: 14px;
+  }
 
-    b {
-      font-weight: 700;
+  .info-value {
+    color: #303133;
+    font-weight: 500;
+  }
+}
+
+.quick-actions-card {
+  margin-bottom: 30px;
+}
+
+.quick-actions {
+  display: flex;
+  gap: 16px;
+  flex-wrap: wrap;
+
+  .action-btn {
+    flex: 1;
+    min-width: 120px;
+  }
+}
+
+.activity-section {
+  .activity-card {
+    .card-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      font-weight: bold;
+    }
+  }
+}
+
+.activity-list {
+  .activity-item {
+    display: flex;
+    align-items: flex-start;
+    padding: 12px 0;
+    border-bottom: 1px solid #f0f0f0;
+
+    &:last-child {
+      border-bottom: none;
     }
   }
 
-  .update-log {
-    ol {
-      display: block;
-      list-style-type: decimal;
-      margin-block-start: 1em;
-      margin-block-end: 1em;
-      margin-inline-start: 0;
-      margin-inline-end: 0;
-      padding-inline-start: 40px;
+  .activity-dot {
+    width: 8px;
+    height: 8px;
+    border-radius: 50%;
+    background: #409eff;
+    margin-top: 6px;
+    margin-right: 12px;
+    flex-shrink: 0;
+
+    &.notice {
+      background: #e6a23c;
     }
   }
+
+  .activity-content {
+    flex: 1;
+  }
+
+  .activity-title {
+    font-size: 14px;
+    color: #303133;
+    margin-bottom: 4px;
+  }
+
+  .activity-time {
+    font-size: 12px;
+    color: #909399;
+  }
+}
+
+@media (max-width: 768px) {
+  .dashboard-container {
+    padding: 16px;
+  }
+
+  .stats-grid {
+    grid-template-columns: 1fr;
+    gap: 16px;
+  }
+
+  .quick-actions {
+    .action-btn {
+      min-width: 100px;
+    }
+  }
+}
+
+.event-status {
+  .status-item {
+    display: flex;
+    align-items: center;
+    padding: 16px 0;
+    border-bottom: 1px solid #f0f0f0;
+
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+
+  .status-icon {
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 16px;
+    color: white;
+
+    &.upcoming {
+      background: #e6a23c;
+    }
+
+    &.ongoing {
+      background: #67c23a;
+    }
+
+    &.completed {
+      background: #909399;
+    }
+
+    &.total {
+      background: #409eff;
+    }
+  }
+
+  .status-content {
+    flex: 1;
+  }
+
+  .status-count {
+    font-size: 20px;
+    font-weight: bold;
+    color: #303133;
+    line-height: 1;
+    margin-bottom: 4px;
+  }
+
+  .status-label {
+    font-size: 14px;
+    color: #909399;
+  }
 }
 </style>

+ 18 - 8
src/views/login.vue

@@ -8,12 +8,16 @@
       <el-form-item v-if="tenantEnabled" prop="tenantId">
         <el-select v-model="loginForm.tenantId" filterable :placeholder="proxy.$t('login.selectPlaceholder')" style="width: 100%">
           <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
-          <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
+          <template #prefix>
+            <svg-icon icon-class="company" class="el-input__icon input-icon" />
+          </template>
         </el-select>
       </el-form-item>
       <el-form-item prop="username">
         <el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" :placeholder="proxy.$t('login.username')">
-          <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
+          <template #prefix>
+            <svg-icon icon-class="user" class="el-input__icon input-icon" />
+          </template>
         </el-input>
       </el-form-item>
       <el-form-item prop="password">
@@ -25,7 +29,9 @@
           :placeholder="proxy.$t('login.password')"
           @keyup.enter="handleLogin"
         >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
+          <template #prefix>
+            <svg-icon icon-class="password" class="el-input__icon input-icon" />
+          </template>
         </el-input>
       </el-form-item>
       <el-form-item v-if="captchaEnabled" prop="code">
@@ -37,13 +43,15 @@
           style="width: 63%"
           @keyup.enter="handleLogin"
         >
-          <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
+          <template #prefix>
+            <svg-icon icon-class="validCode" class="el-input__icon input-icon" />
+          </template>
         </el-input>
         <div class="login-code">
           <img :src="codeUrl" class="login-code-img" @click="getCode" />
         </div>
       </el-form-item>
-      <el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">{{ proxy.$t('login.rememberPassword') }}</el-checkbox>
+      <el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">{{ proxy.$t('login.rememberPassword') }} </el-checkbox>
       <el-form-item style="float: right">
         <el-button circle :title="proxy.$t('login.social.wechat')" @click="doSocialLogin('wechat')">
           <svg-icon icon-class="wechat" />
@@ -95,9 +103,9 @@ const router = useRouter();
 const { t } = useI18n();
 
 const loginForm = ref<LoginData>({
-  tenantId: '000000',
-  username: 'admin',
-  password: 'admin123',
+  tenantId: '',
+  username: '',
+  password: '',
   rememberMe: false,
   code: '',
   uuid: ''
@@ -261,8 +269,10 @@ onMounted(() => {
   width: 400px;
   padding: 25px 25px 5px 25px;
   z-index: 1;
+
   .el-input {
     height: 40px;
+
     input {
       height: 40px;
     }

+ 25 - 6
src/views/system/activityIntroduction/index.vue

@@ -76,6 +76,9 @@
         </el-table-column>
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
           <template #default="scope">
+            <el-tooltip content="查看" placement="top">
+              <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['system:article:view']"></el-button>
+            </el-tooltip>
             <el-tooltip content="修改" placement="top">
               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:article:edit']"></el-button>
             </el-tooltip>
@@ -92,30 +95,30 @@
     <el-dialog :title="dialog.title" v-model="dialog.visible" width="1200px" append-to-body>
       <el-form ref="articleFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="赛事" prop="eventId">
-          <el-select v-model="form.eventId" placeholder="请选择赛事" clearable>
+          <el-select v-model="form.eventId" placeholder="请选择赛事" clearable :disabled="isViewMode">
             <el-option v-for="event in eventOptions" :key="event.eventId" :label="event.eventName" :value="event.eventId" />
           </el-select>
         </el-form-item>
         <el-form-item label="标题" prop="title">
-          <el-input v-model="form.title" placeholder="请输入标题" />
+          <el-input v-model="form.title" placeholder="请输入标题" :disabled="isViewMode" />
         </el-form-item>
         <el-form-item label="内容">
-          <editor v-model="form.content" :min-height="192" />
+          <editor v-model="form.content" :min-height="192" :readOnly="isViewMode" />
         </el-form-item>
         <el-form-item label="文章类型" prop="type">
-          <el-select v-model="form.type" placeholder="请选择文章类型">
+          <el-select v-model="form.type" placeholder="请选择文章类型" :disabled="isViewMode">
             <el-option v-for="dict in article_type" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
           </el-select>
         </el-form-item>
         <el-form-item label="是否展示" prop="status">
-          <el-radio-group v-model="form.status">
+          <el-radio-group v-model="form.status" :disabled="isViewMode">
             <el-radio v-for="dict in article_status" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
           </el-radio-group>
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button v-if="!isViewMode" :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
           <el-button @click="cancel">取 消</el-button>
         </div>
       </template>
@@ -155,6 +158,9 @@ const dialog = reactive<DialogOption>({
   title: ''
 });
 
+// 添加查看模式状态
+const isViewMode = ref(false);
+
 const initFormData: ArticleForm = {
   id: undefined,
   eventId: undefined,
@@ -199,6 +205,7 @@ const getList = async () => {
 /** 取消按钮 */
 const cancel = () => {
   reset();
+  isViewMode.value = false;
   dialog.visible = false;
 };
 
@@ -230,6 +237,7 @@ const handleSelectionChange = (selection: ArticleVO[]) => {
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
+  isViewMode.value = false;
   dialog.visible = true;
   dialog.title = '添加文章';
 };
@@ -240,10 +248,21 @@ const handleUpdate = async (row?: ArticleVO) => {
   const _id = row?.id || ids.value[0];
   const res = await getArticle(_id);
   Object.assign(form.value, res.data);
+  isViewMode.value = false;
   dialog.visible = true;
   dialog.title = '修改文章';
 };
 
+const handleView = async (row?: ArticleVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getArticle(_id);
+  Object.assign(form.value, res.data);
+  isViewMode.value = true;
+  dialog.visible = true;
+  dialog.title = '查看文章';
+};
+
 /** 提交按钮 */
 const submitForm = () => {
   articleFormRef.value?.validate(async (valid: boolean) => {

+ 11 - 11
src/views/system/gameEvent/detail.vue

@@ -15,7 +15,7 @@
             <h3>赛事信息</h3>
           </div>
         </template>
-        
+
         <el-row :gutter="20">
           <el-col :span="12">
             <el-descriptions :column="1" border>
@@ -29,8 +29,8 @@
           </el-col>
           <el-col :span="12">
             <el-descriptions :column="1" border>
-              <el-descriptions-item label="开始时间">{{ parseTime(eventData.startTime, '{y}-{m}-{d}') }}</el-descriptions-item>
-              <el-descriptions-item label="结束时间">{{ parseTime(eventData.endTime, '{y}-{m}-{d}') }}</el-descriptions-item>
+              <el-descriptions-item label="开始时间">{{ parseTime(eventData.startTime, '{y}-{m}-{d}') }} </el-descriptions-item>
+              <el-descriptions-item label="结束时间">{{ parseTime(eventData.endTime, '{y}-{m}-{d}') }} </el-descriptions-item>
               <el-descriptions-item label="举办单位">{{ eventData.unit }}</el-descriptions-item>
               <el-descriptions-item label="状态">
                 <dict-tag :options="game_event_status" :value="eventData.status" />
@@ -38,7 +38,7 @@
             </el-descriptions>
           </el-col>
         </el-row>
-        
+
         <el-row :gutter="20" class="mt-4">
           <el-col :span="24">
             <el-descriptions :column="1" border>
@@ -55,7 +55,7 @@
             <h3>赛事项目</h3>
           </div>
         </template>
-        
+
         <el-table :data="projectData" border>
           <el-table-column label="项目名称" prop="projectName" align="center" />
           <el-table-column label="项目类型" prop="projectType" align="center">
@@ -149,7 +149,7 @@ const loadEventData = async (eventId: string) => {
 // 加载赛事项目
 const loadProjectData = async (eventId: string) => {
   try {
-    const res = await listGameEventProject({ 
+    const res = await listGameEventProject({
       eventId: eventId,
       pageNum: 1,
       pageSize: 1000
@@ -174,27 +174,27 @@ onMounted(async () => {
   .page-header {
     display: flex;
     align-items: center;
-    
+
     .page-title {
       margin-left: 10px;
       font-size: 16px;
       font-weight: bold;
     }
   }
-  
+
   .event-header h3,
   .project-header h3 {
     margin: 0;
     font-size: 16px;
     font-weight: bold;
   }
-  
+
   .mb-4 {
     margin-bottom: 1rem;
   }
-  
+
   .mt-4 {
     margin-top: 1rem;
   }
 }
-</style>
+</style>

+ 70 - 112
src/views/system/gameEvent/edit.vue

@@ -29,24 +29,14 @@
               <el-col :span="12">
                 <el-form-item label="赛事类型" prop="eventType">
                   <el-select v-model="basicForm.eventType" placeholder="请选择赛事类型" style="width: 100%">
-                    <el-option
-                      v-for="dict in game_event_type"
-                      :key="dict.value"
-                      :label="dict.label"
-                      :value="dict.value"
-                    />
+                    <el-option v-for="dict in game_event_type" :key="dict.value" :label="dict.label" :value="dict.value" />
                   </el-select>
                 </el-form-item>
               </el-col>
               <el-col :span="12">
                 <el-form-item label="用途" prop="purpose">
                   <el-select v-model="basicForm.purpose" placeholder="请选择用途" style="width: 100%">
-                    <el-option
-                      v-for="dict in game_event_purpose"
-                      :key="dict.value"
-                      :label="dict.label"
-                      :value="dict.value"
-                    />
+                    <el-option v-for="dict in game_event_purpose" :key="dict.value" :label="dict.label" :value="dict.value" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -105,11 +95,7 @@
               <el-col :span="12">
                 <el-form-item label="状态" prop="status">
                   <el-radio-group v-model="basicForm.status">
-                    <el-radio
-                      v-for="dict in game_event_status"
-                      :key="dict.value"
-                      :value="dict.value"
-                    >{{ dict.label }}</el-radio>
+                    <el-radio v-for="dict in game_event_status" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
                   </el-radio-group>
                 </el-form-item>
               </el-col>
@@ -139,7 +125,6 @@
             <el-form-item label="备注" prop="remark">
               <el-input v-model="basicForm.remark" type="textarea" placeholder="请输入备注" :rows="3" />
             </el-form-item>
-          
           </el-form>
         </el-tab-pane>
 
@@ -150,14 +135,14 @@
               <h3>赛事菜单</h3>
               <el-button type="primary" @click="addMenuItem" icon="Plus">添加菜单项</el-button>
             </div>
-            
+
             <el-table :data="menuItems" border style="width: 100%">
-              <el-table-column label="菜单名称" prop="menuName" width="200">
+              <el-table-column label="菜单名称" prop="menuName">
                 <template #default="scope">
                   <el-input v-model="scope.row.menuName" placeholder="请输入菜单名称" />
                 </template>
               </el-table-column>
-              <el-table-column label="活动类型" prop="activityStatus" width="200">
+              <el-table-column label="活动类型" prop="activityStatus">
                 <template #default="scope">
                   <el-radio-group v-model="scope.row.activityStatus">
                     <el-radio :value="0">赛前</el-radio>
@@ -166,17 +151,17 @@
                   </el-radio-group>
                 </template>
               </el-table-column>
-              <el-table-column label="图标" prop="icon" width="200">
+              <el-table-column label="图标" prop="icon">
                 <template #default="scope">
                   <el-input v-model="scope.row.icon" placeholder="请输入图标" />
                 </template>
               </el-table-column>
-              <el-table-column label="颜色" prop="color" width="200">
+              <el-table-column label="颜色" prop="color">
                 <template #default="scope">
                   <el-input v-model="scope.row.color" placeholder="请输入颜色" />
                 </template>
               </el-table-column>
-              <el-table-column label="是否站外" prop="isFrame" width="200">
+              <el-table-column label="是否站外" prop="isFrame">
                 <template #default="scope">
                   <el-radio-group v-model="scope.row.isFrame">
                     <el-radio :value="1">站内</el-radio>
@@ -184,19 +169,19 @@
                   </el-radio-group>
                 </template>
               </el-table-column>
-              <el-table-column label="链接" prop="siteLink" width="200">
+              <el-table-column label="链接" prop="siteLink">
                 <template #default="scope">
                   <el-input v-model="scope.row.siteLink" placeholder="请输入链接" />
                 </template>
               </el-table-column>
-              <el-table-column label="排序" prop="orderNum" width="180">
+              <el-table-column label="排序" prop="orderNum">
                 <template #default="scope">
                   <el-input-number v-model="scope.row.orderNum" :min="1" :max="999" />
                 </template>
               </el-table-column>
-              <el-table-column label="操作" width="120">
+              <el-table-column label="操作">
                 <template #default="scope">
-                  <el-button type="danger" size="small" @click="removeMenuItem(scope.$index)" icon="Delete">删除</el-button>
+                  <el-button type="danger" size="small" @click="removeMenuItem(scope.$index)" icon="Delete">删除 </el-button>
                 </template>
               </el-table-column>
             </el-table>
@@ -212,36 +197,26 @@
             </div>
 
             <el-table :data="projectItems" border style="width: 100%">
-              <el-table-column label="項目类型" prop="projectType" width="180">
+              <el-table-column label="項目类型" prop="projectType">
                 <template #default="scope">
                   <el-select v-model="scope.row.projectType" placeholder="请选择項目类型" style="width: 100%">
-                    <el-option
-                      v-for="dict in game_project_type"
-                      :key="dict.value"
-                      :label="dict.label"
-                      :value="dict.value"
-                    />
+                    <el-option v-for="dict in game_project_type" :key="dict.value" :label="dict.label" :value="dict.value" />
                   </el-select>
                 </template>
               </el-table-column>
-              <el-table-column label="项目名称" prop="projectName" width="200">
+              <el-table-column label="项目名称" prop="projectName">
                 <template #default="scope">
                   <el-input v-model="scope.row.projectName" placeholder="请输入项目名称" />
                 </template>
               </el-table-column>
-              <el-table-column label="项目组别" prop="groupType" width="200">
+              <el-table-column label="项目组别" prop="groupType">
                 <template #default="scope">
                   <el-select v-model="scope.row.groupType" placeholder="请选择项目组别" style="width: 100%">
-                    <el-option
-                      v-for="group in groupItems"
-                      :key="group.groupId"
-                      :label="group.groupName"
-                      :value="group.groupName"
-                    />
+                    <el-option v-for="group in groupItems" :key="group.groupId" :label="group.groupName" :value="group.groupName" />
                   </el-select>
                 </template>
               </el-table-column>
-              <el-table-column label="排序" prop="orderType" width="100">
+              <el-table-column label="排序" prop="orderType">
                 <template #default="scope">
                   <el-radio-group v-model="scope.row.orderType">
                     <el-radio value="0">降序</el-radio>
@@ -249,24 +224,24 @@
                   </el-radio-group>
                 </template>
               </el-table-column>
-              <el-table-column label="比赛地点" prop="location" width="100">
+              <el-table-column label="比赛地点" prop="location">
                 <template #default="scope">
                   <el-input v-model="scope.row.location" placeholder="请输入比赛地点" />
                 </template>
               </el-table-column>
-              <el-table-column label="录取名次" prop="roundType" width="100">
+              <el-table-column label="录取名次" prop="roundType">
                 <template #default="scope">
                   <el-input v-model="scope.row.roundType" placeholder="请输入录取名次" />
                 </template>
               </el-table-column>
-              <el-table-column label="分数" prop="scoreValue" width="100">
+              <el-table-column label="分数" prop="scoreValue">
                 <template #default="scope">
                   <el-input v-model="scope.row.scoreValue" placeholder="请输入分数" />
                 </template>
               </el-table-column>
-              <el-table-column label="操作" width="120">
+              <el-table-column label="操作">
                 <template #default="scope">
-                  <el-button type="danger" size="small" @click="removeProjectItem(scope.$index)" icon="Delete">移除</el-button>
+                  <el-button type="danger" size="small" @click="removeProjectItem(scope.$index)" icon="Delete">移除 </el-button>
                 </template>
               </el-table-column>
             </el-table>
@@ -280,7 +255,7 @@
               <h3>赛事配置信息</h3>
               <el-button type="primary" @click="addConfigItem" icon="Plus">添加配置</el-button>
             </div>
-            
+
             <el-table :data="configItems" border style="width: 100%">
               <el-table-column label="配置类型" prop="configType" width="150">
                 <template #default="scope">
@@ -299,24 +274,24 @@
                   <el-input v-model="scope.row.configKey" placeholder="请输入配置键" />
                 </template>
               </el-table-column>
-              <el-table-column label="配置值" prop="configValue" width="300">
+              <el-table-column label="配置值" prop="configValue">
                 <template #default="scope">
                   <image-or-url-input v-model="scope.row.configValue" />
                 </template>
               </el-table-column>
-              <el-table-column label="配置描述" prop="configDesc" width="200">
+              <el-table-column label="配置描述" prop="configDesc">
                 <template #default="scope">
                   <el-input v-model="scope.row.configDesc" placeholder="请输入配置描述" />
                 </template>
               </el-table-column>
-              <!-- <el-table-column label="是否启用" prop="isEnabled" width="120">
+              <!-- <el-table-column label="是否启用" prop="isEnabled">
                 <template #default="scope">
                   <el-switch v-model="scope.row.isEnabled" />
                 </template>
               </el-table-column> -->
-              <el-table-column label="操作" width="120">
+              <el-table-column label="操作">
                 <template #default="scope">
-                  <el-button type="danger" size="small" @click="removeConfigItem(scope.$index)" icon="Delete">删除</el-button>
+                  <el-button type="danger" size="small" @click="removeConfigItem(scope.$index)" icon="Delete">删除 </el-button>
                 </template>
               </el-table-column>
             </el-table>
@@ -332,24 +307,24 @@
             </div>
 
             <el-table :data="teamItems" border style="width: 100%">
-              <el-table-column label="单位名称" prop="teamName" width="200">
+              <el-table-column label="单位名称" prop="teamName">
                 <template #default="scope">
                   <el-input v-model="scope.row.teamName" placeholder="请输入单位名称" />
                 </template>
               </el-table-column>
-              <el-table-column label="编号" prop="teamCode" width="100">
+              <el-table-column label="编号" prop="teamCode">
                 <template #default="scope">
                   <el-input v-model="scope.row.teamCode" placeholder="请输入编号" />
                 </template>
               </el-table-column>
-              <el-table-column label="团队描述" prop="teamDescribe" width="200">
+              <el-table-column label="团队描述" prop="teamDescribe">
                 <template #default="scope">
                   <el-input v-model="scope.row.teamDescribe" placeholder="请描述一下你的团队吧~" />
                 </template>
               </el-table-column>
-              <el-table-column label="操作" width="120">
+              <el-table-column label="操作">
                 <template #default="scope">
-                  <el-button type="danger" size="small" @click="removeTeamItem(scope.$index)" icon="Delete">删除</el-button>
+                  <el-button type="danger" size="small" @click="removeTeamItem(scope.$index)" icon="Delete">删除 </el-button>
                 </template>
               </el-table-column>
             </el-table>
@@ -365,12 +340,12 @@
             </div>
 
             <el-table :data="groupItems" border style="width: 100%">
-              <el-table-column label="分组名称" prop="groupName" width="200">
+              <el-table-column label="分组名称" prop="groupName">
                 <template #default="scope">
                   <el-input v-model="scope.row.groupName" placeholder="请输入分组名称" />
                 </template>
               </el-table-column>
-              <el-table-column label="成员性别" prop="memberGender" width="200">
+              <el-table-column label="成员性别" prop="memberGender">
                 <template #default="scope">
                   <el-radio-group v-model="scope.row.memberGender">
                     <el-radio value="0">不分男女</el-radio>
@@ -379,9 +354,9 @@
                   </el-radio-group>
                 </template>
               </el-table-column>
-              <el-table-column label="操作" width="120">
+              <el-table-column label="操作">
                 <template #default="scope">
-                  <el-button type="danger" size="small" @click="removeGroupItem(scope.$index)" icon="Delete">删除</el-button>
+                  <el-button type="danger" size="small" @click="removeGroupItem(scope.$index)" icon="Delete">删除 </el-button>
                 </template>
               </el-table-column>
             </el-table>
@@ -408,18 +383,18 @@ import { GameEventConfigTypeVO } from '@/api/system/gameEventConfigType/types';
 import { useRoute, useRouter } from 'vue-router';
 import { listGameEventMenu, addGameEventMenu, updateGameEventMenu, delGameEventMenu } from '@/api/system/gameEventMenu';
 import { GameEventMenuVO, GameEventMenuForm } from '@/api/system/gameEventMenu/types';
-import { listGameTeam, addGameTeam, updateGameTeam,delGameTeam } from '@/api/system/gameTeam';
+import { listGameTeam, addGameTeam, updateGameTeam, delGameTeam } from '@/api/system/gameTeam';
 import { GameTeamVO, GameTeamForm } from '@/api/system/gameTeam/types';
 import { listGameEventGroup, addGameEventGroup, updateGameEventGroup, delGameEventGroup } from '@/api/system/gameEventGroup';
-import { GameEventGroupForm,GameEventGroupVO } from '@/api/system/gameEventGroup/types';
-import { addGameEventProject, listGameEventProject, updateGameEventProject,delGameEventProject } from '@/api/system/gameEventProject';
+import { GameEventGroupForm, GameEventGroupVO } from '@/api/system/gameEventGroup/types';
+import { addGameEventProject, listGameEventProject, updateGameEventProject, delGameEventProject } from '@/api/system/gameEventProject';
 import { GameEventProjectForm, GameEventProjectVO } from '@/api/system/gameEventProject/types';
 
 const route = useRoute();
 const router = useRouter();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { game_event_type, game_event_purpose, game_event_status, sys_yes_no,game_project_type } = toRefs<any>(
-  proxy?.useDict('game_event_type', 'game_event_purpose', 'game_event_status', 'sys_yes_no','game_project_type')
+const { game_event_type, game_event_purpose, game_event_status, sys_yes_no, game_project_type } = toRefs<any>(
+  proxy?.useDict('game_event_type', 'game_event_purpose', 'game_event_status', 'sys_yes_no', 'game_project_type')
 );
 
 // 页面状态
@@ -448,27 +423,15 @@ const basicForm = ref<GameEventForm>({
   remark: ''
 });
 
-
 // 表单验证规则
 const basicRules = {
-  eventCode: [
-    { required: true, message: '请输入赛事编号', trigger: 'blur' }
-  ],
-  eventName: [
-    { required: true, message: '请输入赛事名称', trigger: 'blur' }
-  ],
-  eventType: [
-    { required: true, message: '请选择赛事类型', trigger: 'change' }
-  ],
-  startTime: [
-    { required: true, message: '请选择开始时间', trigger: 'change' }
-  ],
-  endTime: [
-    { required: true, message: '请选择结束时间', trigger: 'change' }
-  ]
+  eventCode: [{ required: true, message: '请输入赛事编号', trigger: 'blur' }],
+  eventName: [{ required: true, message: '请输入赛事名称', trigger: 'blur' }],
+  eventType: [{ required: true, message: '请选择赛事类型', trigger: 'change' }],
+  startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
+  endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
 };
 
-
 // 初始化页面
 onMounted(async () => {
   const eventId = route.params.id as string;
@@ -489,10 +452,10 @@ const loadEventData = async (eventId: string | number) => {
   try {
     const res = await getGameEvent(eventId);
     const data = res.data;
-    
+
     // 填充基本信息
     Object.assign(basicForm.value, data);
-    
+
     // 加载图片配置数据
     try {
       await loadImageConfigData(eventId);
@@ -500,7 +463,7 @@ const loadEventData = async (eventId: string | number) => {
       console.warn('加载图片配置数据失败,继续加载其他数据:', error);
       // 不中断其他数据的加载
     }
-    
+
     await loadMenuData(eventId); // 加载菜单数据
     await loadGroupData(eventId); // 加载分组数据
     await loadProjectData(eventId); // 加载项目数据
@@ -508,7 +471,6 @@ const loadEventData = async (eventId: string | number) => {
     await loadConfigData(eventId); // 加载配置数据
     // TODO: 加载菜单、项目、配置数据
     // 这里需要调用相应的API来加载数据
-    
   } catch (error) {
     proxy?.$modal.msgError('加载赛事数据失败');
   }
@@ -550,7 +512,7 @@ const addGroupItem = () => {
     groupId: '',
     eventId: '',
     groupName: '',
-    memberGender: '',
+    memberGender: ''
   });
 };
 // 删除赛事分组项
@@ -612,7 +574,7 @@ const addMenuItem = () => {
     isFrame: 1,
     siteLink: '',
     orderNum: menuItems.value.length + 1,
-    eventId: isEdit.value ? route.params.id as string : '' // 关联赛事ID
+    eventId: isEdit.value ? (route.params.id as string) : '' // 关联赛事ID
   });
 };
 // 删除菜单项
@@ -669,12 +631,12 @@ const loadImageConfigData = async (eventId: string | number) => {
       pageNum: 1,
       pageSize: 100
     });
-    
+
     const eventImageConfigs = Array.isArray(eventRes.rows) ? eventRes.rows : [];
     
     // 合并配置:以模板为基础,用当前赛事的数据覆盖
-    imageConfigItems.value = allImageConfigs.map(template => {
-      const existingConfig = eventImageConfigs.find(item => item.configKey === template.configKey);
+    imageConfigItems.value = allImageConfigs.map((template) => {
+      const existingConfig = eventImageConfigs.find((item) => item.configKey === template.configKey);
       return {
         ...template,
         eventId: eventId,
@@ -733,7 +695,7 @@ const refreshImageConfigs = async () => {
 // 保存图片配置数据
 const saveImageConfigData = async (eventId?: string | number) => {
   try {
-    const targetEventId = eventId || route.params.id as string;
+    const targetEventId = eventId || (route.params.id as string);
     const updates: GameEventConfigForm[] = [];
     const adds: GameEventConfigForm[] = [];
 
@@ -746,7 +708,7 @@ const saveImageConfigData = async (eventId?: string | number) => {
           configType: 'IMAGE'
         };
 
-        if (item.configId && item.configValue) { 
+        if (item.configId && item.configValue) {
           updates.push(configData);
         } else if (!item.configId && item.configValue) {
           adds.push(configData);
@@ -799,7 +761,7 @@ const addProjectItem = () => {
     remark: '',
     location: '',
     scoreValue: '',
-    groupType: '',
+    groupType: ''
   });
 };
 // 删除项目项
@@ -834,7 +796,6 @@ const saveProjectData = async (eventId: string | number) => {
   }
 };
 
-
 // 配置信息数据
 const configItems = ref<GameEventConfigForm[]>([]);
 // 配置类型选项
@@ -911,7 +872,6 @@ const saveconfigData = async (eventId: string | number) => {
   }
 };
 
-
 // 参赛队伍数据
 const teamItems = ref<GameTeamForm[]>([]);
 // 加载队伍数据
@@ -934,7 +894,7 @@ const addTeamItem = () => {
     teamCode: '',
     eventId: '',
     teamDescribe: '',
-    remark: '',
+    remark: ''
   });
 };
 // 删除参赛队伍项
@@ -979,7 +939,7 @@ const saveEvent = async () => {
 
     // 合并表单数据
     const formData: GameEventForm = {
-      ...basicForm.value,
+      ...basicForm.value
     };
 
     let savedEventId: string;
@@ -995,7 +955,6 @@ const saveEvent = async () => {
     } else {
       const addRes = await addGameEvent(formData);
       // 假设返回的数据中包含新创建的赛事ID
-      console.log('addRes', addRes);
       savedEventId = addRes?.data as string;
     }
 
@@ -1011,13 +970,12 @@ const saveEvent = async () => {
     await saveGroupData(savedEventId);
     // 保存赛事项目数据
     await saveProjectData(savedEventId);
-    
+
     proxy?.$modal.msgSuccess('保存成功');
-    
+
     // 保存成功后,设置一个标识,表示需要刷新列表数据
     sessionStorage.setItem('needRefreshGameEventList', 'true');
     goBack();
-
   } catch (error) {
     console.error('保存失败:', error);
     proxy?.$modal.msgError('保存失败');
@@ -1038,13 +996,13 @@ const goBack = () => {
     display: flex;
     align-items: center;
     gap: 16px;
-    
+
     .page-title {
       font-size: 18px;
       font-weight: 600;
     }
   }
-  
+
   .menu-config,
   .project-config,
   .config-info,
@@ -1057,7 +1015,7 @@ const goBack = () => {
       justify-content: space-between;
       align-items: center;
       margin-bottom: 16px;
-      
+
       h3 {
         margin: 0;
         font-size: 16px;
@@ -1065,7 +1023,7 @@ const goBack = () => {
       }
     }
   }
-  
+
   .image-config {
     .config-info {
       margin-top: 8px;
@@ -1074,7 +1032,7 @@ const goBack = () => {
       justify-content: space-between;
     }
   }
-  
+
   .team-config {
     .team-header {
       display: flex;
@@ -1122,4 +1080,4 @@ const goBack = () => {
 :deep(.el-form-item) {
   margin-bottom: 20px;
 }
-</style> 
+</style>

+ 106 - 80
src/views/system/gameEvent/index.vue

@@ -3,7 +3,7 @@
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
             <el-form-item label="赛事编号" prop="eventCode">
               <el-input v-model="queryParams.eventCode" placeholder="请输入赛事编号" clearable @keyup.enter="handleQuery" />
             </el-form-item>
@@ -11,26 +11,21 @@
               <el-input v-model="queryParams.eventName" placeholder="请输入赛事名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="赛事类型" prop="eventType">
-              <el-select v-model="queryParams.eventType" placeholder="请选择赛事类型" clearable >
-                <el-option v-for="dict in game_event_type" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              <el-select v-model="queryParams.eventType" placeholder="请选择赛事类型" clearable>
+                <el-option v-for="dict in game_event_type" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
             <el-form-item label="开始时间" prop="startTime">
-              <el-date-picker clearable
-                v-model="queryParams.startTime"
-                type="date"
-                value-format="YYYY-MM-DD"
-                placeholder="请选择开始时间"
-              />
+              <el-date-picker clearable v-model="queryParams.startTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始时间" />
             </el-form-item>
             <el-form-item label="是否默认赛事" prop="isDefault">
-              <el-select v-model="queryParams.isDefault" placeholder="请选择是否默认赛事" clearable >
-                <el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              <el-select v-model="queryParams.isDefault" placeholder="请选择是否默认赛事" clearable>
+                <el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
             <el-form-item label="状态" prop="status">
-              <el-select v-model="queryParams.status" placeholder="请选择状态" clearable >
-                <el-option v-for="dict in game_event_status" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
+                <el-option v-for="dict in game_event_status" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
             <el-form-item>
@@ -46,16 +41,20 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameEvent:add']">新增</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameEvent:add']">新增 </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameEvent:edit']">修改</el-button>
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameEvent:edit']"
+              >修改
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameEvent:remove']">删除</el-button>
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameEvent:remove']"
+              >删除
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameEvent:export']">导出</el-button>
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameEvent:export']">导出 </el-button>
           </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
@@ -68,13 +67,13 @@
         <el-table-column label="赛事名称" align="center" prop="eventName" />
         <el-table-column label="赛事类型" align="center" prop="eventType">
           <template #default="scope">
-            <dict-tag :options="game_event_type" :value="scope.row.eventType"/>
+            <dict-tag :options="game_event_type" :value="scope.row.eventType" />
           </template>
         </el-table-column>
         <el-table-column label="举办地点" align="center" prop="location" />
-        <el-table-column label="用途" align="center" prop="purpose" >
+        <el-table-column label="用途" align="center" prop="purpose">
           <template #default="scope">
-            <dict-tag :options="game_event_purpose" :value="scope.row.purpose"/>
+            <dict-tag :options="game_event_purpose" :value="scope.row.purpose" />
           </template>
         </el-table-column>
         <el-table-column label="开始时间" align="center" prop="startTime" width="180">
@@ -89,18 +88,17 @@
         </el-table-column>
         <el-table-column label="赛事链接" align="center" prop="eventUrlUrl" width="100">
           <template #default="scope">
-            <image-preview :src="scope.row.eventUrlUrl" :width="50" :height="50"/>
+            <image-preview :src="scope.row.eventUrlUrl" :width="50" :height="50" />
           </template>
         </el-table-column>
         <el-table-column label="裁判码" align="center" prop="refereeUrlUrl" width="100">
           <template #default="scope">
-            <image-preview :src="scope.row.refereeUrlUrl" :width="50" :height="50"/>
+            <image-preview :src="scope.row.refereeUrlUrl" :width="50" :height="50" />
           </template>
         </el-table-column>
         <el-table-column label="举办单位" align="center" prop="unit" />
         <el-table-column label="是否默认赛事" align="center" prop="isDefault">
           <template #default="scope">
-            <!-- <dict-tag :options="sys_yes_no" :value="scope.row.isDefault"/> -->
              <el-switch v-model="scope.row.isDefault" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
           </template>
         </el-table-column>
@@ -116,11 +114,11 @@
         </el-table-column>
         <el-table-column label="状态" align="center" prop="status">
           <template #default="scope">
-            <dict-tag :options="game_event_status" :value="scope.row.status"/>
+            <dict-tag :options="game_event_status" :value="scope.row.status" />
           </template>
         </el-table-column>
         <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
           <template #default="scope">
             <el-tooltip content="修改" placement="top">
               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:gameEvent:edit']"></el-button>
@@ -129,23 +127,42 @@
               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:gameEvent:remove']"></el-button>
             </el-tooltip> -->
             <el-tooltip :content="getDeleteTooltip(scope.row)" placement="top">
-              <el-button 
-                link 
-                type="primary" 
-                icon="Delete" 
+              <el-button
+                link
+                type="primary"
+                icon="Delete"
                 :disabled="!canDelete(scope.row)"
-                @click="handleDelete(scope.row)" 
-                v-hasPermi="['system:event:remove']">
+                @click="handleDelete(scope.row)"
+                v-hasPermi="['system:event:remove']"
+              >
               </el-button>
             </el-tooltip>
             <el-tooltip content="导入报名信息" placement="top">
-              <el-button link type="primary" icon="FolderOpened" @click="handleImportRegistration(scope.row)" v-hasPermi="['system:gameEvent:import']"></el-button>
+              <el-button
+                link
+                type="primary"
+                icon="FolderOpened"
+                @click="handleImportRegistration(scope.row)"
+                v-hasPermi="['system:gameEvent:import']"
+              ></el-button>
             </el-tooltip>
             <el-tooltip content="添加参赛者" placement="top">
-              <el-button link type="primary" icon="User" @click="handleAddParticipant(scope.row)" v-hasPermi="['system:gameEvent:addParticipant']"></el-button>
+              <el-button
+                link
+                type="primary"
+                icon="User"
+                @click="handleAddParticipant(scope.row)"
+                v-hasPermi="['system:gameEvent:addParticipant']"
+              ></el-button>
             </el-tooltip>
             <el-tooltip content="添加裁判" placement="top">
-              <el-button link type="primary" icon="Avatar" @click="handleAddReferee(scope.row)" v-hasPermi="['system:gameEvent:addReferee']"></el-button>
+              <el-button
+                link
+                type="primary"
+                icon="Avatar"
+                @click="handleAddReferee(scope.row)"
+                v-hasPermi="['system:gameEvent:addReferee']"
+              ></el-button>
             </el-tooltip>
             <!-- 新增预览按钮 -->
             <el-tooltip content="预览" placement="top">
@@ -153,7 +170,13 @@
             </el-tooltip>
             <!-- 新增比赛数据按钮 -->
             <el-tooltip content="比赛数据" placement="top">
-              <el-button link type="primary" icon="DataAnalysis" @click="handleGameData(scope.row)" v-hasPermi="['system:gameEvent:gameData']"></el-button>
+              <el-button
+                link
+                type="primary"
+                icon="DataAnalysis"
+                @click="handleGameData(scope.row)"
+                v-hasPermi="['system:gameEvent:gameData']"
+              ></el-button>
             </el-tooltip>
           </template>
         </el-table-column>
@@ -180,14 +203,16 @@ import RankingBoard from './RankingBoard.vue';
 
 const router = useRouter();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { game_event_type, game_event_status, sys_yes_no, game_event_purpose } = toRefs<any>(proxy?.useDict('game_event_type', 'game_event_status', 'sys_yes_no', 'game_event_purpose'));
+const { game_event_type, game_event_status, sys_yes_no, game_event_purpose } = toRefs<any>(
+  proxy?.useDict('game_event_type', 'game_event_status', 'sys_yes_no', 'game_event_purpose')
+);
 
 // 定义 RefereeForm 组件的类型
 interface RefereeFormInstance {
   openDialog: (eventId: string) => void;
 }
 
-const refereeFormRef = ref<InstanceType<typeof RefereeForm> & RefereeFormInstance | null>(null);
+const refereeFormRef = ref<(InstanceType<typeof RefereeForm> & RefereeFormInstance) | null>(null);
 
 const gameEventList = ref<GameEventVO[]>([]);
 const buttonLoading = ref(false);
@@ -222,9 +247,9 @@ const initFormData: GameEventForm = {
   isDefault: undefined,
   status: undefined,
   remark: undefined
-}
+};
 const data = reactive<PageData<GameEventForm, GameEventQuery>>({
-  form: {...initFormData},
+  form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
@@ -235,8 +260,7 @@ const data = reactive<PageData<GameEventForm, GameEventQuery>>({
     startTime: undefined,
     isDefault: undefined,
     status: undefined,
-    params: {
-    }
+    params: {}
   },
   rules: {
     eventCode: [
@@ -260,49 +284,49 @@ const getList = async () => {
   gameEventList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 /** 取消按钮 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 表单重置 */
 const reset = () => {
-  form.value = {...initFormData};
+  form.value = { ...initFormData };
   gameEventFormRef.value?.resetFields();
-}
+};
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 重置按钮操作 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection: GameEventVO[]) => {
-  ids.value = selection.map(item => item.eventId);
+  ids.value = selection.map((item) => item.eventId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 新增按钮操作 */
 const handleAdd = () => {
   router.push('/system/gameEvent/add');
-}
+};
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: GameEventVO) => {
   const _eventId = row?.eventId || ids.value[0];
   router.push(`/system/gameEvent/edit/${_eventId}`);
-}
+};
 
 /** 提交按钮 */
 const submitForm = () => {
@@ -310,91 +334,94 @@ const submitForm = () => {
     if (valid) {
       buttonLoading.value = true;
       if (form.value.eventId) {
-        await updateGameEvent(form.value).finally(() =>  buttonLoading.value = false);
+        await updateGameEvent(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addGameEvent(form.value).finally(() =>  buttonLoading.value = false);
+        await addGameEvent(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("操作成功");
+      proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
       await getList();
     }
   });
-}
-
+};
 
 /** 删除按钮操作 */
 const handleDelete = async (row?: GameEventVO) => {
   const _ids = row?.eventId || ids.value;
-  
+
   if (!_ids || (Array.isArray(_ids) && _ids.length === 0)) {
-    proxy?.$modal.msgError("请选择要删除的赛事");
+    proxy?.$modal.msgError('请选择要删除的赛事');
     return;
   }
-  
+
   // 如果是单个删除,需要检查时间限制
   if (row) {
     const endTime = new Date(row.endTime);
     const currentTime = new Date();
     const daysDiff = Math.floor((currentTime.getTime() - endTime.getTime()) / (1000 * 60 * 60 * 24));
-    
+
     if (daysDiff < 100) {
       proxy?.$modal.msgError(`该赛事结束时间不足100天,无法删除。距离可删除时间还有 ${100 - daysDiff} 天。`);
       return;
     }
-    
-    await proxy?.$modal.confirm('是否确认删除赛事编号为"' + row.eventCode + '"的数据项?').finally(() => loading.value = false);
+
+    await proxy?.$modal.confirm('是否确认删除赛事编号为"' + row.eventCode + '"的数据项?').finally(() => (loading.value = false));
     await delGameEvent(row.eventId);
-    proxy?.$modal.msgSuccess("删除成功");
+    proxy?.$modal.msgSuccess('删除成功');
   } else {
     // 批量删除
-    const selectedEvents = gameEventList.value.filter(event => ids.value.includes(event.eventId));
-    const invalidEvents = selectedEvents.filter(event => {
+    const selectedEvents = gameEventList.value.filter((event) => ids.value.includes(event.eventId));
+    const invalidEvents = selectedEvents.filter((event) => {
       const endTime = new Date(event.endTime);
       const currentTime = new Date();
       const daysDiff = Math.floor((currentTime.getTime() - endTime.getTime()) / (1000 * 60 * 60 * 24));
       return daysDiff < 100;
     });
-    
+
     if (invalidEvents.length > 0) {
-      const eventNames = invalidEvents.map(event => event.eventName).join('、');
+      const eventNames = invalidEvents.map((event) => event.eventName).join('、');
       proxy?.$modal.msgError(`以下赛事结束时间不足100天,无法删除:${eventNames}`);
       return;
     }
-    
-    const eventCodes = selectedEvents.map(event => event.eventCode).join('、');
-    await proxy?.$modal.confirm(`是否确认删除以下赛事:${eventCodes}?`).finally(() => loading.value = false);
+
+    const eventCodes = selectedEvents.map((event) => event.eventCode).join('、');
+    await proxy?.$modal.confirm(`是否确认删除以下赛事:${eventCodes}?`).finally(() => (loading.value = false));
     await delGameEvent(ids.value);
     proxy?.$modal.msgSuccess(`成功删除 ${selectedEvents.length} 个赛事`);
   }
-  
+
   await getList();
-}
+};
 /** 判断是否可以删除赛事 */
 const canDelete = (row: GameEventVO): boolean => {
   const endTime = new Date(row.endTime);
   const currentTime = new Date();
   const daysDiff = Math.floor((currentTime.getTime() - endTime.getTime()) / (1000 * 60 * 60 * 24));
   return daysDiff >= 100;
-}
+};
 
 /** 获取删除按钮的提示信息 */
 const getDeleteTooltip = (row: GameEventVO): string => {
   if (canDelete(row)) {
-    return "删除";
+    return '删除';
   } else {
     const endTime = new Date(row.endTime);
     const currentTime = new Date();
     const daysDiff = Math.floor((currentTime.getTime() - endTime.getTime()) / (1000 * 60 * 60 * 24));
     return `赛事结束时间不足100天,无法删除。距离可删除时间还有 ${100 - daysDiff} 天。`;
   }
-}
+};
 
 /** 导出按钮操作 */
 const handleExport = () => {
-  proxy?.download('system/gameEvent/export', {
-    ...queryParams.value
-  }, `gameEvent_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'system/gameEvent/export',
+    {
+      ...queryParams.value
+    },
+    `gameEvent_${new Date().getTime()}.xlsx`
+  );
+};
 
 // 导入报名表逻辑
 const handleImportRegistration = (row: GameEventVO) => {
@@ -473,5 +500,4 @@ onActivated(() => {
     getList();
   }
 });
-
 </script>

+ 59 - 62
src/views/system/gameEventProject/index.vue

@@ -9,12 +9,7 @@
             </el-form-item>
             <el-form-item label="项目组别" prop="groupType">
               <el-select v-model="queryParams.groupType" placeholder="请选择项目组别" clearable @change="handleQuery">
-                <el-option
-                  v-for="group in gameEventGroupList"
-                  :key="group.groupName"
-                  :label="group.groupName"
-                  :value="group.groupName"
-                />
+                <el-option v-for="group in gameEventGroupList" :key="group.groupName" :label="group.groupName" :value="group.groupName" />
               </el-select>
             </el-form-item>
             <el-form-item>
@@ -30,16 +25,20 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameEventProject:add']">新增</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameEventProject:add']"> 新增 </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameEventProject:edit']">修改</el-button>
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameEventProject:edit']"
+              >修改
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameEventProject:remove']">删除</el-button>
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameEventProject:remove']"
+              >删除
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameEventProject:export']">导出</el-button>
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameEventProject:export']">导出 </el-button>
           </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
@@ -92,7 +91,13 @@
               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:gameEventProject:edit']"></el-button>
             </el-tooltip>
             <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:gameEventProject:remove']"></el-button>
+              <el-button
+                link
+                type="primary"
+                icon="Delete"
+                @click="handleDelete(scope.row)"
+                v-hasPermi="['system:gameEventProject:remove']"
+              ></el-button>
             </el-tooltip>
           </template>
         </el-table-column>
@@ -108,22 +113,17 @@
         </el-form-item>
         <el-form-item label="项目类型" prop="projectType">
           <el-select v-model="form.projectType" placeholder="请选择项目类型">
-            <el-option
-              v-for="dict in game_project_type"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
-            />
+            <el-option v-for="dict in game_project_type" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </el-form-item>
-        
+
         <!-- <el-form-item label="裁判员ID" prop="refereeId">
           <el-input v-model="form.refereeId" placeholder="请输入裁判员ID" />
         </el-form-item> -->
         <el-form-item label="比赛场地" prop="location">
           <el-input v-model="form.location" placeholder="请输入比赛场地" />
         </el-form-item>
-        
+
         <!-- <el-form-item label="参赛组数" prop="groupNum">
           <el-input v-model="form.groupNum" placeholder="请输入参赛组数" />
         </el-form-item>
@@ -132,12 +132,7 @@
         </el-form-item> -->
         <el-form-item label="计算规则" prop="scoreRule">
           <el-select v-model="form.scoreRule" placeholder="请选择计算规则" style="width: 100%">
-            <el-option
-              v-for="dict in game_score_type"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
-            />
+            <el-option v-for="dict in game_score_type" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </el-form-item>
         <el-form-item label="排序方式" prop="orderType">
@@ -153,19 +148,11 @@
           <el-input v-model="form.roundType" placeholder="请输入轮次" />
         </el-form-item>
         <el-form-item label="开始时间" prop="startTime">
-          <el-date-picker clearable
-            v-model="form.startTime"
-            type="datetime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            placeholder="请选择开始时间">
+          <el-date-picker clearable v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开始时间">
           </el-date-picker>
         </el-form-item>
         <el-form-item label="结束时间" prop="endTime">
-          <el-date-picker clearable
-            v-model="form.endTime"
-            type="datetime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            placeholder="请选择结束时间">
+          <el-date-picker clearable v-model="form.endTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择结束时间">
           </el-date-picker>
         </el-form-item>
         <!-- <el-form-item label="奖项" prop="award">
@@ -192,7 +179,13 @@
 </template>
 
 <script setup name="GameEventProject" lang="ts">
-import { listGameEventProject, getGameEventProject, delGameEventProject, addGameEventProject, updateGameEventProject } from '@/api/system/gameEventProject';
+import {
+  listGameEventProject,
+  getGameEventProject,
+  delGameEventProject,
+  addGameEventProject,
+  updateGameEventProject
+} from '@/api/system/gameEventProject';
 import { listGameEventGroup } from '@/api/system/gameEventGroup';
 import { getDefaultEvent } from '@/api/system/gameEvent';
 import { GameEventProjectVO, GameEventProjectQuery, GameEventProjectForm } from '@/api/system/gameEventProject/types';
@@ -241,9 +234,9 @@ const initFormData: GameEventProjectForm = {
   boxCode: undefined,
   status: undefined,
   remark: undefined
-}
+};
 const data = reactive<PageData<GameEventProjectForm, GameEventProjectQuery>>({
-  form: {...initFormData},
+  form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
@@ -311,7 +304,7 @@ const getList = async () => {
   gameEventProjectList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 // 获取赛事分组列表
 const getGroupList = async () => {
@@ -320,25 +313,25 @@ const getGroupList = async () => {
     pageSize: 1000
   });
   gameEventGroupList.value = res.rows || [];
-}
+};
 
 /** 取消按钮 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 表单重置 */
 const reset = () => {
   form.value = {...initFormData, eventId: defaultEvent.value?.eventId};
   gameEventProjectFormRef.value?.resetFields();
-}
+};
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 重置按钮操作 */
 const resetQuery = () => {
@@ -346,31 +339,31 @@ const resetQuery = () => {
   // 保留默认赛事ID
   queryParams.value.eventId = defaultEvent.value?.eventId;
   handleQuery();
-}
+};
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection: GameEventProjectVO[]) => {
-  ids.value = selection.map(item => item.projectId);
+  ids.value = selection.map((item) => item.projectId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "添加赛事项目";
-}
+  dialog.title = '添加赛事项目';
+};
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: GameEventProjectVO) => {
   reset();
-  const _projectId = row?.projectId || ids.value[0]
+  const _projectId = row?.projectId || ids.value[0];
   const res = await getGameEventProject(_projectId);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "修改赛事项目";
-}
+  dialog.title = '修改赛事项目';
+};
 
 /** 提交按钮 */
 const submitForm = () => {
@@ -378,32 +371,36 @@ const submitForm = () => {
     if (valid) {
       buttonLoading.value = true;
       if (form.value.projectId) {
-        await updateGameEventProject(form.value).finally(() =>  buttonLoading.value = false);
+        await updateGameEventProject(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addGameEventProject(form.value).finally(() =>  buttonLoading.value = false);
+        await addGameEventProject(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("操作成功");
+      proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 删除按钮操作 */
 const handleDelete = async (row?: GameEventProjectVO) => {
   const _projectIds = row?.projectId || ids.value;
-  await proxy?.$modal.confirm('是否确认删除赛事项目编号为"' + _projectIds + '"的数据项?').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('是否确认删除赛事项目编号为"' + _projectIds + '"的数据项?').finally(() => (loading.value = false));
   await delGameEventProject(_projectIds);
-  proxy?.$modal.msgSuccess("删除成功");
+  proxy?.$modal.msgSuccess('删除成功');
   await getList();
-}
+};
 
 /** 导出按钮操作 */
 const handleExport = () => {
-  proxy?.download('system/gameEventProject/export', {
-    ...queryParams.value
-  }, `gameEventProject_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'system/gameEventProject/export',
+    {
+      ...queryParams.value
+    },
+    `gameEventProject_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
   getDefaultEventInfo().then(() => {

+ 148 - 31
src/views/system/gameTeam/index.vue

@@ -23,16 +23,23 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameTeam:add']">新增</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameTeam:add']">新增 </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameTeam:edit']">修改</el-button>
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameTeam:edit']"
+              >修改
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameTeam:remove']">删除</el-button>
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameTeam:remove']"
+              >删除
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameTeam:export']">导出</el-button>
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameTeam:export']">导出 </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:gameTeam:import']"> 导入 </el-button>
           </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
@@ -41,7 +48,9 @@
       <el-table v-loading="loading" border :data="gameTeamList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
         <el-table-column label="主键" align="center" prop="teamId" v-if="false" />
-        <el-table-column label="赛事ID" align="center" prop="eventId" />
+        <el-table-column label="队伍编号" align="center" prop="teamCode" />
+        <!--        <el-table-column label="赛事ID" align="center" prop="eventId" />-->
+        <el-table-column label="赛事名称" align="center" prop="eventName" />
         <el-table-column label="队伍名称" align="center" prop="teamName" />
         <el-table-column label="队伍编号" align="center" prop="teamCode" />
         <el-table-column label="团队描述" align="center" prop="teamDescribe" />
@@ -62,8 +71,10 @@
     <!-- 添加或修改参赛队伍对话框 -->
     <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
       <el-form ref="gameTeamFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="赛事ID" prop="eventId">
-          <el-input v-model="form.eventId" placeholder="请输入赛事ID" />
+        <el-form-item label="赛事" prop="eventId">
+          <el-select v-model="form.eventId" placeholder="请选择赛事" clearable filterable style="width: 100%">
+            <el-option v-for="option in eventOptions" :key="option.value" :label="option.label" :value="option.value" />
+          </el-select>
         </el-form-item>
         <el-form-item label="队伍名称" prop="teamName">
           <el-input v-model="form.teamName" placeholder="请输入队伍名称" />
@@ -82,6 +93,43 @@
         </div>
       </template>
     </el-dialog>
+
+    <!-- 用户导入对话框 -->
+    <el-dialog v-model="upload.open" :title="upload.title" width="400px" append-to-body>
+      <el-upload
+        ref="uploadRef"
+        :limit="1"
+        accept=".xlsx, .xls"
+        :headers="upload.headers"
+        :action="upload.url + '?updateSupport=' + upload.updateSupport"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <el-icon class="el-icon--upload">
+          <i-ep-upload-filled />
+        </el-icon>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <template #tip>
+          <div class="text-center el-upload__tip">
+            <div class="el-upload__tip">
+              <el-checkbox v-model="upload.updateSupport" />
+              是否更新已经存在的用户数据
+            </div>
+            <span>仅允许导入xls、xlsx格式文件。</span>
+            <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板 </el-link>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">确 定</el-button>
+          <el-button @click="upload.open = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -89,6 +137,8 @@
 import { listGameTeam, getGameTeam, delGameTeam, addGameTeam, updateGameTeam } from '@/api/system/gameTeam';
 import { getDefaultEvent } from '@/api/system/gameEvent';
 import { GameTeamVO, GameTeamQuery, GameTeamForm } from '@/api/system/gameTeam/types';
+import { getEventIdNameMap } from '@/api/system/gameEvent';
+import { globalHeaders } from '@/utils/request';
 import { GameEventVO } from '@/api/system/gameEvent/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -102,15 +152,32 @@ const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
+const eventOptions = ref<Array<{ label: string; value: string | number }>>([]);
 
 const queryFormRef = ref<ElFormInstance>();
 const gameTeamFormRef = ref<ElFormInstance>();
+const uploadRef = ref<ElUploadInstance>();
 
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
 });
 
+/*** 用户导入参数 */
+const upload = reactive<ImportOption>({
+  // 是否显示弹出层(用户导入)
+  open: false,
+  // 弹出层标题(用户导入)
+  title: '',
+  // 是否禁用上传
+  isUploading: false,
+  // 是否更新已经存在的用户数据
+  updateSupport: 0,
+  // 设置上传的请求头部
+  headers: globalHeaders(),
+  // 上传的地址
+  url: import.meta.env.VITE_APP_BASE_API + '/system/gameTeam/import'
+});
 const initFormData: GameTeamForm = {
   eventId: undefined,
   teamName: undefined,
@@ -123,9 +190,9 @@ const initFormData: GameTeamForm = {
   teamDescribe: undefined,
   status: undefined,
   remark: undefined
-}
+};
 const data = reactive<PageData<GameTeamForm, GameTeamQuery>>({
-  form: {...initFormData},
+  form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
@@ -190,25 +257,38 @@ const getList = async () => {
   gameTeamList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
+
+/** 获取赛事选项列表 */
+const getEventOptions = async () => {
+  try {
+    const res = await getEventIdNameMap();
+    eventOptions.value = Object.entries(res.data).map(([key, value]) => ({
+      label: key as string,
+      value: value
+    }));
+  } catch (error) {
+    console.error('获取赛事列表失败:', error);
+  }
+};
 
 /** 取消按钮 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 表单重置 */
 const reset = () => {
   form.value = {...initFormData, eventId: defaultEvent.value?.eventId};
   gameTeamFormRef.value?.resetFields();
-}
+};
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 重置按钮操作 */
 const resetQuery = () => {
@@ -216,31 +296,31 @@ const resetQuery = () => {
   // 保留默认赛事ID
   queryParams.value.eventId = defaultEvent.value?.eventId;
   handleQuery();
-}
+};
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection: GameTeamVO[]) => {
-  ids.value = selection.map(item => item.teamId);
+  ids.value = selection.map((item) => item.teamId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "添加参赛队伍";
-}
+  dialog.title = '添加参赛队伍';
+};
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: GameTeamVO) => {
   reset();
-  const _teamId = row?.teamId || ids.value[0]
+  const _teamId = row?.teamId || ids.value[0];
   const res = await getGameTeam(_teamId);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "修改参赛队伍";
-}
+  dialog.title = '修改参赛队伍';
+};
 
 /** 提交按钮 */
 const submitForm = () => {
@@ -248,36 +328,73 @@ const submitForm = () => {
     if (valid) {
       buttonLoading.value = true;
       if (form.value.teamId) {
-        await updateGameTeam(form.value).finally(() =>  buttonLoading.value = false);
+        await updateGameTeam(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addGameTeam(form.value).finally(() =>  buttonLoading.value = false);
+        await addGameTeam(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("操作成功");
+      proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 删除按钮操作 */
 const handleDelete = async (row?: GameTeamVO) => {
   const _teamIds = row?.teamId || ids.value;
-  await proxy?.$modal.confirm('是否确认删除参赛队伍编号为"' + _teamIds + '"的数据项?').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('是否确认删除参赛队伍编号为"' + _teamIds + '"的数据项?').finally(() => (loading.value = false));
   await delGameTeam(_teamIds);
-  proxy?.$modal.msgSuccess("删除成功");
+  proxy?.$modal.msgSuccess('删除成功');
   await getList();
-}
+};
 
 /** 导出按钮操作 */
 const handleExport = () => {
-  proxy?.download('system/gameTeam/export', {
-    ...queryParams.value
-  }, `gameTeam_${new Date().getTime()}.xlsx`)
+  proxy?.download(
+    'system/gameTeam/export',
+    {
+      ...queryParams.value
+    },
+    `gameTeam_${new Date().getTime()}.xlsx`
+  );
+};
+
+/** 导入按钮操作 */
+const handleImport = () => {
+  upload.title = '参赛队伍导入';
+  upload.open = true;
+};
+
+/**文件上传中处理 */
+const handleFileUploadProgress = () => {
+  upload.isUploading = true;
+};
+
+/** 文件上传成功处理 */
+const handleFileSuccess = (response: any, file: UploadFile) => {
+  upload.open = false;
+  upload.isUploading = false;
+  uploadRef.value?.handleRemove(file);
+  ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', {
+    dangerouslyUseHTMLString: true
+  });
+  getList();
+};
+
+/** 提交上传文件 */
+function submitFileForm() {
+  uploadRef.value?.submit();
 }
 
+/** 下载模板操作 */
+const importTemplate = () => {
+  proxy?.download('system/gameTeam/importTemplate', {}, `game_event_template_${new Date().getTime()}.xlsx`);
+};
+
 onMounted(() => {
   getDefaultEventInfo().then(() => {
     getList();
+    getEventOptions();
   });
 });
 </script>