From ce5ee2a289e08268a5d3225d03de5566fa93b906 Mon Sep 17 00:00:00 2001 From: rjl <98n9@163.com> Date: Wed, 4 Feb 2026 18:01:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E6=96=B0=E5=A2=9E=E6=B4=BB?= =?UTF-8?q?=E5=8A=A8=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=AD=97=E5=85=B8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增活动管理相关功能,包括活动创建、编辑、删除和状态管理 - 新增 PublishStatus 枚举定义活动发布状态 - 优化字典组件,支持字符串和数字类型的字典值 - 重构业务数据存储逻辑,简化字典数据获取流程 - 新增活动详情页面,支持分组管理和队伍配置 - 集成富文本编辑器用于活动规则编辑 - 优化图片上传组件,支持小图上传模式 - 修复模板管理中的区域选择和分页问题 - 更新 TypeScript 类型定义,完善 API 接口 - 调整主题配置,新增活动状态相关颜色 --- apps/admin/package.json | 2 + .../common/oss-image-upload/index.vue | 54 +- apps/admin/src/enum/business.ts | 6 + apps/admin/src/hooks/business/useDict.ts | 19 +- apps/admin/src/hooks/common/table.ts | 38 +- apps/admin/src/service/api/competition.ts | 100 +++ apps/admin/src/service/api/dictionary.ts | 11 + .../admin/src/store/modules/business/index.ts | 17 +- apps/admin/src/theme/settings.ts | 6 +- apps/admin/src/theme/vars.ts | 12 +- apps/admin/src/typings/api/Competition.d.ts | 127 +++- apps/admin/src/typings/app.d.ts | 4 + apps/admin/src/typings/components.d.ts | 4 + apps/admin/src/typings/wangeditor.d.ts | 8 + .../competition/competition-add/index.vue | 70 +- .../competition-add/modules/BasicInfo.vue | 161 ++++- .../modules/GroupManagement.vue | 17 +- .../modules/QuestionConfig.vue | 69 +- .../competition-add/modules/useBasicInfo.ts | 17 + .../modules/useGroupManagement.ts | 65 +- .../modules/useQuestionConfig.ts | 207 ++++-- .../competition/competition-detail/index.vue | 408 +++++++++-- .../modules/BasicInfoCard.vue | 336 +++++++-- .../modules/QuestionConfigCard.vue | 662 ++++++++++++------ .../competition-detail/modules/SideMenu.vue | 67 +- .../modules/TeamListCard.vue | 84 ++- .../competition/competition-list/index.vue | 60 +- .../modules/competition-card.vue | 39 +- apps/admin/src/views/dictionary/index.vue | 86 +-- .../modules/DictionaryTypeModal.vue | 2 +- .../question-store/modules/CategoryTree.vue | 4 +- .../views/template/template-detail/index.vue | 3 +- .../modules/TemplateCanvas.vue | 2 +- .../modules/TemplateSettings.vue | 36 +- .../views/template/template-list/index.vue | 2 +- apps/admin/tsconfig.json | 2 +- apps/admin/tsconfig.tsbuildinfo | 2 +- packages/hooks/src/use-table.ts | 28 +- pnpm-lock.yaml | 477 +++++++++++++ 39 files changed, 2621 insertions(+), 693 deletions(-) create mode 100644 apps/admin/src/enum/business.ts create mode 100644 apps/admin/src/typings/wangeditor.d.ts diff --git a/apps/admin/package.json b/apps/admin/package.json index 6ce75d6..e4134de 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -35,6 +35,8 @@ "@sa/utils": "workspace:*", "@sapphire/snowflake": "^3.5.5", "@vueuse/core": "14.1.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", "@zumer/snapdom": "^2.0.1", "ali-oss": "^6.23.0", "clipboard": "2.0.11", diff --git a/apps/admin/src/components/common/oss-image-upload/index.vue b/apps/admin/src/components/common/oss-image-upload/index.vue index f951486..e9c64e4 100644 --- a/apps/admin/src/components/common/oss-image-upload/index.vue +++ b/apps/admin/src/components/common/oss-image-upload/index.vue @@ -10,16 +10,19 @@ const props = withDefaults(defineProps<{ /** 图片地址 */ modelValue?: string /** 上传路径 */ - path?: string + path?: string // 上传路径,默认 temp/ 目录,同名文件会覆盖 /** 提示文字 */ hint?: string /** 最大文件大小 (KB) */ maxSize?: number /** 接受的文件类型 */ accept?: string + /** 模式: default-大图上传(带文字), mini-小图上传(仅图标) */ + variant?: 'default' | 'mini' }>(), { hint: '支持 JPG/PNG 格式', accept: 'image/*', + variant: 'default', }) const emit = defineEmits(['update:modelValue']) @@ -80,14 +83,19 @@ function handleRemove() { :accept="accept" :show-file-list="false" :custom-request="customRequest" - class="block w-full" + class="block" + :class="{ 'w-full': props.variant === 'default' }" >
- +
@@ -119,5 +148,6 @@ function handleRemove() { diff --git a/apps/admin/src/enum/business.ts b/apps/admin/src/enum/business.ts new file mode 100644 index 0000000..64c701f --- /dev/null +++ b/apps/admin/src/enum/business.ts @@ -0,0 +1,6 @@ +export enum PublishStatus { + Unpublished = 0, + Published = 1, + Processing = 2, + Finished = 3, +} diff --git a/apps/admin/src/hooks/business/useDict.ts b/apps/admin/src/hooks/business/useDict.ts index 5024987..6939649 100644 --- a/apps/admin/src/hooks/business/useDict.ts +++ b/apps/admin/src/hooks/business/useDict.ts @@ -1,18 +1,25 @@ import { computed, onMounted } from 'vue' import { useBusinessStore } from '@/store/modules/business' -export function useDict(key: string) { +/** + * 字典项 hook + * @param key 字典项 key + * @param valueType 字典项 value 类型,默认 number + */ +export function useDict(key: string, valueType: 'string' | 'number' = 'number') { const store = useBusinessStore() const data = computed(() => store.dictData[key] || []) const loading = computed(() => store.loadingMap[key] || false) - const options = computed(() => { if (data.value && data.value.length > 0) { - return data.value.map((item: any) => ({ - label: item.uI_Key || item.UI_Key || item.DicKey, - value: item.uI_Value || item.UI_Value || item.DicValue, - })) + return data.value.map((item: any) => { + const rawValue = item.uI_Value || item.UI_Value || item.DicValue + return { + label: item.uI_Key || item.UI_Key || item.DicKey, + value: valueType === 'number' ? Number(rawValue) : String(rawValue), + } + }) } return [] }) diff --git a/apps/admin/src/hooks/common/table.ts b/apps/admin/src/hooks/common/table.ts index 52a81e1..f3a72f7 100644 --- a/apps/admin/src/hooks/common/table.ts +++ b/apps/admin/src/hooks/common/table.ts @@ -28,8 +28,12 @@ const SELECTION_KEY = '__selection__' const EXPAND_KEY = '__expand__' +/** + * NaiveUI 表格 + * @param options 表格选项 + */ export function useNaiveTable(options: UseNaiveTableOptions) { - const scope = effectScope() + const scope = effectScope() // 表格作用域,用于管理表格的响应式数据 const appStore = useAppStore() const result = useTable, false>({ @@ -77,6 +81,10 @@ type UseNaivePaginatedTableOptions = UseNaiveTableOptions onPaginationParamsChange?: (params: PaginationParams) => void | Promise } +/** + * NaiveUI 分页表格 + * @param options 表格选项 + */ export function useNaivePaginatedTable( options: UseNaivePaginatedTableOptions, ) { @@ -172,21 +180,27 @@ export function useNaivePaginatedTable( } } +/** + * NaiveUI 表格操作 + * @param data 表格数据 + * @param idKey 表格数据的主键字段 + * @param getData 获取表格数据的函数 + */ export function useTableOperate( data: Ref, idKey: keyof TableData, getData: () => Promise, ): { - drawerVisible: Ref - openDrawer: () => void - closeDrawer: () => void - operateType: Ref - handleAdd: () => void - editingData: Ref - handleEdit: (id: TableData[keyof TableData]) => void - checkedRowKeys: Ref - onBatchDeleted: () => Promise - onDeleted: () => Promise + drawerVisible: Ref /** 抽屉是否可见 */ + openDrawer: () => void /** 打开抽屉 */ + closeDrawer: () => void /** 关闭抽屉 */ + operateType: Ref /** 操作类型 */ + handleAdd: () => void /** 新增操作 */ + editingData: Ref /** 编辑行数据 */ + handleEdit: (id: TableData[keyof TableData]) => void /** 编辑操作 */ + checkedRowKeys: Ref /** 表格的选中行 keys */ + onBatchDeleted: () => Promise /** 批量删除操作完成后的钩子 */ + onDeleted: () => Promise /** 删除操作完成后的钩子 */ } { const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean() @@ -253,7 +267,7 @@ export function defaultTransform( const { data, error } = response if (!error) { - const { list, currentPage: pageNum, pageSize: size, total } = data + const { data: list, currentPage: pageNum, pageSize: size, total = 0 } = data return { data: list, diff --git a/apps/admin/src/service/api/competition.ts b/apps/admin/src/service/api/competition.ts index 33f6ec0..b39a175 100644 --- a/apps/admin/src/service/api/competition.ts +++ b/apps/admin/src/service/api/competition.ts @@ -23,6 +23,14 @@ export function fetchCreateActivity(data: Api.Competition.CreateRoomRequest) { }) } +/** 获取活动的基础信息 */ +export function fetchGetActivityDetail(id: number) { + return request>({ + url: `/Base/ActivityMain/GetActivityByID/?ID=${id}`, + method: 'get', + }) +} + /** 创建活动队伍 */ export function fetchCreateTeamList(data: Api.Competition.CreateTeamListRequest[]) { return request>({ @@ -32,6 +40,14 @@ export function fetchCreateTeamList(data: Api.Competition.CreateTeamListRequest[ }) } +/** 根据活动ID获取活动队伍列表 */ +export function fetchGetTeamList(id: number) { + return request({ + url: `/Base/ActivityMain/GetActivity_TeamsByMainID/?MainID=${id}`, + method: 'get', + }) +} + /** 创建活动题目 */ export function fetchCreateQuestion(data: Api.Competition.CreateQuestionRequest[]) { return request>({ @@ -40,3 +56,87 @@ export function fetchCreateQuestion(data: Api.Competition.CreateQuestionRequest[ data, }) } + +/** 根据活动ID获取活动题目列表 */ +export function fetchGetQuestionList(id: number) { + return request>({ + url: `/Base/ActivityMain/GetActivity_QuestionByActivityID?ID=${id}`, + method: 'get', + }) +} + +/** 获取活动列表 */ +export function fetchGetActivityList() { + return request({ + url: '/Base/ActivityMain/Activity_MainList', + method: 'get', + }) +} + +/** 新增活动 (包括基础信息、房间、队伍、题目) */ +export function fetchCreateCompetition(data: any) { + return request>({ + url: '/Base/ActivityMain/AddActivityAllData', + method: 'post', + data, + }) +} + +/** 根据活动id查询分组列表 */ +export function fetchGetGroupList(id: string) { + return request({ + url: `/Base/ActivityMain/GetActivity_TeamGroupByMainID?ActivityID=${id}`, + method: 'get', + }) +} + +/** 根据组id查询队伍列表 */ +export function fetchGetTeamListByGroupId(GroupID: number) { + return request>({ + url: `/Base/ActivityMain/GetActivity_TeamsByTeamGroupID?GroupID=${GroupID}`, + method: 'get', + }) +} + +/** 更新活动基础信息 */ +export function fetchUpdateActivity(data: Api.Competition.ActivityDetail) { + return request>({ + url: '/Base/ActivityMain/UpdateActivity', + method: 'post', + data, + }) +} + +/** 更新活动队伍 */ +export function fetchUpdateTeamList(data: Api.Competition.CreateTeamListRequest[]) { + return request>({ + url: '/Base/ActivityMain/UpdateActivity_TeamList', + method: 'post', + data, + }) +} + +/** 更新活动题目 */ +export function fetchUpdateQuestion(data: Api.Competition.QuestionListRecord[]) { + return request>({ + url: '/Base/ActivityMain/AddOrUpdateActivity_Question', + method: 'post', + data, + }) +} + +/** 删除活动 */ +export function fetchDeleteActivity(id: number) { + return request>({ + url: `/Base/ActivityMain/DeleteActivity?ID=${id}`, + method: 'post', + }) +} + +/** 更新活动状态 */ +export function fetchUpdatePublishStatus(id: number, status: number) { + return request>({ + url: `/Base/ActivityMain/UpdateActivityStatus?ActivityID=${id}&status=${status}`, + method: 'post', + }) +} diff --git a/apps/admin/src/service/api/dictionary.ts b/apps/admin/src/service/api/dictionary.ts index 6783c94..cf17ea1 100644 --- a/apps/admin/src/service/api/dictionary.ts +++ b/apps/admin/src/service/api/dictionary.ts @@ -57,3 +57,14 @@ export function deleteDictionaryItem(DicUIID: number) { data: { DicUIID }, }) } + +/** 根据字典value查询字典项 */ +export function getDictionaryItemListByDicID(DicValue: string) { + return request({ + url: `/Base/ActivityMain/GetDictionaryListUITypeByDicValue/?DicValue=${DicValue}`, + method: 'get', + params: { + DicValue, + }, + }) +} diff --git a/apps/admin/src/store/modules/business/index.ts b/apps/admin/src/store/modules/business/index.ts index 14aa7d4..a73d526 100644 --- a/apps/admin/src/store/modules/business/index.ts +++ b/apps/admin/src/store/modules/business/index.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { ref } from 'vue' -import { getDictionaryList, getDictionaryListUITypeByDicID } from '@/service/api/dictionary' +import { getDictionaryItemListByDicID, getDictionaryList } from '@/service/api/dictionary' export const useBusinessStore = defineStore('business-store', () => { const dictList = ref([]) @@ -21,28 +21,17 @@ export const useBusinessStore = defineStore('business-store', () => { /** 获取字典数据 */ async function getDict(key: string) { - if (!isInitialized.value) { - await initDict() - } - // 已经加载过,直接返回 if (dictData.value[key] && dictData.value[key].length > 0) return dictData.value[key] - // 查找 ID - const dict = dictList.value.find(d => d.DicValue === key) - if (!dict) { - console.warn(`Dictionary key ${key} not found`) - return [] - } - if (loadingMap.value[key]) { return [] } loadingMap.value[key] = true try { - const { data: res, error } = await getDictionaryListUITypeByDicID(dict.Id) + const { data: res, error } = await getDictionaryItemListByDicID(key) if (!error && res) { const list = res.data || [] // 使用新对象赋值,确保触发响应式更新 @@ -67,4 +56,4 @@ export const useBusinessStore = defineStore('business-store', () => { initDict, getDict, } -}, { persist: true }) +}) diff --git a/apps/admin/src/theme/settings.ts b/apps/admin/src/theme/settings.ts index 12cc78c..b236990 100644 --- a/apps/admin/src/theme/settings.ts +++ b/apps/admin/src/theme/settings.ts @@ -11,9 +11,13 @@ export const themeSettings: App.Theme.ThemeSetting = { success: '#52c41a', warning: '#faad14', error: '#f5222d', + unpublished: '#6b7280', // gray-500 + published: '#2563eb', // blue-600 + processing: '#16a34a', // green-600 + finished: '#dc2626', // red-600 }, isInfoFollowPrimary: true, // 是否开启信息类颜色跟随主题颜色 - layout: { mode: 'vertical', scrollMode: 'content' }, // 布局模式 mode: horizontal | vertical + layout: { mode: 'horizontal', scrollMode: 'content' }, // 布局模式 mode: horizontal | vertical page: { animate: true, animateMode: 'fade-slide' }, // 页面动画模式 header: { height: 56, // 头部高度 diff --git a/apps/admin/src/theme/vars.ts b/apps/admin/src/theme/vars.ts index b072ec6..9c5591c 100644 --- a/apps/admin/src/theme/vars.ts +++ b/apps/admin/src/theme/vars.ts @@ -1,6 +1,16 @@ /** Create color palette vars */ function createColorPaletteVars() { - const colors: App.Theme.ThemeColorKey[] = ['primary', 'info', 'success', 'warning', 'error'] + const colors: App.Theme.ThemeColorKey[] = [ + 'primary', + 'info', + 'success', + 'warning', + 'error', + 'unpublished', + 'published', + 'processing', + 'finished', + ] const colorPaletteNumbers: App.Theme.ColorPaletteNumber[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] const colorPaletteVar = {} as App.Theme.ThemePaletteColor diff --git a/apps/admin/src/typings/api/Competition.d.ts b/apps/admin/src/typings/api/Competition.d.ts index 1fa2697..e594772 100644 --- a/apps/admin/src/typings/api/Competition.d.ts +++ b/apps/admin/src/typings/api/Competition.d.ts @@ -29,10 +29,10 @@ declare namespace Api { * - "1": enabled * - "2": disabled */ - type EnableStatus = '1' | '2' + type EnableStatus = 1 | 2 /** question ui type */ - type QuestionUiType = '1' | '2' | '3' | '4' /** 文本输入 | 单选 | 多选 | 下拉选择 */ + type QuestionUiType = 1 | 2 | 3 | 4 /** 文本输入 | 单选 | 多选 | 下拉选择 */ /** question time type */ type QuestionTimeType = 5 | 10 | 15 | 20 | 30 | 45 | 60 | 90 /** 5s | 10s | 15s | 20s | 30s | 45s | 60s | 90s */ @@ -47,10 +47,25 @@ declare namespace Api { | 'TEMPLATE_IDIOM_IMAGE' /** 汉字听写-提示 | 汉字听写-同音 | 汉字加一加 | 词语听写 | 成语-文字要求 | 成语-看图 */ /** question score type */ - type QuestionScoreType = '1' | '2' /** 固定分数 | 答题个数 */ + type QuestionScoreType = 0 | 1 /** 固定分数 | 答题个数 */ /** competition round type */ - type CompetitionRoundType = '1' | '2' /** 题包环节 | 加时环节 */ + type CompetitionRoundType = 0 | 1 /** 题包环节 | 加时环节 */ + + /** + * competition publish status + * + * - 0: unpublished + * - 1: published + * - 2: processing + * - 3: finished + */ + enum PublishStatus { + Unpublished = 0, + Published = 1, + Processing = 2, + Finished = 3, + } /** room */ interface Room { @@ -60,6 +75,36 @@ declare namespace Api { Name: string } + /** activity detail */ + interface ActivityDetail { + /** 背景图片 */ + BackgroundImg: string + /** 创建时间 */ + CreatedTime: string + /** 结束时间 */ + EndTime: string + /** 活动 id */ + Id: number + /** 活动名称 */ + Name: string + /** 发布状态 */ + PublishStatus: number + /** 房间 id */ + RoomID: number + /** 开始时间 */ + StartTime: string + /** 队伍分组数量 */ + TeamGroupNumber: number + /** 队伍数量 */ + Teams: number + /** 活动副标题 */ + ActivityTitle?: string + /** 普通赛事规则 */ + ActivityContent?: string + /** 加时赛规则 */ + ExtraTimeContent?: string + } + /** create room request */ interface CreateRoomRequest { /** 活动 id */ @@ -82,6 +127,28 @@ declare namespace Api { backgroundImg: string } + /** update room request */ + interface UpdateRoomRequest { + /** 活动 id */ + id: number + /** 活动名称 */ + name: string + /** 开始时间 */ + startTime: string + /** 结束时间 */ + endTime: string + /** 队伍数量 */ + teams: number + /** 队伍分组数量 */ + teamGroupNumber: number + /** 活动副标题 */ + activityTitle?: string + /** 普通赛事规则 */ + activityContent?: string + /** 加时赛规则 */ + extraTimeContent?: string + } + /** create team list request */ interface CreateTeamListRequest { /** 队伍 id */ @@ -100,6 +167,56 @@ declare namespace Api { schoolName: string /** 队伍分组 id */ teamGroupId: number + /** 队伍头像 */ + headImg?: string + } + + interface TeamListRecord { + /** 队伍 id */ + Id: number + /** 主 id */ + MainId: number + /** 队伍编号 */ + Number: string + /** 队伍名称 */ + Name: string + /** 笔序列号 */ + PenSerial: string + /** 队员名单 */ + NameList: string + /** 学校名称 */ + SchoolName: string + /** 队伍分组 id */ + TeamGroupId: number + /** 队伍头像 */ + HeadImg?: string + } + + interface QuestionListRecord { + /** 活动题目名称 */ + ActitvityQuestionName: string + /** 活动 id */ + ActivityID: number + /** 主键 id */ + ID: number + /** 分值 */ + Point: number + /** 题目 id */ + QuestionID: number + /** 题目序号 */ + QuestionIndex: number + /** 题目规则 */ + QuestionRule: number + /** 题目副标题 */ + QuestionSubTitle: string + /** 答题时间(秒) */ + QuestionTime: number + /** 赛题包类型 */ + RoundType: number + /** 题目模板 id */ + TemplateID: number + /** UI 类型 */ + UIType: QuestionTemplateId } interface CreateQuestionRequest { @@ -124,7 +241,7 @@ declare namespace Api { /** 题目副标题 */ questionSubTitle: string /** 题目模板 id */ - templateID: QuestionTemplateId + templateID: number /** 赛题包类型 */ roundType: number } diff --git a/apps/admin/src/typings/app.d.ts b/apps/admin/src/typings/app.d.ts index 6915cf4..f01af63 100644 --- a/apps/admin/src/typings/app.d.ts +++ b/apps/admin/src/typings/app.d.ts @@ -140,6 +140,10 @@ declare namespace App { success: string warning: string error: string + unpublished: string + published: string + processing: string + finished: string } interface ThemeColor extends OtherColor { diff --git a/apps/admin/src/typings/components.d.ts b/apps/admin/src/typings/components.d.ts index 4868069..706a3c1 100644 --- a/apps/admin/src/typings/components.d.ts +++ b/apps/admin/src/typings/components.d.ts @@ -24,6 +24,8 @@ declare module 'vue' { IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default'] IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default'] IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default'] + 'IconIc:baselineDeleteForever': typeof import('~icons/ic/baseline-delete-forever')['default'] + 'IconIc:roundPublish': typeof import('~icons/ic/round-publish')['default'] IconIcBaselineAdd: typeof import('~icons/ic/baseline-add')['default'] IconIcBaselineAddPlus: typeof import('~icons/ic/baseline-add-plus')['default'] IconIcBaselineArrowBack: typeof import('~icons/ic/baseline-arrow-back')['default'] @@ -185,6 +187,8 @@ declare global { const IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default'] const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default'] const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default'] + const 'IconIc:baselineDeleteForever': typeof import('~icons/ic/baseline-delete-forever')['default'] + const 'IconIc:roundPublish': typeof import('~icons/ic/round-publish')['default'] const IconIcBaselineAdd: typeof import('~icons/ic/baseline-add')['default'] const IconIcBaselineAddPlus: typeof import('~icons/ic/baseline-add-plus')['default'] const IconIcBaselineArrowBack: typeof import('~icons/ic/baseline-arrow-back')['default'] diff --git a/apps/admin/src/typings/wangeditor.d.ts b/apps/admin/src/typings/wangeditor.d.ts new file mode 100644 index 0000000..cddd376 --- /dev/null +++ b/apps/admin/src/typings/wangeditor.d.ts @@ -0,0 +1,8 @@ +declare module '@wangeditor/editor-for-vue' { + import type { DefineComponent } from 'vue' + + const Editor: DefineComponent, Record, any> + const Toolbar: DefineComponent, Record, any> + + export { Editor, Toolbar } +} diff --git a/apps/admin/src/views/competition/competition-add/index.vue b/apps/admin/src/views/competition/competition-add/index.vue index 0288100..9388827 100644 --- a/apps/admin/src/views/competition/competition-add/index.vue +++ b/apps/admin/src/views/competition/competition-add/index.vue @@ -2,7 +2,7 @@ import { NButton, NModal, NStep, NSteps } from 'naive-ui' import { computed, onMounted, ref, useTemplateRef, watch } from 'vue' import { useDict } from '@/hooks/business/useDict' -import { fetchCreateActivity, fetchCreateQuestion, fetchCreateTeamList, fetchGetRoomList } from '@/service/api/competition' +import { fetchCreateCompetition, fetchGetRoomList } from '@/service/api/competition' /** 房间相关接口 */ import BasicInfo from './modules/BasicInfo.vue' @@ -41,17 +41,20 @@ const competitionData = ref({ teamCount: 4, // 每组队伍数量 poster: '', // 竞赛海报 roomId: null, // 关联场地AP + subTitle: '星·辞海遨游', // 活动口号或副标题 + extraTimeContent: '每道题额外时间10秒', // 加时赛的规则 + activityContent: '

本轮环节共有4道题目,分别是“诗词理解”、“联想对对碰”、“逆向接诗句”、“情景猜诗句

阅读完题目介绍,主持人点击“开始作答”后均在答题本田字格上作答


诗词理解” 根据题目选择正确答案

联想对对碰” 根据关键信息写出完整诗句

逆向接诗句” 根据关键信息写出关联诗句

情景猜诗句” 根据情景写出关联诗句


答题倒计时结束后,界面自动跳转到下一题

', rounds: [ // 赛题包配置 { id: 'round_default', name: '星·辞海遨游', - roundType: roundTypeOptions.value?.[0]?.value, + roundType: roundTypeOptions.value?.[0]?.value || 0, questions: Array.from({ length: 10 }).map(() => ({ value: null, score: 5, time: 30, title: '第1题', - scoreType: scoreTypeOptions.value?.[0]?.value, + scoreType: scoreTypeOptions.value?.[0]?.value || 0, })), }, ], @@ -93,7 +96,7 @@ async function nextStep() { } async function submitForm() { - const { name, startTime, endTime, groupCount, teamCount, poster, rounds, groupManagement, roomId } = competitionData.value + const { name, startTime, endTime, groupCount, teamCount, poster, rounds, groupManagement, roomId, subTitle, extraTimeContent, activityContent } = competitionData.value if (!startTime || !endTime) { window.$message?.error('请完善比赛时间') @@ -107,10 +110,14 @@ async function submitForm() { startTime, endTime, teams: groupCount * teamCount, - teamGroupNumber: groupCount, + teamGroupNumber: teamCount, createdTime: startTime, roomID: roomId || 0, backgroundImg: poster, + publishStatus: 0, + ActivityTitle: subTitle, + ActivityContent: activityContent, + ExtraTimeContent: extraTimeContent, } const teamParams: Api.Competition.CreateTeamListRequest[] = [] @@ -123,11 +130,11 @@ async function submitForm() { id: 0, mainId: 0, number: `${group.id}-${index + 1}`, - name, + name: team.name, penSerial: `${group.id}-${index + 1}`, - nameList: 'to do?', - schoolName: name, - teamGroupId: group.id && Number(group.id), + nameList: `${index + 1}-${team.name}`, + schoolName: team.name, + teamGroupId: 0, }) }) }) @@ -141,27 +148,38 @@ async function submitForm() { questionParams.push({ id: 0, activityID: 0, - questionID: question.questionId, - questionIndex: index + 1, - actitvityQuestionName: question.title, - questionTime: question.time, - questionRule: question.scoreType, - uiType: question.uiType, - point: question.score, - templateID: question.templateId, + questionID: question.QuestionID, + questionIndex: index, + actitvityQuestionName: question.ActitvityQuestionName, + questionTime: question.QuestionTime, + questionRule: question.QuestionRule, + uiType: question.UIType, + point: question.Point, + templateID: question.TemplateID, roundType: round.roundType, - questionSubTitle: question.title, + questionSubTitle: question.ActitvityQuestionName, }) }) }) - // 使用promise.all 提交数据 - const [competitionRes, teamRes, questionRes] = await Promise.all([ - fetchCreateActivity(roomParams), - fetchCreateTeamList(teamParams), - fetchCreateQuestion(questionParams), - ]) - if (competitionRes && teamRes && questionRes) { + // 根据 questionID 降序排序 (例如: 54455 -> 55544) + questionParams.sort((a, b) => Number(b.questionID) - Number(a.questionID)) + + // 重新计算 questionIndex + questionParams.forEach((item, index) => { + item.questionIndex = index + }) + + // eslint-disable-next-line no-console + console.log(questionParams, 'questionParams') + + const { error } = await fetchCreateCompetition({ + actMain: roomParams, + teams: teamParams, + questions: questionParams, + }) + + if (!error) { window.$message?.success('比赛发布成功') emit('success') emit('update:show', false) @@ -216,7 +234,7 @@ watch(() => competitionData.value, (val) => {