Преглед на файлове

feat(physical): 添加计时器背景图片功能

- 在表格中新增计时器图片列用于显示背景图片
- 添加计时器背景图上传组件和预览功能
- 更新表单验证规则添加计时器背景图片必填校验
- 修改图片预览逻辑统一使用previewImageUrl变量
- 在API类型定义中添加timerBgPicture字段支持
fugui преди 4 дни
родител
ревизия
242b3bdb74
променени са 2 файла, в които са добавени 134 реда и са изтрити 2 реда
  1. 15 0
      src/api/system/physical/leagueTournament/types.ts
  2. 119 2
      src/views/system/physical/leagueTournament/index.vue

+ 15 - 0
src/api/system/physical/leagueTournament/types.ts

@@ -60,6 +60,11 @@ export interface LeagueTournamentVO {
    * 预报名结束时间
    */
   registrationEndTime?: string;
+
+  /**
+   * 计时器背景图片
+   */
+  timerBgPicture: string;
 }
 
 export interface LeagueTournamentForm extends BaseEntity {
@@ -126,6 +131,11 @@ export interface LeagueTournamentForm extends BaseEntity {
   registrationEndTime?: string;
 
   isRegistration: string | number;
+
+  /**
+   * 计时器背景图片
+   */
+  timerBgPicture: string;
 }
 
 export interface LeagueTournamentQuery extends PageQuery {
@@ -188,4 +198,9 @@ export interface LeagueTournamentQuery extends PageQuery {
    * 预报名结束时间
    */
   registrationEndTime?: string;
+
+  /**
+   * 计时器背景图片
+   */
+  timerBgPicture: string;
 }

+ 119 - 2
src/views/system/physical/leagueTournament/index.vue

@@ -71,6 +71,19 @@
             <span v-else></span>
           </template>
         </el-table-column>
+        <el-table-column label="计时器图片" align="center" width="90">
+          <template #default="scope">
+            <el-image
+              v-if="scope.row.timerBgPicture"
+              :src="scope.row.timerBgPicture"
+              style="width: 40px; height: 40px; border-radius: 4px; cursor: zoom-in"
+              :preview-src-list="[scope.row.timerBgPicture]"
+              :preview-teleported="true"
+              fit="cover"
+            />
+            <span v-else></span>
+          </template>
+        </el-table-column>
         <el-table-column label="赛事时间" align="center" width="300">
           <template #default="scope">
             <span>
@@ -204,7 +217,41 @@
             </el-upload>
           </div>
         </el-form-item>
+        <el-form-item label="计时器背景图" prop="timerBgPicture">
+          <div class="upload-container">
+            <el-upload
+              class="upload-icon"
+              action="#"
+              :on-change="handleTimerBgChange"
+              :on-remove="handleTimerBgRemove"
+              :file-list="timerBgFileList"
+              :auto-upload="false"
+              accept="image/*"
+            >
+              <template #trigger>
+                <el-button type="primary">点击选择图片</el-button>
+              </template>
 
+              <template #default>
+                <div class="preview-area" @click="handleTimerBgPreviewClick">
+                  <img
+                    v-if="timerBgPreviewUrl || form.timerBgPicture"
+                    :src="timerBgPreviewUrl || form.timerBgPicture"
+                    alt="计时器背景预览"
+                    style="max-width: 100px; max-height: 100px; margin-top: 10px; cursor: pointer"
+                  />
+                  <div v-else style="margin-top: 10px; color: #999">无图片</div>
+                </div>
+              </template>
+
+              <template #tip>
+                <div class="el-upload__tip">
+                  <span v-if="timerBgFileList.length > 0">当前已选文件:{{ timerBgFileList[0].name }}</span>
+                </div>
+              </template>
+            </el-upload>
+          </div>
+        </el-form-item>
         <el-form-item label="赛事开始时间" prop="startTime">
           <el-date-picker clearable v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择赛事开始时间">
           </el-date-picker>
@@ -251,7 +298,7 @@
       </template>
     </el-dialog>
     <el-dialog v-model="dialogVisible" title="图片预览" width="60%">
-      <img :src="iconPreviewUrl || form.imageUrl" alt="预览" style="max-width: 100%; max-height: 80vh" />
+      <img :src="previewImageUrl" alt="预览" style="max-width: 100%; max-height: 80vh" />
     </el-dialog>
   </div>
 </template>
@@ -295,6 +342,7 @@ const initFormData: LeagueTournamentForm = {
   id: undefined,
   title: undefined,
   imageUrl: undefined,
+  timerBgPicture: undefined,
   startTime: undefined,
   endTime: undefined,
   gameType: undefined,
@@ -310,6 +358,7 @@ const data = reactive<PageData<LeagueTournamentForm, LeagueTournamentQuery>>({
     pageSize: 10,
     title: undefined,
     imageUrl: undefined,
+    timerBgPicture: undefined,
     startTime: undefined,
     endTime: undefined,
     gameType: undefined,
@@ -322,6 +371,7 @@ const data = reactive<PageData<LeagueTournamentForm, LeagueTournamentQuery>>({
   rules: {
     id: [{ required: true, message: '锦标赛联赛ID不能为空', trigger: 'blur' }],
     title: [{ required: true, message: '赛事标题不能为空', trigger: 'blur' }],
+    timerBgPicture: [{ required: true, message: '不能为空', trigger: 'blur' }],
     gameVariant: [{ required: true, message: '比赛项目不能为空', trigger: 'blur' }],
     startTime: [{ required: true, message: '赛事开始时间不能为空', trigger: 'blur' }],
     endTime: [{ required: true, message: '赛事结束时间不能为空', trigger: 'blur' }],
@@ -385,9 +435,11 @@ const handleSelectionChange = (selection: LeagueTournamentVO[]) => {
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
-  // 清空文件列表
   fileList.value = [];
   iconPreviewUrl.value = '';
+  timerBgFileList.value = [];
+  timerBgPreviewUrl.value = '';
+  previewImageUrl.value = '';
   dialog.visible = true;
   dialog.title = '添加锦标赛联赛';
 };
@@ -413,6 +465,16 @@ const handleUpdate = async (row?: LeagueTournamentVO) => {
       }
     ];
   }
+  timerBgFileList.value = [];
+  if (res.data.timerBgPicture) {
+    timerBgFileList.value = [
+      {
+        name: '已上传',
+        url: res.data.timerBgPicture,
+        status: 'success'
+      }
+    ];
+  }
 };
 
 /** 提交按钮 */
@@ -459,6 +521,9 @@ onMounted(() => {
 const fileList = ref([]); // ← 新增
 const iconPreviewUrl = ref(''); // ← 新增
 const dialogVisible = ref(false); // ← 新增
+const previewImageUrl = ref('');
+const timerBgFileList = ref([]);
+const timerBgPreviewUrl = ref('');
 
 // 处理图片上传变化
 const handleIconChange = async (file) => {
@@ -510,6 +575,58 @@ const handleIconRemove = (file, updatedFileList) => {
 // 点击预览图放大
 const handlePreviewClick = () => {
   if (iconPreviewUrl.value || form.value.imageUrl) {
+    previewImageUrl.value = iconPreviewUrl.value || form.value.imageUrl;
+    dialogVisible.value = true;
+  }
+};
+const handleTimerBgChange = async (file) => {
+  const index = timerBgFileList.value.findIndex((f) => f.uid === file.uid);
+  timerBgFileList.value = [];
+
+  if (file.raw) {
+    timerBgPreviewUrl.value = URL.createObjectURL(file.raw);
+  }
+
+  if (index === -1) {
+    timerBgFileList.value.push(file);
+  }
+
+  try {
+    const rawFile = file.raw;
+    if (!rawFile) return;
+
+    const res = await uploadTournament(rawFile);
+    if (res.code === 200 && res.data?.url) {
+      timerBgFileList.value[index] = {
+        ...file,
+        status: 'success',
+        response: res.data.url
+      };
+      form.value.timerBgPicture = res.data.url;
+      timerBgPreviewUrl.value = res.data.url;
+      ElMessage.success('上传成功');
+    } else {
+      throw new Error(res.msg || '上传失败');
+    }
+  } catch (error) {
+    timerBgFileList.value[index] = {
+      ...file,
+      status: 'fail',
+      error: '上传失败'
+    };
+    ElMessage.error('上传失败,请重试');
+  }
+};
+
+const handleTimerBgRemove = (file, updatedFileList) => {
+  timerBgFileList.value = updatedFileList;
+  timerBgPreviewUrl.value = '';
+  form.value.timerBgPicture = '';
+};
+
+const handleTimerBgPreviewClick = () => {
+  if (timerBgPreviewUrl.value || form.value.timerBgPicture) {
+    previewImageUrl.value = timerBgPreviewUrl.value || form.value.timerBgPicture;
     dialogVisible.value = true;
   }
 };