From 1744adcf927e17cf82a23d9d1302bf9bbc604fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=85=E6=88=BF?= Date: Fri, 26 Dec 2025 11:06:38 +0800 Subject: [PATCH] fix bugs --- src/api/martial/attachment.js | 136 +++++++ src/views/martial/competition/create.vue | 4 +- src/views/martial/competition/index.vue | 470 ++++++++++++++++++++++- src/views/martial/competition/list.vue | 4 +- 4 files changed, 605 insertions(+), 9 deletions(-) create mode 100644 src/api/martial/attachment.js diff --git a/src/api/martial/attachment.js b/src/api/martial/attachment.js new file mode 100644 index 0000000..32bef24 --- /dev/null +++ b/src/api/martial/attachment.js @@ -0,0 +1,136 @@ +import request from '@/axios'; + +// ==================== 赛事附件管理接口 ==================== + +/** + * 获取附件详情 + * @param {Number} id - 附件ID + */ +export const getAttachmentDetail = (id) => { + return request({ + url: '/api/martial/competition/attachment/detail', + method: 'get', + params: { id } + }) +} + +/** + * 附件列表查询(分页) + * @param {Number} current - 当前页 + * @param {Number} size - 每页条数 + * @param {Object} params - 查询参数 + */ +export const getAttachmentList = (current, size, params) => { + return request({ + url: '/api/martial/competition/attachment/list', + method: 'get', + params: { + current, + size, + ...params + } + }) +} + +/** + * 根据赛事ID和类型获取附件列表 + * @param {Number} competitionId - 赛事ID + * @param {String} attachmentType - 附件类型:info-赛事发布, rules-赛事规程, schedule-活动日程, results-成绩, medals-奖牌榜, photos-图片直播 + */ +export const getAttachmentsByType = (competitionId, attachmentType) => { + return request({ + url: '/api/martial/competition/attachment/getByType', + method: 'get', + params: { competitionId, attachmentType } + }) +} + +/** + * 根据赛事ID获取所有附件 + * @param {Number} competitionId - 赛事ID + */ +export const getAttachmentsByCompetition = (competitionId) => { + return request({ + url: '/api/martial/competition/attachment/getByCompetition', + method: 'get', + params: { competitionId } + }) +} + +/** + * 新增或修改附件 + * @param {Object} data - 附件数据 + * @param {Number} data.id - ID(修改时必传) + * @param {Number} data.competitionId - 赛事ID + * @param {String} data.attachmentType - 附件类型 + * @param {String} data.fileName - 文件名称 + * @param {String} data.fileUrl - 文件URL + * @param {Number} data.fileSize - 文件大小(字节) + * @param {String} data.fileType - 文件类型(扩展名) + * @param {Number} data.orderNum - 排序序号 + * @param {Number} data.status - 状态(1-启用 0-禁用) + */ +export const submitAttachment = (data) => { + return request({ + url: '/api/martial/competition/attachment/submit', + method: 'post', + data + }) +} + +/** + * 批量保存附件 + * @param {Array} attachments - 附件列表 + */ +export const batchSubmitAttachments = (attachments) => { + return request({ + url: '/api/martial/competition/attachment/batchSubmit', + method: 'post', + data: attachments + }) +} + +/** + * 删除附件 + * @param {String} ids - 附件ID,多个用逗号分隔 + */ +export const removeAttachment = (ids) => { + return request({ + url: '/api/martial/competition/attachment/remove', + method: 'post', + params: { ids } + }) +} + +/** + * 删除赛事的指定类型附件 + * @param {Number} competitionId - 赛事ID + * @param {String} attachmentType - 附件类型 + */ +export const removeAttachmentByType = (competitionId, attachmentType) => { + return request({ + url: '/api/martial/competition/attachment/removeByType', + method: 'post', + params: { competitionId, attachmentType } + }) +} + +// 附件类型常量 +export const ATTACHMENT_TYPES = { + INFO: 'info', // 赛事发布 + RULES: 'rules', // 赛事规程 + SCHEDULE: 'schedule', // 活动日程 + RESULTS: 'results', // 成绩 + MEDALS: 'medals', // 奖牌榜 + PHOTOS: 'photos' // 图片直播 +} + +// 附件类型标签映射 +export const ATTACHMENT_TYPE_LABELS = { + info: '赛事发布', + rules: '赛事规程', + schedule: '活动日程', + results: '成绩', + medals: '奖牌榜', + photos: '图片直播' +} diff --git a/src/views/martial/competition/create.vue b/src/views/martial/competition/create.vue index f2556ad..3ce22d3 100644 --- a/src/views/martial/competition/create.vue +++ b/src/views/martial/competition/create.vue @@ -188,7 +188,7 @@ -
+
diff --git a/src/views/martial/competition/index.vue b/src/views/martial/competition/index.vue index c56a079..fbc556a 100644 --- a/src/views/martial/competition/index.vue +++ b/src/views/martial/competition/index.vue @@ -370,8 +370,134 @@
- +
+
+ + 附件管理 +
+ + + + +
+ +
+ + 上传{{ tab.label }}附件 + + 支持 PDF、Word、Excel、图片等格式,单个文件不超过 50MB +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

