Selaa lähdekoodia

feat(system): 新增排期配置和创建方案功能

- 添加排期配置相关API接口
- 实现排期配置列表、新增、编辑、删除等功能页面
-集成创建方案相关API接口
- 优化时间选择器和下拉选项加载逻辑
fugui001 6 kuukautta sitten
vanhempi
commit
251bbdce2a

+ 120 - 0
src/api/system/business/config/index.ts

@@ -0,0 +1,120 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ConfigVO, ConfigForm, ConfigQuery } from '@/api/system/business/config/types';
+import { StructuresVO } from '@/api/system/business/structures/types';
+
+/**
+ * 查询排期配置:用于保存具体的排期实例配置列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listConfig = (query?: ConfigQuery): AxiosPromise<ConfigVO[]> => {
+  return request({
+    url: '/business/config/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询排期配置:用于保存具体的排期实例配置详细
+ * @param id
+ */
+export const getConfig = (id: string | number): AxiosPromise<ConfigVO> => {
+  return request({
+    url: '/business/config/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增排期配置:用于保存具体的排期实例配置
+ * @param data
+ */
+export const addConfig = (data: ConfigForm) => {
+  return request({
+    url: '/business/config',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改排期配置:用于保存具体的排期实例配置
+ * @param data
+ */
+export const updateConfig = (data: ConfigForm) => {
+  return request({
+    url: '/business/config',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除排期配置:用于保存具体的排期实例配置
+ * @param id
+ */
+export const delConfig = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/business/config/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 查询所有模版
+ */
+export const selectTemplateTourList = (): AxiosPromise<ConfigVO> => {
+  return request({
+    url: '/business/tournamentsTemplate/selectTemplateTourList',
+    method: 'get'
+  });
+};
+
+/**
+ * 新增排期配置:用于保存具体的排期实例配置
+ * @param data
+ */
+export const createSchedule = (data: ConfigForm) => {
+  return request({
+    url: '/business/config/createSchedule',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改排期配置:用于保存具体的排期实例配置
+ * @param data
+ */
+export const updateScheduleConfig = (data: ConfigForm) => {
+  return request({
+    url: '/business/config/updateScheduleConfig',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 删除排期配置:用于保存具体的排期实例配置
+ * @param id
+ */
+export const deleteScheduleConfig = (id: string | number): AxiosPromise<ConfigVO> => {
+  return request({
+    url: '/business/config/deleteScheduleConfig/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 停止配置
+ * @param id
+ */
+export const stopScheduleConfig = (id: string | number): AxiosPromise<ConfigVO> => {
+  return request({
+    url: '/business/config/stopScheduleConfig/' + id,
+    method: 'post'
+  });
+};

+ 146 - 0
src/api/system/business/config/types.ts

@@ -0,0 +1,146 @@
+export interface ConfigVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 模板ID
+   */
+  templateId: string | number;
+
+  /**
+   * 配置id
+   */
+  configId: string | number;
+
+  /**
+   * 创建方案ID
+   */
+  creationSchemeId: string | number;
+
+  /**
+   * 开始日期
+   */
+  startDate: string;
+
+  /**
+   * 结束日期
+   */
+  endDate: string;
+
+  /**
+   * 是否启用
+   */
+  enabled: number;
+
+  /**
+   * 创建时间
+   */
+  createdAt: string;
+
+  /**
+   * 更新时间
+   */
+  updatedAt: string;
+
+  execTimes: string;
+}
+
+export interface ConfigForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 配置ID
+   */
+  configId?: string | number;
+
+  /**
+   * 模板ID
+   */
+  templateId?: string | number;
+
+  /**
+   * 创建方案ID
+   */
+  creationSchemeId?: string | number;
+
+  creationSchemeCode?: string;
+
+  /**
+   * 开始日期
+   */
+  startDate?: string;
+
+  /**
+   * 结束日期
+   */
+  endDate?: string;
+
+  /**
+   * 是否启用
+   */
+  enabled?: number;
+
+  /**
+   * 创建时间
+   */
+  createdAt?: string;
+
+  /**
+   * 更新时间
+   */
+  updatedAt?: string;
+
+  /**
+   * 重复类型(多选),例如:['daily', 'weekly']
+   */
+  repeatTypes?: string[]; // 👈 新增:用于存储多选的重复类型
+
+  execTimes?: string[]; // 👈 新增:用于时间
+}
+
+export interface ConfigQuery extends PageQuery {
+  /**
+   * 模板ID
+   */
+  templateId?: string | number;
+
+  /**
+   * 创建方案ID
+   */
+  creationSchemeId?: string | number;
+
+  /**
+   * 开始日期
+   */
+  startDate?: string;
+
+  /**
+   * 结束日期
+   */
+  endDate?: string;
+
+  /**
+   * 是否启用
+   */
+  enabled?: boolean;
+
+  /**
+   * 创建时间
+   */
+  createdAt?: string;
+
+  /**
+   * 更新时间
+   */
+  updatedAt?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 73 - 0
src/api/system/business/scheme/index.ts

@@ -0,0 +1,73 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SchemeVO, SchemeForm, SchemeQuery } from '@/api/system/business/scheme/types';
+
+/**
+ * 查询创建方案:用于定义排期生成规则模板列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listScheme = (query?: SchemeQuery): AxiosPromise<SchemeVO[]> => {
+  return request({
+    url: '/business/scheme/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询创建方案:用于定义排期生成规则模板详细
+ * @param id
+ */
+export const getScheme = (id: string | number): AxiosPromise<SchemeVO> => {
+  return request({
+    url: '/business/scheme/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增创建方案:用于定义排期生成规则模板
+ * @param data
+ */
+export const addScheme = (data: SchemeForm) => {
+  return request({
+    url: '/business/scheme',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改创建方案:用于定义排期生成规则模板
+ * @param data
+ */
+export const updateScheme = (data: SchemeForm) => {
+  return request({
+    url: '/business/scheme',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除创建方案:用于定义排期生成规则模板
+ * @param id
+ */
+export const delScheme = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/business/scheme/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 查询所有模版
+ */
+export const selectAllCreationSchemesAllList = (): AxiosPromise<SchemeVO> => {
+  return request({
+    url: '/business/scheme/selectAllCreationSchemesAllList',
+    method: 'get'
+  });
+};

+ 111 - 0
src/api/system/business/scheme/types.ts

@@ -0,0 +1,111 @@
+export interface SchemeVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 唯一标识符
+   */
+  code: string;
+
+  /**
+   * 名称
+   */
+  name: string;
+
+  /**
+   * 描述
+   */
+  description: string;
+
+  /**
+   * 默认开始偏移天数
+   */
+  defaultStartOffset: number;
+
+  /**
+   * 默认持续天数
+   */
+  defaultDurationDays: number;
+
+  /**
+   * 是否激活
+   */
+  isActive: number;
+}
+
+export interface SchemeForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 唯一标识符
+   */
+  code?: string;
+
+  /**
+   * 名称
+   */
+  name?: string;
+
+  /**
+   * 描述
+   */
+  description?: string;
+
+  /**
+   * 默认开始偏移天数
+   */
+  defaultStartOffset?: number;
+
+  /**
+   * 默认持续天数
+   */
+  defaultDurationDays?: number;
+
+  /**
+   * 是否激活
+   */
+  isActive?: number;
+}
+
+export interface SchemeQuery extends PageQuery {
+
+  /**
+   * 唯一标识符
+   */
+  code?: string;
+
+  /**
+   * 名称
+   */
+  name?: string;
+
+  /**
+   * 描述
+   */
+  description?: string;
+
+  /**
+   * 默认开始偏移天数
+   */
+  defaultStartOffset?: number;
+
+  /**
+   * 默认持续天数
+   */
+  defaultDurationDays?: number;
+
+  /**
+   * 是否激活
+   */
+  isActive?: number;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 520 - 0
src/views/system/business/config/index.vue

@@ -0,0 +1,520 @@
+<template>
+  <div class="p-2">
+    <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-item label="模板ID" prop="templateId">
+              <el-input v-model="queryParams.templateId" placeholder="请输入模板ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="创建方案ID" prop="creationSchemeId">
+              <el-input v-model="queryParams.creationSchemeId" placeholder="请输入创建方案ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="开始日期" prop="startDate">
+              <el-date-picker clearable v-model="queryParams.startDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始日期" />
+            </el-form-item>
+            <el-form-item label="结束日期" prop="endDate">
+              <el-date-picker clearable v-model="queryParams.endDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择结束日期" />
+            </el-form-item>
+
+            <el-form-item label="创建时间" prop="createdAt">
+              <el-date-picker clearable v-model="queryParams.createdAt" type="date" value-format="YYYY-MM-DD" placeholder="请选择创建时间" />
+            </el-form-item>
+            <el-form-item label="更新时间" prop="updatedAt">
+              <el-date-picker clearable v-model="queryParams.updatedAt" type="date" value-format="YYYY-MM-DD" placeholder="请选择更新时间" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>-->
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <router-link to="/business/tournamentsTemplate">
+            <el-button type="success" plain icon="Edit">自动比赛模版</el-button>
+          </router-link>
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:config:add']">新增投放方案</el-button>
+          </el-col>
+          <!--          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:config:remove']"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:config:export']">导出</el-button>
+          </el-col>-->
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="configList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="false" />
+
+        <el-table-column label="比赛名" align="center" prop="name" />
+        <el-table-column label="比赛Logo" align="center">
+          <template #default="scope">
+            <el-image
+              v-if="scope.row.competitionIcon"
+              :src="scope.row.competitionIcon"
+              style="width: 40px; height: 40px; border-radius: 4px; cursor: zoom-in"
+              :preview-src-list="[scope.row.competitionIcon]"
+              :preview-teleported="true"
+              fit="cover"
+            />
+            <span v-else></span>
+          </template>
+        </el-table-column>
+        <el-table-column label="频率" align="center">
+          <template #default="scope">
+            <span v-html="formatFrequency(scope.row)"></span>
+          </template>
+        </el-table-column>
+        <el-table-column label="报名要求" align="center">
+          <template #default="scope">
+            {{
+              scope.row.itemsName && scope.row.itemsNum
+                ? `${scope.row.itemsName} x ${scope.row.itemsNum}`
+                : scope.row.itemsName || scope.row.itemsNum || '—'
+            }}
+          </template>
+        </el-table-column>
+
+        <el-table-column label="报名截止盲注等级" align="center" prop="lateRegistrationLevel" />
+        <el-table-column label="盲注表" align="center" prop="blindStructuresName" />
+
+        <el-table-column label="奖励" align="center">
+          <template #default="scope">
+            <div v-for="(prize, index) in scope.row.itemsPrizeList" :key="index">
+              第{{ prize.ranking }}名:{{ prize.quantity }} {{ prize.itemsName }}
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="状态" align="center" prop="statusText" />
+        <el-table-column label="操作" align="center" width="320">
+          <template #default="scope">
+            <!-- 查看按钮 -->
+            <el-tooltip content="查看" placement="top">
+              <el-button link type="primary" size="small" @click="handleUpdate(scope.row, 'view')" v-hasPermi="['system:config:edit']">
+                <el-icon><View /></el-icon>
+                查看
+              </el-button>
+            </el-tooltip>
+
+            <!-- 投放配置按钮 -->
+            <el-tooltip content="投放配置" placement="top">
+              <el-button link type="success" size="small" @click="handleUpdate(scope.row, 'edit')" v-hasPermi="['system:config:edit']">
+                <el-icon><Setting /></el-icon>
+                投放配置
+              </el-button>
+            </el-tooltip>
+
+            <!-- 停止按钮 -->
+            <el-tooltip content="停止" placement="top">
+              <el-button link type="warning" size="small" @click="handleStop(scope.row)" v-hasPermi="['system:config:remove']">
+                <el-icon><VideoPause /></el-icon>
+                停止
+              </el-button>
+            </el-tooltip>
+
+            <!-- 删除按钮 -->
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="danger" size="small" @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']">
+                <el-icon><Delete /></el-icon>
+                删除
+              </el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <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-form ref="configFormRef" :model="form" :rules="mode === 'view' ? {} : rules" label-width="80px">
+        <!-- 选择模版 -->
+        <el-form-item label="选择模版" prop="templateId">
+          <div style="display: flex; align-items: center">
+            <el-select v-model="form.templateId" :disabled="mode === 'view'" placeholder="选择模版" style="width: 200px">
+              <el-option v-for="item in itemOptionsTemplateList" :key="item.id" :label="item.label" :value="item.id" />
+            </el-select>
+          </div>
+        </el-form-item>
+
+        <!-- 重复设置 -->
+        <el-form-item label="重复设置" prop="repeatTypes">
+          <el-select v-model="form.repeatTypes" :disabled="mode === 'view'" placeholder="请选择重复类型" multiple style="width: 100%">
+            <el-option v-for="dict in repeat_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+          </el-select>
+        </el-form-item>
+
+        <!-- 时间设置 -->
+        <el-form-item label="时间设置">
+          <div style="margin-bottom: 8px">设置投放方案后,系统会依据设置时间和方案自动创建当天比赛;</div>
+          <div v-for="(reward, index) in formPrize.rewards" :key="index" style="display: flex; align-items: center; margin-bottom: 8px">
+            <span style="margin-right: 16px">场次{{ index + 1 }}:</span>
+
+            <!-- 时间选择器 -->
+            <el-time-picker
+              v-model="reward.execDates"
+              :disabled="mode === 'view'"
+              format="HH:mm"
+              value-format="HH:mm"
+              :picker-options="{
+                disabledMinutes: handleDisabledMinutes,
+                selectableRange: '00:00:00 - 23:59:59'
+              }"
+              placeholder="请选择时间"
+              style="width: 220px; margin-right: 8px"
+            />
+
+            <!-- 操作按钮:查看时不显示 +/- -->
+            <el-button v-if="mode !== 'view' && index === 0" type="primary" @click="addReward" size="small"> + </el-button>
+            <el-button v-if="mode !== 'view' && index !== 0" type="primary" @click="removeReward(index)" size="small"> - </el-button>
+          </div>
+        </el-form-item>
+
+        <!-- 创建方案 -->
+        <el-form-item label="创建方案" prop="creationSchemeCode">
+          <div style="display: flex; align-items: center">
+            <el-select v-model="form.creationSchemeCode" :disabled="mode === 'view'" placeholder="选择创建方案" style="width: 200px">
+              <el-option v-for="item in itemOptionsSchemeList" :key="item.id" :label="item.label" :value="item.code" />
+            </el-select>
+          </div>
+        </el-form-item>
+      </el-form>
+
+      <!-- 底部按钮 -->
+      <template #footer>
+        <div class="dialog-footer">
+          <!-- 查看模式下不显示确定按钮 -->
+          <el-button v-if="mode !== 'view'" :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+          <el-button @click="cancel">
+            {{ mode === 'view' ? '关 闭' : '取 消' }}
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Config" lang="ts">
+import {
+  listConfig,
+  getConfig,
+  delConfig,
+  createSchedule,
+  updateConfig,
+  selectTemplateTourList,
+  updateScheduleConfig,
+  deleteScheduleConfig,
+  stopScheduleConfig
+} from '@/api/system/business/config';
+import { selectAllCreationSchemesAllList } from '@/api/system/business/scheme';
+import { ConfigVO, ConfigQuery, ConfigForm } from '@/api/system/business/config/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { repeat_type } = toRefs<any>(proxy?.useDict('repeat_type'));
+const configList = ref<ConfigVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const configFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+// 新增 mode 字段:'add' | 'edit' | 'view'
+const mode = ref('add'); // 默认为添加
+
+const initFormData: ConfigForm = {
+  id: undefined,
+  templateId: undefined,
+  creationSchemeId: undefined,
+  startDate: undefined,
+  endDate: undefined,
+  enabled: undefined,
+  createdAt: undefined,
+  updatedAt: undefined
+};
+const data = reactive<PageData<ConfigForm, ConfigQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    templateId: undefined,
+    creationSchemeId: undefined,
+    startDate: undefined,
+    endDate: undefined,
+    enabled: undefined,
+    createdAt: undefined,
+    updatedAt: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '不能为空', trigger: 'blur' }],
+    templateId: [{ required: true, message: '模板ID不能为空', trigger: 'blur' }],
+    startDate: [{ required: true, message: '开始日期不能为空', trigger: 'blur' }],
+    endDate: [{ required: true, message: '结束日期不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询排期配置:用于保存具体的排期实例配置列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listConfig(queryParams.value);
+  configList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  resetFormPrize();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  configFormRef.value?.resetFields();
+  resetFormPrize();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ConfigVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  resetFormPrize();
+  mode.value = 'add';
+  dialog.visible = true;
+  dialog.title = '新增投放方案';
+};
+
+/**
+ * 处理修改或查看操作
+ * @param row 当前行数据
+ * @param action 'edit' | 'view'
+ */
+const handleUpdate = async (row, action = 'edit') => {
+  reset();
+  resetFormPrize();
+  mode.value = action; // 设置模式
+  dialog.title = action === 'view' ? '查看投放设置' : '编辑投放设置';
+
+  const _id = row?.id || ids.value[0];
+  const res = await getConfig(_id);
+
+  // 1. 赋值 form
+  Object.assign(form.value, res.data);
+
+  // 2. 处理 execTimes -> formPrize.rewards
+  if (res.data.execTimes && Array.isArray(res.data.execTimes)) {
+    formPrize.rewards = res.data.execTimes.filter((time) => time).map((time) => ({ execDates: time }));
+  } else {
+    formPrize.rewards = [{ execDates: '' }]; // 默认一个空行
+  }
+
+  dialog.visible = true;
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  configFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+
+      // ✅ 关键步骤:将 formPrize.rewards 中的时间提取为 execTimes 数组
+      form.value.execTimes = formPrize.rewards.map((reward) => reward.execDates).filter((time) => time); // 可选:过滤掉空值或 null
+
+      if (form.value.id) {
+        await updateScheduleConfig(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await createSchedule(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ConfigVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除排期配置,编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await deleteScheduleConfig(row.configId);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+//停止操作
+const handleStop = async (row?: ConfigVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认停止排期配置,编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await stopScheduleConfig(row.configId);
+  proxy?.$modal.msgSuccess('操作成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'system/config/export',
+    {
+      ...queryParams.value
+    },
+    `config_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+  handleOptionsTemplateChange();
+  handleOptionsSchemeChange();
+});
+
+// 下拉选项数据
+const itemOptionsTemplateList = ref<{ id: number; label: string }[]>([]);
+// 加载报名条件选项
+const handleOptionsTemplateChange = async () => {
+  try {
+    const res = await selectTemplateTourList();
+    if (res.code === 200) {
+      // 使用 unknown 中间类型进行类型转换
+      const data = res.data as unknown as { id: number; name: number }[];
+      const list = [];
+      for (let i = 0; i < data.length; i++) {
+        const item = data[i];
+        list.push({
+          id: item.id,
+          label: item.name
+        });
+      }
+      itemOptionsTemplateList.value = list;
+    } else {
+      alert('加载失败:' + res.msg);
+    }
+  } catch (error) {
+    console.error('请求出错:', error);
+  }
+};
+
+// 下拉选项数据
+const itemOptionsSchemeList = ref<{ id: number; label: string; code: string }[]>([]);
+// 加载报名条件选项
+const handleOptionsSchemeChange = async () => {
+  try {
+    const res = await selectAllCreationSchemesAllList();
+    if (res.code === 200) {
+      // 使用 unknown 中间类型进行类型转换
+      const data = res.data as unknown as { id: number; name: string; code: string }[];
+      const list = [];
+      for (let i = 0; i < data.length; i++) {
+        const item = data[i];
+        list.push({
+          id: item.id,
+          label: item.name,
+          code: item.code
+        });
+      }
+      itemOptionsSchemeList.value = list;
+    } else {
+      alert('加载失败:' + res.msg);
+    }
+  } catch (error) {
+    console.error('请求出错:', error);
+  }
+};
+
+const formPrize = reactive({
+  // ...其他表单字段
+  rewards: [
+    {
+      execDates: null
+    }
+  ]
+});
+const addReward = () => {
+  formPrize.rewards.push({
+    execDates: null
+  });
+};
+const removeReward = (index: number) => {
+  formPrize.rewards.splice(index, 1);
+};
+
+// 下拉选项数据
+const itemOptionsTimeList = ref<{ label: string; value: string }[]>([]);
+const handleDisabledMinutes = () => {
+  const minutes = [];
+  for (let i = 0; i < 60; i++) {
+    if (i % 30 !== 0) {
+      minutes.push(i);
+    }
+  }
+  return minutes;
+};
+
+// 用于保存原始的默认奖励结构
+const defaultTimes = {
+  execDates: null
+};
+// 重置函数
+const resetFormPrize = () => {
+  // 清空 rewards 并添加默认项
+  formPrize.rewards.splice(0); // 清空数组
+  formPrize.rewards.push({ ...defaultTimes }); // 添加初始项
+};
+
+// 格式化频率逻辑
+const formatFrequency = (row) => {
+  const { weekDayList = [], weekDaySize = 0 } = row;
+
+  // 1. 截取指定数量的周几
+  const selectedWeekDays = weekDayList;
+
+  // 2. 拼接最终文本
+  if (selectedWeekDays.length === 0) {
+    return '无'; // 如果没有周几信息
+  } else {
+    return `每日${weekDaySize}场<br>${selectedWeekDays.join('、')}每天${weekDaySize}场`;
+  }
+};
+</script>

+ 265 - 0
src/views/system/business/scheme/index.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="p-2">
+    <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-item label="唯一标识符" prop="code">
+              <el-input v-model="queryParams.code" placeholder="请输入唯一标识符" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="描述" prop="description">
+              <el-input v-model="queryParams.description" placeholder="请输入描述" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="默认开始偏移天数" prop="defaultStartOffset">
+              <el-input v-model="queryParams.defaultStartOffset" placeholder="请输入默认开始偏移天数" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="默认持续天数" prop="defaultDurationDays">
+              <el-input v-model="queryParams.defaultDurationDays" placeholder="请输入默认持续天数" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="是否激活" prop="isActive">
+              <el-input v-model="queryParams.isActive" placeholder="请输入是否激活" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <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:scheme:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:scheme:edit']"
+              >修改</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:scheme:remove']"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:scheme:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="schemeList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="true" />
+        <el-table-column label="唯一标识符" align="center" prop="code" />
+        <el-table-column label="名称" align="center" prop="name" />
+        <el-table-column label="描述" align="center" prop="description" />
+        <el-table-column label="默认开始偏移天数" align="center" prop="defaultStartOffset" />
+        <el-table-column label="默认持续天数" align="center" prop="defaultDurationDays" />
+        <el-table-column label="是否激活" align="center" prop="isActive" />
+        <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:scheme:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:scheme:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <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-form ref="schemeFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="唯一标识符" prop="code">
+          <el-input v-model="form.code" placeholder="请输入唯一标识符" />
+        </el-form-item>
+        <el-form-item label="名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入名称" />
+        </el-form-item>
+        <el-form-item label="描述" prop="description">
+          <el-input v-model="form.description" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="默认开始偏移天数" prop="defaultStartOffset">
+          <el-input v-model="form.defaultStartOffset" placeholder="请输入默认开始偏移天数" />
+        </el-form-item>
+        <el-form-item label="默认持续天数" prop="defaultDurationDays">
+          <el-input v-model="form.defaultDurationDays" placeholder="请输入默认持续天数" />
+        </el-form-item>
+        <el-form-item label="是否激活" prop="isActive">
+          <el-input v-model="form.isActive" placeholder="请输入是否激活" />
+        </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>
+  </div>
+</template>
+
+<script setup name="Scheme" lang="ts">
+import { listScheme, getScheme, delScheme, addScheme, updateScheme } from '@/api/system/business/scheme';
+import { SchemeVO, SchemeQuery, SchemeForm } from '@/api/system/business/scheme/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const schemeList = ref<SchemeVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const schemeFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: SchemeForm = {
+  id: undefined,
+  code: undefined,
+  name: undefined,
+  description: undefined,
+  defaultStartOffset: undefined,
+  defaultDurationDays: undefined,
+  isActive: undefined
+};
+const data = reactive<PageData<SchemeForm, SchemeQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    code: undefined,
+    name: undefined,
+    description: undefined,
+    defaultStartOffset: undefined,
+    defaultDurationDays: undefined,
+    isActive: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '不能为空', trigger: 'blur' }],
+    code: [{ required: true, message: '唯一标识符不能为空', trigger: 'blur' }],
+    name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
+    defaultStartOffset: [{ required: true, message: '默认开始偏移天数不能为空', trigger: 'blur' }],
+    defaultDurationDays: [{ required: true, message: '默认持续天数不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询创建方案:用于定义排期生成规则模板列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listScheme(queryParams.value);
+  schemeList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  schemeFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: SchemeVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '添加创建方案:用于定义排期生成规则模板';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: SchemeVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getScheme(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改创建方案:用于定义排期生成规则模板';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  schemeFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateScheme(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addScheme(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: SchemeVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除创建方案:用于定义排期生成规则模板编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delScheme(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'system/scheme/export',
+    {
+      ...queryParams.value
+    },
+    `scheme_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 249 - 0
src/views/system/business/structures/BlindStructureDialog.vue

@@ -0,0 +1,249 @@
+<template>
+  <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body @close="cancel">
+    <el-form ref="structuresFormRef" :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="上传盲注表" prop="file">
+        <div class="upload-container">
+          <el-upload
+            class="upload-demo"
+            action="#"
+            :on-change="handleFileChange"
+            :on-remove="handleFileRemove"
+            :file-list="fileList"
+            :auto-upload="false"
+            :limit="1"
+            accept=".xlsx,.xls"
+          >
+            <!-- 触发上传按钮 -->
+            <template #trigger>
+              <el-button type="primary">点击上传盲注表</el-button>
+            </template>
+
+            <!-- 自定义文件列表 -->
+            <template #file="{ file }">
+              <div style="display: flex; align-items: center">
+                <span class="el-upload__tip">{{ file.name }}</span>
+                <el-button type="text" icon="el-icon-delete" @click.stop="() => handleFileRemove(file)" style="margin-left: auto"> 删除 </el-button>
+              </div>
+            </template>
+
+            <!-- 提示信息 -->
+            <template #tip>
+              <div class="el-upload__tip">
+                <el-link type="primary" @click="downloadTemplate"> <i class="el-icon-download"></i> 模板下载 </el-link>
+                |
+                <el-button link @click="openPreview"> <i class="el-icon-document"></i> 预 览 </el-button>
+              </div>
+            </template>
+          </el-upload>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="盲注表备注" prop="description">
+        <el-input v-model="form.description" type="textarea" placeholder="请输入盲注表备注" />
+      </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="previewVisible" width="70%" append-to-body>
+    <div style="max-height: 500px; overflow-y: auto">
+      <el-table :data="previewData" border v-if="previewData.length > 0">
+        <!-- 使用动态表头 -->
+        <el-table-column v-for="(header, index) in previewHeaders" :key="index" :label="header">
+          <template #default="scope">
+            {{ scope.row[index] }}
+          </template>
+        </el-table-column>
+      </el-table>
+      <p v-else>暂无数据可预览</p>
+    </div>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="closePreview">关 闭</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup name="BlindStructureDialog" lang="ts">
+import { ref, reactive, toRefs, watch } from 'vue';
+import { ElMessage } from 'element-plus';
+import * as XLSX from 'xlsx';
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  },
+  mode: {
+    type: String,
+    default: 'add'
+  },
+  initialFormData: {
+    type: Object,
+    default: () => ({})
+  }
+});
+
+const emit = defineEmits(['update:visible', 'submit']);
+
+const dialog = reactive({
+  visible: props.visible,
+  title: props.mode === 'add' ? '新增盲注表' : '修改盲注表'
+});
+
+const buttonLoading = ref(false);
+const fileList = ref([]);
+const previewData = ref<any[]>([]);
+const previewHeaders = ref<string[]>([]);
+const previewVisible = ref(false);
+
+const initFormData = {
+  id: undefined,
+  name: undefined,
+  description: undefined
+};
+
+const data = reactive({
+  form: { ...initFormData, ...props.initialFormData },
+  rules: {
+    name: [{ required: true, message: '盲注表名称不能为空', trigger: 'blur' }]
+  }
+});
+
+const { form, rules } = toRefs(data);
+
+watch(
+  () => props.visible,
+  (newVal) => {
+    dialog.visible = newVal;
+    if (!newVal) {
+      reset();
+    }
+  }
+);
+
+const structuresFormRef = ref();
+
+const cancel = () => {
+  reset();
+  emit('update:visible', false);
+};
+
+const reset = () => {
+  Object.assign(data.form, initFormData);
+  structuresFormRef.value?.resetFields();
+  fileList.value = [];
+  previewData.value = [];
+  previewHeaders.value = [];
+};
+
+const submitForm = () => {
+  structuresFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+
+      try {
+        const formData = new FormData();
+
+        // 如果有文件,添加文件字段
+        if (fileList.value.length > 0 && fileList.value[0].raw) {
+          formData.append('file', fileList.value[0].raw); // 假设后端接收字段名为 'file'
+        } else {
+          buttonLoading.value = false;
+          return ElMessage.error('文件内容为空');
+        }
+
+        // 添加常规字段
+        formData.append('name', form.value.name || '');
+        formData.append('description', form.value.description || '');
+
+        emit('submit', formData);
+
+        buttonLoading.value = false;
+      } catch (error) {
+        ElMessage.error('提交失败');
+      }
+    }
+  });
+};
+
+const handleFileChange = (file) => {
+  const index = fileList.value.findIndex((f) => f.uid === file.uid);
+
+  // 如果文件不存在于列表中,则添加进去
+  if (index === -1) {
+    fileList.value.push(file);
+  }
+};
+
+// 删除文件处理函数
+const handleFileRemove = (file) => {
+  const index = fileList.value.findIndex((f) => f.uid === file.uid);
+  if (index > -1) {
+    const newFileList = [...fileList.value];
+    newFileList.splice(index, 1);
+    fileList.value = newFileList;
+  }
+};
+
+// 预览数据
+const openPreview = () => {
+  if (fileList.value.length === 0) {
+    return;
+  }
+
+  const file = fileList.value[0].raw;
+  const isValidType = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel'].includes(file.type);
+  if (!isValidType) {
+    return;
+  }
+
+  const reader = new FileReader();
+  reader.onload = (e) => {
+    const data = new Uint8Array(e.target?.result as ArrayBuffer);
+    const workbook = XLSX.read(data, { type: 'array' });
+
+    const sheetName = workbook.SheetNames[0];
+    const worksheet = workbook.Sheets[sheetName];
+
+    // 获取表头(第二行)
+    const headerRow = XLSX.utils.decode_range(worksheet['!ref']).e.r + 1;
+    const headerRange = `A2:E2`; // 假设最多5列
+    const headerCells = XLSX.utils.decode_range(headerRange);
+    previewHeaders.value = [];
+    for (let C = headerCells.s.c; C <= headerCells.e.c; ++C) {
+      const cellAddress = XLSX.utils.encode_cell({ c: C, r: 0 });
+      const cell = worksheet[cellAddress];
+      if (cell && cell.v) {
+        previewHeaders.value.push(cell.v.toString());
+      }
+    }
+
+    // 获取数据(第三行开始)
+    const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, range: 1 }); // 从第3行开始读取
+    previewData.value = jsonData;
+    previewVisible.value = true;
+  };
+  reader.readAsArrayBuffer(file);
+};
+
+const closePreview = () => {
+  previewVisible.value = false;
+};
+
+const downloadTemplate = async () => {
+  // 实现模板下载逻辑
+};
+</script>

+ 114 - 114
src/views/system/business/structures/BlindTableUploadDialog.vue

@@ -1,114 +1,114 @@
-<!--<template>-->
-<!--  <el-dialog :title="title" v-model="visible" width="500px" append-to-body @close="handleClose">-->
-<!--    <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">-->
-<!--      <el-form-item label="名称" prop="name">-->
-<!--        <el-input v-model="formData.name" placeholder="请输入名称" />-->
-<!--      </el-form-item>-->
-
-<!--      <el-form-item label="上传文件" prop="file">-->
-<!--        <div class="upload-container">-->
-<!--          <el-upload-->
-<!--            class="upload-demo"-->
-<!--            action="#"-->
-<!--            :on-change="handleFileChange"-->
-<!--            :file-list="fileList"-->
-<!--            :auto-upload="false"-->
-<!--            :limit="1"-->
-<!--            accept=".xlsx,.xls"-->
-<!--          >-->
-<!--            <template #trigger>-->
-<!--              <el-button type="primary">点击上传</el-button>-->
-<!--            </template>-->
-
-<!--            <template #tip>-->
-<!--              <div class="el-upload__tip">-->
-<!--                <el-link type="primary" @click="downloadTemplate">-->
-<!--                  <el-icon name="download" /> 模板下载-->
-<!--                </el-link>-->
-<!--                |-->
-<!--                <el-button link @click="openPreview">-->
-<!--                  <el-icon name="document" /> 预 览-->
-<!--                </el-button>-->
-<!--              </div>-->
-<!--            </template>-->
-<!--          </el-upload>-->
-<!--        </div>-->
-<!--      </el-form-item>-->
-
-<!--      <el-form-item label="备注" prop="description">-->
-<!--        <el-input v-model="formData.description" type="textarea" placeholder="请输入备注" />-->
-<!--      </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>-->
-<!--</template>-->
-
-<!--<script setup>import { ref, defineProps, defineEmits } from 'vue';-->
-
-<!--const props = defineProps({-->
-<!--  modelValue: Boolean,-->
-<!--  title: String,-->
-<!--  formData: Object,-->
-<!--  rules: Object,-->
-<!--  fileList: Array-->
-<!--});-->
-
-<!--const emit = defineEmits(['update:modelValue', 'submit', 'cancel', 'file-change', 'download-template', 'open-preview']);-->
-
-<!--const visible = ref(false);-->
-<!--const formRef = ref(null);-->
-
-<!--const buttonLoading = ref(false);-->
-
-<!--const handleClose = () => {-->
-<!--  emit('update:modelValue', false);-->
-<!--};-->
-
-<!--const handleFileChange = (file) => {-->
-<!--  emit('file-change', file);-->
-<!--};-->
-
-<!--const downloadTemplate = () => {-->
-<!--  emit('download-template');-->
-<!--};-->
-
-<!--const openPreview = () => {-->
-<!--  emit('open-preview');-->
-<!--};-->
-
-<!--const submitForm = () => {-->
-<!--  formRef.value?.validate(async (valid) => {-->
-<!--    if (valid) {-->
-<!--      buttonLoading.value = true;-->
-<!--      try {-->
-<!--        const formData = new FormData();-->
-
-<!--        if (props.fileList.length > 0 && props.fileList[0].raw) {-->
-<!--          formData.append('file', props.fileList[0].raw);-->
-<!--        } else {-->
-<!--          buttonLoading.value = false;-->
-<!--          return;-->
-<!--        }-->
-
-<!--        formData.append('name', props.formData.name || '');-->
-<!--        formData.append('description', props.formData.description || '');-->
-
-<!--        await emit('submit', formData);-->
-<!--        buttonLoading.value = false;-->
-<!--      } catch (error) {-->
-<!--        buttonLoading.value = false;-->
-<!--      }-->
-<!--    }-->
-<!--  });-->
-<!--};-->
-
-<!--const cancel = () => {-->
-<!--  emit('cancel');-->
-<!--};-->
-<!--</script>-->
+<template>
+  <el-dialog :title="title" v-model="visible" width="500px" append-to-body @close="handleClose">
+    <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
+      <el-form-item label="名称" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入名称" />
+      </el-form-item>
+
+      <el-form-item label="上传文件" prop="file">
+        <div class="upload-container">
+          <el-upload
+            class="upload-demo"
+            action="#"
+            :on-change="handleFileChange"
+            :file-list="fileList"
+            :auto-upload="false"
+            :limit="1"
+            accept=".xlsx,.xls"
+          >
+            <template #trigger>
+              <el-button type="primary">点击上传</el-button>
+            </template>
+
+            <template #tip>
+              <div class="el-upload__tip">
+                <el-link type="primary" @click="downloadTemplate">
+                  <el-icon name="download" /> 模板下载
+                </el-link>
+                |
+                <el-button link @click="openPreview">
+                  <el-icon name="document" /> 预 览
+                </el-button>
+              </div>
+            </template>
+          </el-upload>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="备注" prop="description">
+        <el-input v-model="formData.description" type="textarea" placeholder="请输入备注" />
+      </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>
+</template>
+
+<script setup>import { ref, defineProps, defineEmits } from 'vue';
+
+const props = defineProps({
+  modelValue: Boolean,
+  title: String,
+  formData: Object,
+  rules: Object,
+  fileList: Array
+});
+
+const emit = defineEmits(['update:modelValue', 'submit', 'cancel', 'file-change', 'download-template', 'open-preview']);
+
+const visible = ref(false);
+const formRef = ref(null);
+
+const buttonLoading = ref(false);
+
+const handleClose = () => {
+  emit('update:modelValue', false);
+};
+
+const handleFileChange = (file) => {
+  emit('file-change', file);
+};
+
+const downloadTemplate = () => {
+  emit('download-template');
+};
+
+const openPreview = () => {
+  emit('open-preview');
+};
+
+const submitForm = () => {
+  formRef.value?.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        const formData = new FormData();
+
+        if (props.fileList.length > 0 && props.fileList[0].raw) {
+          formData.append('file', props.fileList[0].raw);
+        } else {
+          buttonLoading.value = false;
+          return;
+        }
+
+        formData.append('name', props.formData.name || '');
+        formData.append('description', props.formData.description || '');
+
+        await emit('submit', formData);
+        buttonLoading.value = false;
+      } catch (error) {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+const cancel = () => {
+  emit('cancel');
+};
+</script>

+ 4 - 5
src/views/system/business/tournamentsTemplate/index.vue

@@ -63,8 +63,8 @@
             <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" 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">
           <template #default="scope">
             {{
@@ -101,7 +101,6 @@
               <el-tooltip content="删除" placement="top">
                 <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
               </el-tooltip>
-
             </div>
           </template>
         </el-table-column>
@@ -159,7 +158,7 @@
         </el-form-item>
 
         <!-- 开始时间 -->
-        <el-form-item label="开始时间" prop="startTime">
+<!--        <el-form-item label="开始时间" prop="startTime">
           <el-date-picker
             clearable
             v-model="form.startTime"
@@ -168,7 +167,7 @@
             placeholder="请选择开始时间"
             :disabled="dialog.mode === 'view'"
           />
-        </el-form-item>
+        </el-form-item>-->
 
         <el-form-item label="比赛类型" prop="gameType">
           <el-select aria-required="true" v-model="form.gameType" placeholder="请选择" :disabled="dialog.mode === 'view'">