|
|
@@ -101,7 +101,7 @@
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
</el-card>
|
|
|
<!-- 添加或修改【请填写功能名称】对话框 -->
|
|
|
- <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
|
|
+ <!-- <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
|
|
<el-form ref="tournamentsFormRef" :model="form" :rules="rules" label-width="80px">
|
|
|
<el-form-item label="赛事名称" prop="name">
|
|
|
<el-input v-model="form.name" placeholder="请输入赛事名称" />
|
|
|
@@ -129,29 +129,126 @@
|
|
|
<el-button @click="cancel">取 消</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
- </el-dialog>
|
|
|
+ </el-dialog>-->
|
|
|
+ <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="cancel">
|
|
|
+ <el-form ref="tournamentsFormRef" :model="form" :rules="rules" label-width="120px">
|
|
|
+ <!-- 赛事名称 -->
|
|
|
+ <el-form-item label="比赛名称" prop="name">
|
|
|
+ <el-input v-model="form.name" placeholder="请输入比赛名称" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 比赛图标 -->
|
|
|
+ <!-- <el-form-item label="比赛图标">
|
|
|
+ <el-button type="primary">点击选择图标</el-button>
|
|
|
+ </el-form-item>-->
|
|
|
+ <el-form-item label="比赛图标" prop="icon">
|
|
|
+ <div class="upload-container">
|
|
|
+ <el-upload
|
|
|
+ class="upload-icon"
|
|
|
+ action="#"
|
|
|
+ :on-change="handleIconChange"
|
|
|
+ :file-list="fileList"
|
|
|
+ :auto-upload="false"
|
|
|
+ :limit="1"
|
|
|
+ accept="image/*"
|
|
|
+ >
|
|
|
+ <template #trigger>
|
|
|
+ <el-button type="primary">点击选择图标</el-button>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #tip>
|
|
|
+ <div class="el-upload__tip">
|
|
|
+ <!-- 请选择 PNG/JPG/JPEG 格式图片-->
|
|
|
+ <span v-if="fileList.length > 0">当前已选文件:{{ fileList[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>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 报名时间 -->
|
|
|
+ <el-form-item label="报名时间">
|
|
|
+ <el-select v-model="form.signTime" placeholder="请选择">
|
|
|
+ <el-option label="比赛前5分钟" :value="5" />
|
|
|
+ <el-option label="比赛前10分钟" :value="10" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 报名条件 -->
|
|
|
+
|
|
|
+ <el-form-item label="报名条件">
|
|
|
+ <div style="display: flex; align-items: center; gap: 10px">
|
|
|
+ <el-select v-model="form.itemsId" placeholder="请选择道具类型">
|
|
|
+ <el-option v-for="item in itemOptions" :key="item.id" :label="item.label" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ <el-input v-model="form.itemsNum" :min="1" placeholder="数量" />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 盲注表 -->
|
|
|
+ <el-form-item label="盲注表">
|
|
|
+ <div style="display: flex; align-items: center">
|
|
|
+ <el-select v-model="form.blindStructureId" placeholder="选项" style="width: 200px" @change="handleBlindStructureChange">
|
|
|
+ <el-option v-for="item in itemOptionsStructures" :key="item.id" :label="item.label" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <el-button type="primary">上传新盲注</el-button>
|
|
|
+ <el-button type="primary" @click="handleViewLevels">预览</el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 报名截止等级 itemOptionsStructuresLevel-->
|
|
|
+ <el-form-item label="报名截止等级">
|
|
|
+ <el-select v-model="form.lateRegistrationLevel" placeholder="选项" style="width: 200px">
|
|
|
+ <el-option v-for="item in itemOptionsStructuresLevel" :key="item.id" :label="item.label" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ </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">
|
|
|
+ <span>第{{ reward.ranking }}名</span>
|
|
|
+
|
|
|
+ <!-- 道具选择 -->
|
|
|
+ <el-select v-model="reward.itemId" placeholder="选项" style="width: 120px; margin-right: 8px">
|
|
|
+ <el-option v-for="item in itemOptions" :key="item.id" :label="item.label" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <!-- 数量输入 -->
|
|
|
+ <el-input v-model="reward.quantity" placeholder="请输入数量" style="width: 100px; margin-right: 8px"></el-input>
|
|
|
|
|
|
+ <!-- 操作按钮 -->
|
|
|
+ <el-button type="primary" @click="addReward" v-if="index === 0">+</el-button>
|
|
|
+ <el-button type="primary" @click="removeReward(index)" v-if="index !== 0">-</el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
|
|
+ <el-button @click="cancel">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
<!-- 分配模态框 -->
|
|
|
<el-dialog title="分配盲注信息" v-model="assignDialog.visible" width="800px" append-to-body destroy-on-close>
|
|
|
<el-form ref="assignFormRef" :model="assignForm" label-width="100px">
|
|
|
<!-- 可勾选的列表 -->
|
|
|
<el-form-item label="">
|
|
|
- <div style="display: flex; flex-direction: column; align-items: center; width: 100%;">
|
|
|
- <div style="width: 100%; height: 400px; overflow-y: auto; padding: 0;">
|
|
|
- <el-table
|
|
|
- border
|
|
|
- ref="assignTable"
|
|
|
- :data="assignList"
|
|
|
-
|
|
|
- :row-key="'blindStructuresId'"
|
|
|
- style="width: 100%"
|
|
|
- >
|
|
|
+ <div style="display: flex; flex-direction: column; align-items: center; width: 100%">
|
|
|
+ <div style="width: 100%; height: 400px; overflow-y: auto; padding: 0">
|
|
|
+ <el-table border ref="assignTable" :data="assignList" :row-key="'blindStructuresId'" style="width: 100%">
|
|
|
<el-table-column label="操作" align="center" width="55">
|
|
|
<template #default="{ row }">
|
|
|
<el-radio v-model="selectedRadio" :label="row.blindStructuresId"> </el-radio>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
-<!-- <el-table-column label="blindStructuresId" align="center" prop="blindStructuresId" width="100" show-overflow-tooltip />-->
|
|
|
+ <!-- <el-table-column label="blindStructuresId" align="center" prop="blindStructuresId" width="100" show-overflow-tooltip />-->
|
|
|
<el-table-column prop="blindStructuresName" label="盲注结构名称" align="center" width="150" show-overflow-tooltip />
|
|
|
<el-table-column prop="blindDescription" label="盲注结构描述" align="center" width="200" show-overflow-tooltip />
|
|
|
<el-table-column prop="allocationStatus" label="绑定情况" align="center" width="100">
|
|
|
@@ -183,6 +280,11 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="levelsDialogVisible" title="盲注等级列表" width="80%">
|
|
|
+ <!-- 使用 component 动态加载目标组件 -->
|
|
|
+ <levels-index :blind-structure-id="dialogParams.blindStructureId" :name="dialogParams.name" />
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -194,9 +296,15 @@ import {
|
|
|
addTournaments,
|
|
|
updateTournaments,
|
|
|
getSelectTournamentBlindStructuresList,
|
|
|
- assignTournamentBlindStructures
|
|
|
+ assignTournamentBlindStructures,
|
|
|
+ uploadTournament
|
|
|
} from '@/api/system/business/tournaments';
|
|
|
+import { selectItemsSelList } from '@/api/system/business/items';
|
|
|
+import { selectBlindLevelsById } from '@/api/system/business/levels';
|
|
|
+import { selectBlingStructuresInfo } from '@/api/system/business/structures';
|
|
|
import { TournamentsVO, TournamentsQuery, TournamentsForm, TournamentsBindStructuresVO } from '@/api/system/business/tournaments/types';
|
|
|
+import { ref } from 'vue';
|
|
|
+import LevelsIndex from '@/views/system/business/levels/index.vue';
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
const tournamentsList = ref<TournamentsVO[]>([]);
|
|
|
@@ -226,6 +334,129 @@ const assignForm = reactive({
|
|
|
selectedIds: [] as number[]
|
|
|
});
|
|
|
|
|
|
+// 控制 Dialog 是否显示
|
|
|
+const levelsDialogVisible = ref(false);
|
|
|
+// 传递给子组件的参数
|
|
|
+const dialogParams = ref({
|
|
|
+ blindStructureId: null,
|
|
|
+ name: null
|
|
|
+});
|
|
|
+
|
|
|
+// 下拉选项数据 selectBlingStructuresInfo
|
|
|
+const itemOptions = ref<{ id: number; label: string }[]>([]);
|
|
|
+
|
|
|
+// 加载报名条件选项
|
|
|
+const loadItemOptions = async () => {
|
|
|
+ try {
|
|
|
+ const res = await selectItemsSelList();
|
|
|
+ if (res.code === 200) {
|
|
|
+ // 使用 unknown 中间类型进行类型转换
|
|
|
+ const data = res.data as unknown as { id: number; name: string }[];
|
|
|
+ const list = [];
|
|
|
+ for (let i = 0; i < data.length; i++) {
|
|
|
+ const item = data[i];
|
|
|
+ list.push({
|
|
|
+ id: item.id,
|
|
|
+ label: item.name
|
|
|
+ });
|
|
|
+ }
|
|
|
+ itemOptions.value = list;
|
|
|
+ } else {
|
|
|
+ alert('加载失败:' + res.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('请求出错:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 下拉选项数据 selectBlingStructuresInfo
|
|
|
+const itemOptionsStructures = ref<{ id: number; label: string }[]>([]);
|
|
|
+
|
|
|
+// 加载报名条件选项
|
|
|
+const loadItemStructuresOptions = async () => {
|
|
|
+ try {
|
|
|
+ const res = await selectBlingStructuresInfo();
|
|
|
+ if (res.code === 200) {
|
|
|
+ // 使用 unknown 中间类型进行类型转换
|
|
|
+ const data = res.data as unknown as { id: number; name: string }[];
|
|
|
+ const list = [];
|
|
|
+ for (let i = 0; i < data.length; i++) {
|
|
|
+ const item = data[i];
|
|
|
+ list.push({
|
|
|
+ id: item.id,
|
|
|
+ label: item.name
|
|
|
+ });
|
|
|
+ }
|
|
|
+ itemOptionsStructures.value = list;
|
|
|
+ } else {
|
|
|
+ alert('加载失败:' + res.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('请求出错:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 下拉选项数据
|
|
|
+const itemOptionsStructuresLevel = ref<{ id: number; label: string }[]>([]);
|
|
|
+
|
|
|
+// 加载报名条件选项
|
|
|
+const handleBlindStructureChange = async (value: number) => {
|
|
|
+ try {
|
|
|
+ const res = await selectBlindLevelsById(value);
|
|
|
+ if (res.code === 200) {
|
|
|
+ // 使用 unknown 中间类型进行类型转换
|
|
|
+ const data = res.data as unknown as { id: number; levelNumber: number }[];
|
|
|
+ const list = [];
|
|
|
+ for (let i = 0; i < data.length; i++) {
|
|
|
+ const item = data[i];
|
|
|
+ list.push({
|
|
|
+ id: item.levelNumber,
|
|
|
+ label: item.levelNumber
|
|
|
+ });
|
|
|
+ }
|
|
|
+ itemOptionsStructuresLevel.value = list;
|
|
|
+ } else {
|
|
|
+ alert('加载失败:' + res.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('请求出错:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+const formPrize = reactive({
|
|
|
+ // ...其他表单字段
|
|
|
+ rewards: [
|
|
|
+ {
|
|
|
+ ranking: 1,
|
|
|
+ itemId: '',
|
|
|
+ quantity: ''
|
|
|
+ }
|
|
|
+ ]
|
|
|
+});
|
|
|
+
|
|
|
+const addReward = () => {
|
|
|
+ const currentLength = formPrize.rewards.length;
|
|
|
+
|
|
|
+ // 判断是否超过 itemOptions 的数量限制
|
|
|
+ if (currentLength < itemOptions.value.length) {
|
|
|
+ formPrize.rewards.push({
|
|
|
+ ranking: currentLength + 1, // 自动生成排名,从 1 开始
|
|
|
+ itemId: null,
|
|
|
+ quantity: ''
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ ElMessage.warning(`最多只能添加 ${itemOptions.value.length} 个奖励项`);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const removeReward = (index: number) => {
|
|
|
+ formPrize.rewards.splice(index, 1);
|
|
|
+
|
|
|
+ // 删除后重新设置排名
|
|
|
+ formPrize.rewards.forEach((r, i) => {
|
|
|
+ r.ranking = i + 1;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
// 单选控制变量(用于绑定 el-radio)
|
|
|
const selectedRadio = ref<number | null>(null);
|
|
|
|
|
|
@@ -254,7 +485,13 @@ const initFormData: TournamentsForm = {
|
|
|
maxPlayers: undefined,
|
|
|
status: undefined,
|
|
|
createdAt: undefined,
|
|
|
- updatedAt: undefined
|
|
|
+ updatedAt: undefined,
|
|
|
+ signTime: null,
|
|
|
+ competitionIcon: null,
|
|
|
+ itemsId: null,
|
|
|
+ itemsNum: null,
|
|
|
+ blindStructureId: null,
|
|
|
+ itemsPrizeList: []
|
|
|
};
|
|
|
const data = reactive<PageData<TournamentsForm, TournamentsQuery>>({
|
|
|
form: { ...initFormData },
|
|
|
@@ -301,6 +538,7 @@ const getList = async () => {
|
|
|
const cancel = () => {
|
|
|
reset();
|
|
|
dialog.visible = false;
|
|
|
+ fileList.value = [];
|
|
|
};
|
|
|
|
|
|
/** 表单重置 */
|
|
|
@@ -332,7 +570,7 @@ const handleSelectionChange = (selection: TournamentsVO[]) => {
|
|
|
const handleAdd = () => {
|
|
|
reset();
|
|
|
dialog.visible = true;
|
|
|
- dialog.title = '添加【赛事信息】';
|
|
|
+ dialog.title = '创建比赛';
|
|
|
};
|
|
|
|
|
|
/** 修改按钮操作 */
|
|
|
@@ -342,22 +580,49 @@ const handleUpdate = async (row?: TournamentsVO) => {
|
|
|
const res = await getTournaments(_id);
|
|
|
Object.assign(form.value, res.data);
|
|
|
dialog.visible = true;
|
|
|
- dialog.title = '修改【赛事信息】';
|
|
|
+ dialog.title = '编辑比赛';
|
|
|
};
|
|
|
|
|
|
/** 提交按钮 */
|
|
|
const submitForm = () => {
|
|
|
tournamentsFormRef.value?.validate(async (valid: boolean) => {
|
|
|
- if (valid) {
|
|
|
+ // 校验奖励内容是否至少填写了一项
|
|
|
+ if (!formPrize.rewards.some((r) => r.itemId && r.quantity)) {
|
|
|
+ ElMessage.warning('请至少填写一项奖励内容');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!valid) return;
|
|
|
+ try {
|
|
|
buttonLoading.value = true;
|
|
|
- if (form.value.id) {
|
|
|
- await updateTournaments(form.value).finally(() => (buttonLoading.value = false));
|
|
|
+
|
|
|
+ // 构造最终要提交的数据对象
|
|
|
+ const formData: TournamentsForm = {
|
|
|
+ ...data.form,
|
|
|
+ competitionIcon: data.form.competitionIcon, // 确保 iconUrl 存在
|
|
|
+ itemsPrizeList: formPrize.rewards.map((reward) => ({
|
|
|
+ ranking: Number(reward.ranking),
|
|
|
+ itemId: Number(reward.itemId),
|
|
|
+ quantity: Number(reward.quantity)
|
|
|
+ }))
|
|
|
+ };
|
|
|
+
|
|
|
+ // 提交数据(区分新增/编辑)
|
|
|
+ let response;
|
|
|
+ if (formData.id) {
|
|
|
+ response = await updateTournaments(formData);
|
|
|
} else {
|
|
|
- await addTournaments(form.value).finally(() => (buttonLoading.value = false));
|
|
|
+ response = await addTournaments(formData);
|
|
|
}
|
|
|
+
|
|
|
proxy?.$modal.msgSuccess('操作成功');
|
|
|
dialog.visible = false;
|
|
|
await getList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提交失败:', error);
|
|
|
+ proxy?.$modal.msgError('提交失败,请重试');
|
|
|
+ } finally {
|
|
|
+ buttonLoading.value = false;
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
@@ -384,6 +649,8 @@ const handleExport = () => {
|
|
|
|
|
|
onMounted(() => {
|
|
|
getList();
|
|
|
+ loadItemOptions();
|
|
|
+ loadItemStructuresOptions();
|
|
|
});
|
|
|
|
|
|
async function getAssignList() {
|
|
|
@@ -416,7 +683,6 @@ async function submitAssign() {
|
|
|
proxy?.$modal.msgError('请选择至少一个盲注结构');
|
|
|
return;
|
|
|
}
|
|
|
- debugger;
|
|
|
buttonLoading.value = true;
|
|
|
try {
|
|
|
await assignTournamentBlindStructures({
|
|
|
@@ -466,4 +732,50 @@ watch(
|
|
|
}
|
|
|
}
|
|
|
);
|
|
|
+
|
|
|
+const fileList = ref([]);
|
|
|
+const handleIconChange = async (file) => {
|
|
|
+ const index = fileList.value.findIndex((f) => f.uid === file.uid);
|
|
|
+ fileList.value = [];
|
|
|
+ if (index === -1) {
|
|
|
+ // 如果文件不在列表中,则添加进去
|
|
|
+ fileList.value.push(file);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const rawFile = file.raw;
|
|
|
+ const res = await uploadTournament(rawFile);
|
|
|
+ if (res.code === 200) {
|
|
|
+ // 更新文件状态为成功
|
|
|
+ fileList.value[index] = {
|
|
|
+ ...file,
|
|
|
+ status: 'success',
|
|
|
+ response: res.data.url
|
|
|
+ };
|
|
|
+ data.form.competitionIcon = fileList.value[index].response;
|
|
|
+ ElMessage.success('上传成功');
|
|
|
+ } else {
|
|
|
+ throw new Error(res.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ // 更新文件状态为失败
|
|
|
+ fileList.value[index] = {
|
|
|
+ ...file,
|
|
|
+ status: 'fail',
|
|
|
+ error: '上传失败'
|
|
|
+ };
|
|
|
+ ElMessage.error('上传失败,请重试');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleViewLevels = () => {
|
|
|
+ const blindStructureId = data.form.blindStructureId;
|
|
|
+ if (blindStructureId === null || blindStructureId === undefined) {
|
|
|
+ ElMessage.warning('请先选择一个盲注表');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ dialogParams.value.blindStructureId = blindStructureId;
|
|
|
+ /* dialogParams.value.name = row.name;*/
|
|
|
+ levelsDialogVisible.value = true;
|
|
|
+};
|
|
|
</script>
|