|
@@ -12,6 +12,12 @@
|
|
|
<el-input v-model="queryParams.name" placeholder="请输入比赛名称" clearable @keyup.enter="handleQuery" />
|
|
<el-input v-model="queryParams.name" placeholder="请输入比赛名称" clearable @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
+ <el-form-item label="比赛状态" prop="gameType">
|
|
|
|
|
+ <el-select aria-required="true" v-model="queryParams.status" placeholder="请选择" :disabled="dialog.mode === 'view'">
|
|
|
|
|
+ <el-option v-for="dict in tournaments_status" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
<el-form-item label="开始时间" prop="startTime">
|
|
<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>
|
|
@@ -84,6 +90,21 @@
|
|
|
<span v-else></span>
|
|
<span v-else></span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="比赛背景" align="center" width="90">
|
|
|
|
|
+ <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="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" prop="endTime" width="150"> </el-table-column>
|
|
|
<el-table-column label="报名人数" align="center" prop="signNum" width="80"> </el-table-column>
|
|
<el-table-column label="报名人数" align="center" prop="signNum" width="80"> </el-table-column>
|
|
@@ -265,6 +286,48 @@
|
|
|
</div>
|
|
</div>
|
|
|
</el-form-item>
|
|
</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-form-item label="开始时间" prop="startTime">
|
|
|
<el-date-picker
|
|
<el-date-picker
|
|
@@ -454,6 +517,10 @@
|
|
|
<img :src="previewSrc || iconPreviewUrl || competitionIcon" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
|
|
<img :src="previewSrc || iconPreviewUrl || competitionIcon" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
|
|
|
</el-dialog>
|
|
</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>
|
|
|
|
|
+
|
|
|
<el-dialog
|
|
<el-dialog
|
|
|
:title="`${tournamentInfo.name} ${tournamentInfo.tournamentsBiId}`"
|
|
:title="`${tournamentInfo.name} ${tournamentInfo.tournamentsBiId}`"
|
|
|
v-model="auditDialog.visible"
|
|
v-model="auditDialog.visible"
|
|
@@ -567,7 +634,9 @@ import { ref } from 'vue';
|
|
|
import LevelsIndex from '@/views/system/business/levels/index.vue';
|
|
import LevelsIndex from '@/views/system/business/levels/index.vue';
|
|
|
import { ClaimsVO } from '@/api/system/business/claims/types';
|
|
import { ClaimsVO } from '@/api/system/business/claims/types';
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
-const { tournaments_type, tournaments_time } = toRefs<any>(proxy?.useDict('tournaments_type', 'tournaments_time'));
|
|
|
|
|
|
|
+const { tournaments_type, tournaments_time, tournaments_status } = toRefs<any>(
|
|
|
|
|
+ proxy?.useDict('tournaments_type', 'tournaments_time', 'tournaments_status')
|
|
|
|
|
+);
|
|
|
|
|
|
|
|
const tournamentsList = ref<TournamentsVO[]>([]);
|
|
const tournamentsList = ref<TournamentsVO[]>([]);
|
|
|
const buttonLoading = ref(false);
|
|
const buttonLoading = ref(false);
|
|
@@ -603,11 +672,17 @@ const assignForm = reactive({
|
|
|
const dialogVisible = ref(false);
|
|
const dialogVisible = ref(false);
|
|
|
const previewSrc = ref('');
|
|
const previewSrc = ref('');
|
|
|
|
|
|
|
|
|
|
+const dialogVisible2 = ref(false);
|
|
|
|
|
+const previewSrc2 = ref('');
|
|
|
|
|
+
|
|
|
function openPreview(src: string) {
|
|
function openPreview(src: string) {
|
|
|
previewSrc.value = src;
|
|
previewSrc.value = src;
|
|
|
dialogVisible.value = true;
|
|
dialogVisible.value = true;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+function openPreview2(src: string) {
|
|
|
|
|
+ previewSrc2.value = src;
|
|
|
|
|
+ dialogVisible2.value = true;
|
|
|
|
|
+}
|
|
|
// 控制 Dialog 是否显示
|
|
// 控制 Dialog 是否显示
|
|
|
const levelsDialogVisible = ref(false);
|
|
const levelsDialogVisible = ref(false);
|
|
|
// 传递给子组件的参数
|
|
// 传递给子组件的参数
|
|
@@ -740,6 +815,12 @@ const removeReward = (index: number) => {
|
|
|
const iconPreviewUrl = ref('');
|
|
const iconPreviewUrl = ref('');
|
|
|
const competitionIcon = ref('');
|
|
const competitionIcon = ref('');
|
|
|
const fileList = ref([]);
|
|
const fileList = ref([]);
|
|
|
|
|
+
|
|
|
|
|
+//预览图标需要
|
|
|
|
|
+const iconPreviewUrl2 = ref('');
|
|
|
|
|
+const competitionBg = ref('');
|
|
|
|
|
+const fileList2 = ref([]);
|
|
|
|
|
+
|
|
|
import { parseTime } from '@/utils/dateUtils';
|
|
import { parseTime } from '@/utils/dateUtils';
|
|
|
// 单选控制变量(用于绑定 el-radio)
|
|
// 单选控制变量(用于绑定 el-radio)
|
|
|
const selectedRadio = ref<number | null>(null);
|
|
const selectedRadio = ref<number | null>(null);
|
|
@@ -913,6 +994,9 @@ const cancel = () => {
|
|
|
// 清除预览图和临时链接
|
|
// 清除预览图和临时链接
|
|
|
iconPreviewUrl.value = '';
|
|
iconPreviewUrl.value = '';
|
|
|
competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
|
|
|
+
|
|
|
|
|
+ iconPreviewUrl2.value = '';
|
|
|
|
|
+ competitionBg.value = '';
|
|
|
dialog.visible = false;
|
|
dialog.visible = false;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -955,6 +1039,8 @@ const handleAdd = () => {
|
|
|
// 清除预览图和临时链接
|
|
// 清除预览图和临时链接
|
|
|
iconPreviewUrl.value = '';
|
|
iconPreviewUrl.value = '';
|
|
|
competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
|
|
|
+ iconPreviewUrl2.value = '';
|
|
|
|
|
+ competitionBg.value = '';
|
|
|
dialog.visible = true;
|
|
dialog.visible = true;
|
|
|
dialog.title = '创建比赛';
|
|
dialog.title = '创建比赛';
|
|
|
dialog.mode = 'add'; // 设置模式
|
|
dialog.mode = 'add'; // 设置模式
|
|
@@ -975,6 +1061,7 @@ const handleUpdate = async (row?: TournamentsVO, mode: 'edit' | 'view' = 'edit')
|
|
|
form.value.signTime = String(res.data.signTime);
|
|
form.value.signTime = String(res.data.signTime);
|
|
|
form.value.gameType = String(res.data.gameType); // 转为字符串
|
|
form.value.gameType = String(res.data.gameType); // 转为字符串
|
|
|
competitionIcon.value = res.data.competitionIcon;
|
|
competitionIcon.value = res.data.competitionIcon;
|
|
|
|
|
+ competitionBg.value = res.data.competitionBg;
|
|
|
// 处理奖励表单数据
|
|
// 处理奖励表单数据
|
|
|
const prizeItems = res.data.itemsPrizeList || [];
|
|
const prizeItems = res.data.itemsPrizeList || [];
|
|
|
if (prizeItems.length > 0) {
|
|
if (prizeItems.length > 0) {
|
|
@@ -1240,6 +1327,46 @@ const handleIconChange = async (file) => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+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 handleViewLevels = () => {
|
|
|
const blindStructureId = data.form.blindStructureId;
|
|
const blindStructureId = data.form.blindStructureId;
|
|
|
if (blindStructureId === null || blindStructureId === undefined) {
|
|
if (blindStructureId === null || blindStructureId === undefined) {
|
|
@@ -1258,6 +1385,14 @@ const handlePreviewClick = () => {
|
|
|
dialogVisible.value = true;
|
|
dialogVisible.value = true;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
+// 点击预览图触发放大
|
|
|
|
|
+const handlePreviewClick2 = () => {
|
|
|
|
|
+ const currentSrc = iconPreviewUrl2.value || competitionBg.value;
|
|
|
|
|
+ if (currentSrc) {
|
|
|
|
|
+ dialogVisible2.value = true;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 删除文件处理函数
|
|
// 删除文件处理函数
|
|
|
const handleIconRemove = (file, updatedFileList) => {
|
|
const handleIconRemove = (file, updatedFileList) => {
|
|
|
fileList.value = updatedFileList;
|
|
fileList.value = updatedFileList;
|
|
@@ -1266,6 +1401,14 @@ const handleIconRemove = (file, updatedFileList) => {
|
|
|
competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
competitionIcon.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 删除文件处理函数
|
|
|
|
|
+const handleIconRemove2 = (file, updatedFileList) => {
|
|
|
|
|
+ fileList2.value = updatedFileList;
|
|
|
|
|
+ // 清除预览图和临时链接
|
|
|
|
|
+ iconPreviewUrl2.value = '';
|
|
|
|
|
+ competitionBg.value = ''; // 如果需要清除后台加载的图标,可以在这里设置为空字符串
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 排序字段和顺序
|
|
// 排序字段和顺序
|
|
|
const sortData = ref({
|
|
const sortData = ref({
|
|
|
prop: 'startTime',
|
|
prop: 'startTime',
|