Browse Source

feat(system):为比赛模板添加比赛背景图片功能新增比赛背景图片的上传、预览及展示功能,包括:- 表格中增加“比赛背景”列,用于显示背景图片缩略图
- 表单中新增“比赛背景”上传项,支持图片选择与预览
- 新增独立的图片预览对话框,用于展示背景图- 扩展类型定义,增加 competitionBg 字段以支持背景图存储

fugui001 2 months ago
parent
commit
48f8a7d4b4

+ 2 - 0
src/api/system/business/tournamentsTemplate/types.ts

@@ -76,6 +76,7 @@ export interface TournamentsVO {
    */
   blindStructureId?: number;
   competitionIcon?: string;
+  competitionBg?: string;
   blindStructuresName?: string;
   itemsName?: string;
   itemsPrizeList?: ItemsPrize[];
@@ -155,6 +156,7 @@ export interface TournamentsForm extends BaseEntity {
    */
   blindStructureId?: number;
   competitionIcon?: string;
+  competitionBg?: string;
   itemsPrizeList?: ItemsPrize[];
   robotCount: number;
 

+ 132 - 2
src/views/system/business/tournamentsTemplate/index.vue

@@ -59,6 +59,19 @@
             <span v-else></span>
           </template>
         </el-table-column>
+        <el-table-column label="比赛背景" align="center">
+          <template #default="scope">
+            <el-image
+              v-if="scope.row.competitionBg"
+              :src="scope.row.competitionBg"
+              style="width: 40px; height: 40px; border-radius: 4px; cursor: zoom-in"
+              :preview-src-list="[scope.row.competitionBg]"
+              :preview-teleported="true"
+              fit="cover"
+            />
+            <span v-else></span>
+          </template>
+        </el-table-column>
         <!--        <el-table-column label="开始时间" align="center" prop="startTime" width="150" sortable="custom"> </el-table-column>
         <el-table-column label="结束时间" align="center" prop="endTime" width="150"> </el-table-column>-->
         <el-table-column label="报名要求" align="center">
@@ -167,6 +180,50 @@
           </div>
         </el-form-item>
 
+        <!-- 比赛图标 -->
+        <el-form-item label="比赛背景" prop="icon">
+          <div class="upload-container">
+            <el-upload
+              class="upload-icon"
+              action="#"
+              :on-change="handleIconChange2"
+              :on-remove="handleIconRemove2"
+              :file-list="fileList2"
+              :auto-upload="false"
+              :limit="1"
+              accept="image/*"
+              :disabled="dialog.mode === 'view'"
+            >
+              <template #trigger>
+                <el-button type="primary" :disabled="dialog.mode === 'view'">点击选择背景</el-button>
+              </template>
+
+              <!-- 预览图区域 -->
+              <template #default>
+                <div class="preview-area" @click="handlePreviewClick2">
+                  <!-- 只有当 iconPreviewUrl 或 competitionIcon 存在时才显示 img -->
+                  <img
+                    v-if="iconPreviewUrl2 || competitionBg"
+                    :src="iconPreviewUrl2 || competitionBg"
+                    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="fileList2.length > 0">当前已选文件:{{ fileList2[0].name }}</span>
+                </div>
+              </template>
+            </el-upload>
+          </div>
+        </el-form-item>
+
+
+
         <!-- 开始时间 -->
         <!--        <el-form-item label="开始时间" prop="startTime">
           <el-date-picker
@@ -255,7 +312,6 @@
           <el-input v-model="form.delayCardNum" placeholder="请输入延迟卡数量" :disabled="dialog.mode === 'view'" />
         </el-form-item>
 
-
         <!-- 奖励内容 -->
         <el-form-item label="奖励内容">
           <div v-for="(reward, index) in formPrize.rewards" :key="index" style="display: flex; align-items: center; margin-bottom: 8px">
@@ -338,6 +394,11 @@
     <el-dialog v-model="dialogVisible" title="图片预览" width="50%">
       <img :src="previewSrc || iconPreviewUrl || competitionIcon" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
     </el-dialog>
+
+    <el-dialog v-model="dialogVisible2" title="图片预览" width="50%">
+      <img :src="previewSrc2 || iconPreviewUrl2 || competitionBg" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
+    </el-dialog>
+
   </div>
 </template>
 
@@ -395,10 +456,17 @@ const assignForm = reactive({
 const dialogVisible = ref(false);
 const previewSrc = ref('');
 
+const dialogVisible2 = ref(false);
+const previewSrc2 = ref('');
+
 function openPreview(src: string) {
   previewSrc.value = src;
   dialogVisible.value = true;
 }
+function openPreview2(src: string) {
+  previewSrc2.value = src;
+  dialogVisible2.value = true;
+}
 
 // 控制 Dialog 是否显示
 const levelsDialogVisible = ref(false);
@@ -533,6 +601,11 @@ const iconPreviewUrl = ref('');
 const competitionIcon = ref('');
 const fileList = ref([]);
 
+//预览图标需要
+const iconPreviewUrl2 = ref('');
+const competitionBg = ref('');
+const fileList2 = ref([]);
+
 // 单选控制变量(用于绑定 el-radio)
 const selectedRadio = ref<number | null>(null);
 
@@ -705,6 +778,9 @@ const cancel = () => {
   // 清除预览图和临时链接
   iconPreviewUrl.value = '';
   competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+
+  iconPreviewUrl2.value = '';
+  competitionBg.value = '';
   dialog.visible = false;
 };
 
@@ -741,6 +817,8 @@ const handleAdd = () => {
   // 清除预览图和临时链接
   iconPreviewUrl.value = '';
   competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+  iconPreviewUrl2.value = '';
+  competitionBg.value = '';
   dialog.visible = true;
   dialog.title = '创建自动比赛模版';
   dialog.mode = 'edit'; // 设置模式
@@ -764,7 +842,7 @@ const handleUpdate = async (row?: TournamentsVO, mode: 'edit' | 'view' = 'edit')
   Object.assign(form.value, res.data);
   form.value.gameType = gameType; // 确保赋值正确
   form.value.signTime = signTime;
-
+  competitionBg.value = res.data.competitionBg;
   competitionIcon.value = res.data.competitionIcon;
   // 处理奖励表单数据
   const prizeItems = res.data.itemsPrizeList || [];
@@ -981,7 +1059,45 @@ const handleIconChange = async (file) => {
     ElMessage.error('上传失败,请重试');
   }
 };
+const handleIconChange2 = async (file) => {
+  const index = fileList2.value.findIndex((f) => f.uid === file.uid);
+  fileList2.value = [];
+
+  if (file.raw) {
+    iconPreviewUrl2.value = URL.createObjectURL(file.raw);
+  }
 
+  if (index === -1) {
+    // 如果文件不在列表中,则添加进去
+    fileList2.value.push(file);
+  }
+
+  try {
+    const rawFile = file.raw;
+    const res = await uploadTournament(rawFile);
+    if (res.code === 200) {
+      // 更新文件状态为成功
+      fileList2.value[index] = {
+        ...file,
+        status: 'success',
+        response: res.data.url
+      };
+      data.form.competitionBg = fileList2.value[index].response;
+      iconPreviewUrl2.value = fileList2.value[index].response;
+      ElMessage.success('上传成功');
+    } else {
+      throw new Error(res.msg);
+    }
+  } catch (error) {
+    // 更新文件状态为失败
+    fileList2.value[index] = {
+      ...file,
+      status: 'fail',
+      error: '上传失败'
+    };
+    ElMessage.error('上传失败,请重试');
+  }
+};
 const handleViewLevels = () => {
   const blindStructureId = data.form.blindStructureId;
   if (blindStructureId === null || blindStructureId === undefined) {
@@ -1007,7 +1123,21 @@ const handleIconRemove = (file, updatedFileList) => {
   iconPreviewUrl.value = '';
   competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
 };
+// 点击预览图触发放大
+const handlePreviewClick2 = () => {
+  const currentSrc = iconPreviewUrl2.value || competitionBg.value;
+  if (currentSrc) {
+    dialogVisible2.value = true;
+  }
+};
 
+// 删除文件处理函数
+const handleIconRemove2 = (file, updatedFileList) => {
+  fileList2.value = updatedFileList;
+  // 清除预览图和临时链接
+  iconPreviewUrl2.value = '';
+  competitionBg.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
+};
 // 排序字段和顺序
 const sortData = ref({
   prop: 'startTime',