暂无{{ tab.label }}附件

+
+
+
+
+
+ + +
@@ -781,6 +907,22 @@
+ + + + +
@@ -806,6 +948,14 @@ import { removeVenue, getVenuesByCompetition } from '@/api/martial/venue' +import { + getAttachmentsByCompetition, + submitAttachment, + removeAttachment, + batchSubmitAttachments, + ATTACHMENT_TYPES, + ATTACHMENT_TYPE_LABELS +} from '@/api/martial/attachment' export default { name: 'CompetitionManagement', @@ -820,6 +970,39 @@ export default { size: 10, total: 0 }, + // 附件相关数据 + activeAttachmentTab: 'info', + attachmentTabs: [ + { type: 'info', label: '赛事发布' }, + { type: 'rules', label: '赛事规程' }, + { type: 'schedule', label: '活动日程' }, + { type: 'results', label: '成绩' }, + { type: 'medals', label: '奖牌榜' }, + { type: 'photos', label: '图片直播' } + ], + attachmentUploadDialogVisible: false, + currentAttachmentType: '', + attachmentUploadForm: {}, + attachmentUploadOption: { + submitBtn: false, + emptyBtn: false, + column: [ + { + label: '附件上传', + prop: 'attachmentFile', + type: 'upload', + drag: true, + loadText: '文件上传中,请稍等', + span: 24, + accept: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.jpg,.jpeg,.png,.gif,.zip,.rar', + tip: '支持 PDF、Word、Excel、PPT、图片、压缩包等格式,单个文件不超过 50MB', + propsHttp: { + res: 'data', + }, + action: '/blade-resource/oss/endpoint/put-file' + } + ] + }, formData: { competitionName: '', competitionCode: '', // 比赛编码 @@ -840,7 +1023,15 @@ export default { awards: '', schedule: [], projects: [], - venues: [] + venues: [], + attachments: { + info: [], + rules: [], + schedule: [], + results: [], + medals: [], + photos: [] + } }, formRules: { competitionName: [ @@ -988,10 +1179,11 @@ export default { try { this.formData = this.formatBackendData(detailData); console.log('格式化后的表单数据:', this.formData); - // 加载关联数据:活动日程、项目列表、场地配置 + // 加载关联数据:活动日程、项目列表、场地配置、附件 this.loadActivitySchedules(); this.loadProjects(); this.loadVenues(); + this.loadAttachments(); } catch (error) { console.error('格式化数据时出错:', error); this.$message.error('数据格式化失败: ' + error.message); @@ -1154,6 +1346,212 @@ export default { }); }, + // 加载附件列表 + loadAttachments() { + if (!this.competitionId) { + console.warn('loadAttachments: competitionId 为空,跳过加载'); + return; + } + + console.log('开始加载附件列表,competitionId:', this.competitionId); + getAttachmentsByCompetition(this.competitionId) + .then(res => { + console.log('附件列表返回数据:', res); + const responseData = res.data?.data; + + // 初始化附件对象 + const attachments = { + info: [], + rules: [], + schedule: [], + results: [], + medals: [], + photos: [] + }; + + if (responseData && Array.isArray(responseData)) { + // 按类型分组 + responseData.forEach(item => { + const type = item.attachmentType; + if (attachments[type]) { + attachments[type].push({ + id: item.id, + fileName: item.fileName, + fileUrl: item.fileUrl, + fileSize: item.fileSize, + fileType: item.fileType, + orderNum: item.orderNum || 0, + createTime: item.createTime + }); + } + }); + console.log('✅ 加载的附件列表:', attachments); + } else { + console.log('⚠️ 附件列表为空'); + } + + this.formData.attachments = attachments; + }) + .catch(err => { + console.error('❌ 加载附件列表失败:', err); + // 不显示错误消息,因为可能是新建赛事还没有附件 + }); + }, + + // 打开附件上传对话框 + handleOpenAttachmentUpload(type) { + this.currentAttachmentType = type; + this.attachmentUploadForm = {}; + this.attachmentUploadDialogVisible = true; + }, + + // 附件上传成功回调 + attachmentUploadAfter(res, done, loading, column) { + console.log('附件上传响应:', res); + + if (res && (res.link || res.url)) { + const fileUrl = res.link || res.url; + const fileName = res.originalName || res.name || this.getFileNameFromUrl(fileUrl); + const fileType = this.getFileExtension(fileName); + const fileSize = res.size || 0; + + // 添加到对应类型的附件列表 + if (!this.formData.attachments[this.currentAttachmentType]) { + this.formData.attachments[this.currentAttachmentType] = []; + } + + this.formData.attachments[this.currentAttachmentType].push({ + fileName: fileName, + fileUrl: fileUrl, + fileSize: fileSize, + fileType: fileType, + orderNum: this.formData.attachments[this.currentAttachmentType].length, + isNew: true // 标记为新上传的附件 + }); + + this.$message.success('附件上传成功'); + this.attachmentUploadDialogVisible = false; + } else { + this.$message.error('上传失败,未获取到文件地址'); + } + done(); + }, + + // 预览附件 + handlePreviewAttachment(attachment) { + if (!attachment.fileUrl) { + this.$message.warning('文件地址不存在'); + return; + } + window.open(attachment.fileUrl, '_blank'); + }, + + // 删除附件 + handleDeleteAttachment(type, index) { + this.$confirm('确定要删除该附件吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + const attachment = this.formData.attachments[type][index]; + + // 如果是已保存的附件,需要调用后端删除 + if (attachment.id) { + removeAttachment(attachment.id.toString()) + .then(() => { + this.formData.attachments[type].splice(index, 1); + this.$message.success('删除成功'); + }) + .catch(err => { + console.error('删除附件失败:', err); + this.$message.error('删除失败'); + }); + } else { + // 新上传的附件直接从列表中移除 + this.formData.attachments[type].splice(index, 1); + this.$message.success('删除成功'); + } + }).catch(() => {}); + }, + + // 获取文件图标 + getFileIcon(fileType) { + const iconMap = { + 'pdf': 'el-icon-document', + 'doc': 'el-icon-document', + 'docx': 'el-icon-document', + 'xls': 'el-icon-document', + 'xlsx': 'el-icon-document', + 'ppt': 'el-icon-document', + 'pptx': 'el-icon-document', + 'jpg': 'el-icon-picture', + 'jpeg': 'el-icon-picture', + 'png': 'el-icon-picture', + 'gif': 'el-icon-picture', + 'zip': 'el-icon-folder', + 'rar': 'el-icon-folder' + }; + return iconMap[fileType?.toLowerCase()] || 'el-icon-document'; + }, + + // 格式化文件大小 + formatFileSize(bytes) { + if (!bytes || bytes === 0) return '0 B'; + if (typeof bytes === 'string') return bytes; + + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toFixed(1) + ' ' + sizes[i]; + }, + + // 从URL获取文件名 + getFileNameFromUrl(url) { + if (!url) return 'unknown'; + const parts = url.split('/'); + return parts[parts.length - 1] || 'unknown'; + }, + + // 获取文件扩展名 + getFileExtension(fileName) { + if (!fileName) return ''; + const parts = fileName.split('.'); + return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : ''; + }, + + // 保存附件 + async saveAttachments(competitionId) { + const allAttachments = []; + + // 收集所有需要保存的附件 + for (const type of Object.keys(this.formData.attachments)) { + const attachments = this.formData.attachments[type] || []; + for (const attachment of attachments) { + // 只保存新上传的附件或已修改的附件 + if (attachment.isNew || attachment.isModified) { + allAttachments.push({ + id: attachment.id || null, + competitionId: competitionId, + attachmentType: type, + fileName: attachment.fileName, + fileUrl: attachment.fileUrl, + fileSize: attachment.fileSize, + fileType: attachment.fileType, + orderNum: attachment.orderNum || 0, + status: 1 + }); + } + } + } + + if (allAttachments.length === 0) { + return Promise.resolve(); + } + + console.log('准备保存的附件:', allAttachments); + return batchSubmitAttachments(allAttachments); + }, + getStatusText(status) { const statusMap = { 1: '未开始', @@ -1311,6 +1709,9 @@ export default { savePromises.push(this.saveVenues(savedCompetitionId)); } + // 4. 保存附件 + savePromises.push(this.saveAttachments(savedCompetitionId)); + // 等待所有保存操作完成 if (savePromises.length > 0) { Promise.all(savePromises) @@ -1498,7 +1899,15 @@ export default { awards: '', schedule: [], projects: [], - venues: [] + venues: [], + attachments: { + info: [], + rules: [], + schedule: [], + results: [], + medals: [], + photos: [] + } }; }, @@ -1789,4 +2198,55 @@ export default { width: 100%; } } + +// 附件管理样式 +.attachment-section { + padding: 10px 0; +} + +.attachment-upload { + display: flex; + align-items: center; + gap: 15px; + + .upload-tip { + font-size: 12px; + color: #909399; + } +} + +.file-name-cell { + display: flex; + align-items: center; + gap: 8px; + + .file-icon { + font-size: 18px; + color: #409eff; + } +} + +.empty-attachment { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 0; + color: #909399; + + i { + font-size: 48px; + margin-bottom: 10px; + opacity: 0.5; + } + + p { + margin: 0; + font-size: 14px; + } +} + +:deep(.el-tabs__content) { + padding: 10px 0; +} diff --git a/src/views/martial/competition/list.vue b/src/views/martial/competition/list.vue index 1bfc1bb..ff233e3 100644 --- a/src/views/martial/competition/list.vue +++ b/src/views/martial/competition/list.vue @@ -284,7 +284,7 @@ -
+