// pages/registration/registration.js const app = getApp() const { graphqlRequest } = require('../../lib/utils') const { uploadFile, chooseAndUploadImage } = require('../../lib/upload') const cosUtil = require('../../lib/cosUtil') Page({ data: { activityId: '', activity: null, loading: false, // 表单数据 formData: { name: '', phone: '', gender: null, birthDate: '', education: '', regionId: null, projectName: '', description: '', introduction: '', avatarUrl: '', avatarMediaId: '' }, // 表单验证 errors: {}, // 选择器数据 genderOptions: ['男', '女'], genderIndex: -1, educationOptions: ['高中', '大专', '本科', '硕士', '博士'], educationIndex: -1, regions: [], regionIndex: -1, today: new Date().toISOString().split('T')[0], // 今天的日期,用于限制生日选择 isSubmitting: false, // 头像上传状态 avatarUploading: false, avatarUploadProgress: 0, // 本地头像临时文件 localAvatarPath: '', // 本地选择的头像文件路径,提交时才上传 // 附件上传 attachments: [], // 附件列表 maxAttachments: 8, // 最大附件数量 // 文件类型配置 fileTypeConfig: { image: { extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'], maxSize: 10 * 1024 * 1024, // 10MB icon: '🖼️' }, video: { extensions: ['mp4', 'avi', 'mov', 'wmv', 'flv', '3gp'], maxSize: 200 * 1024 * 1024, // 200MB icon: '🎬' }, pdf: { extensions: ['pdf'], maxSize: 50 * 1024 * 1024, // 50MB icon: '📄' }, word: { extensions: ['doc', 'docx'], maxSize: 50 * 1024 * 1024, // 50MB icon: '📝' }, excel: { extensions: ['xls', 'xlsx'], maxSize: 50 * 1024 * 1024, // 50MB icon: '📊' }, ppt: { extensions: ['ppt', 'pptx'], maxSize: 50 * 1024 * 1024, // 50MB icon: '📽️' }, text: { extensions: ['txt'], maxSize: 10 * 1024 * 1024, // 10MB icon: '📄' } } }, onLoad(options) { console.log('📥 registration页面接收到的参数:', options) if (options.activityId || options.id) { const activityId = options.activityId || options.id console.log('🎯 设置activityId:', activityId) this.setData({ activityId: activityId }) this.loadActivityInfo() } else { console.log('❌ 没有接收到activityId参数') } // 从全局数据获取用户信息预填充 this.prefillUserInfo() // 加载区域数据 this.loadRegions() }, // 加载活动信息 async loadActivityInfo() { try { this.setData({ loading: true }) const query = ` query GetActivity($id: ID!) { activity(id: $id) { id name description signupDeadline matchTime address playerMax state } } ` console.log('🔍 查询活动信息,activityId:', this.data.activityId) const result = await graphqlRequest(query, { id: this.data.activityId }) console.log('📊 GraphQL查询结果:', result) if (result && result.activity) { console.log('✅ 活动信息加载成功:', result.activity) this.setData({ activity: result.activity }) // 根据活动要求初始化表单 this.initFormByRequirements() } else { // 活动不存在或查询失败 wx.showModal({ title: '提示', content: '活动信息不存在或已失效', showCancel: false, success: () => { wx.navigateBack() } }) } } catch (error) { console.error('加载活动信息失败:', error) wx.showModal({ title: '网络错误', content: '加载活动信息失败,请检查网络连接', confirmText: '重试', cancelText: '返回', success: (res) => { if (res.confirm) { this.loadActivityInfo() } else { wx.navigateBack() } } }) } finally { this.setData({ loading: false }) } }, // 加载区域数据 async loadRegions() { try { console.log('🔄 开始加载区域数据...') const query = ` query { allRegions { id name pid leafFlag } } ` const result = await graphqlRequest(query) console.log('📊 区域数据查询结果:', result) if (result && result.allRegions) { console.log(`📍 总区域数量: ${result.allRegions.length}`) // 只显示leaf_flag=true的区域(叶子节点区域) const leafRegions = result.allRegions.filter(region => region.leafFlag === true) console.log(`🍃 叶子节点区域数量: ${leafRegions.length}`) console.log('🍃 叶子节点区域列表:', leafRegions.map(r => `${r.name}(${r.id})`)) this.setData({ regions: leafRegions }) console.log('✅ 区域数据设置完成') } else { console.warn('⚠️ 未获取到区域数据') } } catch (error) { console.error('❌ 加载区域数据失败:', error) // 区域数据加载失败不影响主要功能,只记录错误 } }, // 预填充用户信息 prefillUserInfo() { const userInfo = app.globalData.userInfo if (userInfo) { this.setData({ 'formData.name': userInfo.name || '', 'formData.phone': userInfo.phone || '', 'formData.email': userInfo.email || '' }) } }, // 根据活动要求初始化表单 initFormByRequirements() { const { activity } = this.data if (!activity) return // 基本表单验证规则初始化 const errors = {} this.setData({ errors }) }, // 表单输入处理 onInputChange(e) { const { field } = e.currentTarget.dataset const { value } = e.detail this.setData({ [`formData.${field}`]: value }) // 清除该字段的错误状态 if (this.data.errors[field]) { this.setData({ [`errors.${field}`]: false }) } // 实时验证 this.validateField(field, value) }, // 性别选择 onGenderChange(e) { const index = e.detail.value this.setData({ genderIndex: index, 'formData.gender': index }) }, // 学历选择 onEducationChange(e) { const index = e.detail.value this.setData({ educationIndex: index, 'formData.education': this.data.educationOptions[index] }) }, // 区域选择 onRegionChange(e) { const index = e.detail.value const region = this.data.regions[index] this.setData({ regionIndex: index, 'formData.regionId': region ? region.id : null }) }, // 生日选择 onBirthDateChange(e) { const birthDate = e.detail.value this.setData({ 'formData.birthDate': birthDate }) // 清除该字段的错误状态 if (this.data.errors.birthDate) { this.setData({ 'errors.birthDate': false }) } }, // 获取手机号授权 async onGetPhoneNumber(e) { console.log('获取手机号授权:', e.detail) if (e.detail.errMsg === 'getPhoneNumber:ok') { try { console.log('=== 手机号获取调试信息 ===') console.log('微信返回的code:', e.detail.code) console.log('code长度:', e.detail.code ? e.detail.code.length : 0) let phoneNumber = null // 使用新版API直接通过code获取手机号 if (e.detail.code) { console.log('🚀 使用新版API获取手机号') console.log('code:', e.detail.code) // 调用新版API直接通过code获取手机号 const response = await graphqlRequest(` mutation GetPhoneNumberByCode($code: String!) { getPhoneNumberByCode(code: $code) { phoneNumber purePhoneNumber countryCode } } `, { code: e.detail.code }) if (response.getPhoneNumberByCode) { phoneNumber = response.getPhoneNumberByCode.phoneNumber console.log('✅ 新版API获取手机号成功:', phoneNumber) } else { throw new Error('获取手机号失败,请重试') } } else { throw new Error('缺少获取手机号所需的code参数') } if (phoneNumber) { this.setData({ 'formData.phone': phoneNumber }) // 清除手机号错误状态 if (this.data.errors.phone) { this.setData({ 'errors.phone': false }) } console.log('✅ 手机号获取成功:', phoneNumber) wx.showToast({ title: '手机号获取成功', icon: 'success' }) } else { throw new Error('获取手机号失败') } } catch (error) { console.error('获取手机号失败:', error) wx.showToast({ title: '获取手机号失败', icon: 'error' }) } } else { console.log('用户拒绝授权手机号') wx.showToast({ title: '需要手机号授权', icon: 'none' }) } }, // 清除手机号 onClearPhone() { this.setData({ 'formData.phone': '' }) }, // 选择头像(只做本地预览,不立即上传) async onChooseAvatar() { try { const res = await wx.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['album', 'camera'], sizeType: ['compressed'] }) if (res.tempFiles && res.tempFiles.length > 0) { const tempFile = res.tempFiles[0] // 检查文件类型 - 使用文件扩展名判断,因为iOS下fileType属性可能不存在 const getFileExtension = (filePath) => { return filePath.split('.').pop().toLowerCase() } const fileExtension = getFileExtension(tempFile.tempFilePath) const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'] if (!allowedExtensions.includes(fileExtension)) { wx.showToast({ title: '请选择JPG、PNG或WebP格式的图片', icon: 'none', duration: 3000 }) return } // 检查文件大小(限制为5MB) if (tempFile.size > 5 * 1024 * 1024) { wx.showToast({ title: '图片大小不能超过5MB', icon: 'none', duration: 3000 }) return } // 检查图片尺寸 try { const imageInfo = await wx.getImageInfo({ src: tempFile.tempFilePath }) // 建议最小尺寸为200x200 if (imageInfo.width < 200 || imageInfo.height < 200) { const result = await wx.showModal({ title: '图片尺寸提示', content: '建议上传尺寸不小于200x200的图片,当前图片尺寸为' + imageInfo.width + 'x' + imageInfo.height + ',是否继续?', confirmText: '继续', cancelText: '重新选择' }) if (!result.confirm) { return } } } catch (imageError) { console.warn('获取图片信息失败:', imageError) } // 只保存本地路径,用于预览,不立即上传 this.setData({ localAvatarPath: tempFile.tempFilePath }) // 清除错误状态 if (this.data.errors.avatar) { this.setData({ 'errors.avatar': false }) } wx.showToast({ title: '头像选择成功,提交时将自动上传', icon: 'success', duration: 2000 }) } } catch (error) { console.error('头像选择失败:', error) wx.showToast({ title: '头像选择失败,请重试', icon: 'none', duration: 3000 }) } }, // 删除头像 async onDeleteAvatar() { try { const hasLocalAvatar = !!this.data.localAvatarPath const hasUploadedAvatar = !!this.data.formData.avatarUrl const result = await wx.showModal({ title: '删除头像', content: hasLocalAvatar ? '确定要删除当前选择的头像吗?' : '确定要删除当前头像吗?删除后需要重新选择。', confirmText: '删除', confirmColor: '#ff4757', cancelText: '取消' }) if (result.confirm) { // 如果是本地头像,直接清空 if (hasLocalAvatar) { this.setData({ localAvatarPath: '' }) wx.showToast({ title: '头像已删除', icon: 'success' }) return } // 如果是已上传的头像,需要删除服务器文件 if (hasUploadedAvatar) { // 显示删除进度 wx.showLoading({ title: '正在删除...', mask: true }) // 如果有COS文件,尝试删除 if (this.data.formData.avatarMediaId) { try { await cosUtil.deleteFile(this.data.formData.avatarMediaId) } catch (deleteError) { console.warn('删除COS文件失败:', deleteError) // 即使删除COS文件失败,也继续清空本地数据 } } // 清空头像数据 this.setData({ 'formData.avatarUrl': '', 'formData.avatarMediaId': '' }) wx.hideLoading() wx.showToast({ title: '头像已删除', icon: 'success' }) } } } catch (error) { console.error('删除头像失败:', error) wx.hideLoading() wx.showToast({ title: '删除失败,请重试', icon: 'none', duration: 2000 }) } }, // 选择附件 async onChooseAttachment() { try { if (this.data.attachments.length >= this.data.maxAttachments) { wx.showToast({ title: `最多只能上传${this.data.maxAttachments}个附件`, icon: 'none' }) return } const result = await wx.chooseMessageFile({ count: this.data.maxAttachments - this.data.attachments.length, type: 'all' }) if (result.tempFiles && result.tempFiles.length > 0) { for (const file of result.tempFiles) { await this.uploadAttachment(file) } } } catch (error) { console.error('选择附件失败:', error) if (error.errMsg && !error.errMsg.includes('cancel')) { wx.showToast({ title: '选择文件失败', icon: 'none' }) } } }, // 添加附件(不立即上传) async uploadAttachment(file) { try { // 验证文件 const validation = this.validateAttachment(file) if (!validation.valid) { wx.showToast({ title: validation.message, icon: 'none', duration: 3000 }) return } // 创建附件对象(只保存本地信息,不上传) const attachment = { id: Date.now() + Math.random(), name: file.name, size: file.size, sizeText: this.formatFileSize(file.size), type: validation.type, path: file.path, uploading: false, uploaded: false, progress: 0, error: null, url: '', mediaId: '' } // 添加到附件列表 const attachments = [...this.data.attachments, attachment] this.setData({ attachments }) wx.showToast({ title: '文件已添加', icon: 'success' }) } catch (error) { console.error('处理附件失败:', error) wx.showToast({ title: '处理失败', icon: 'error' }) } }, // 上传所有附件 async uploadAllAttachments() { const attachments = this.data.attachments.filter(attachment => !attachment.uploaded) if (attachments.length === 0) { return // 没有需要上传的附件 } wx.showLoading({ title: '正在上传附件...', mask: true }) try { for (let i = 0; i < attachments.length; i++) { const attachment = attachments[i] // 更新上传状态 const updatedAttachments = this.data.attachments.map(item => { if (item.id === attachment.id) { return { ...item, uploading: true, progress: 0 } } return item }) this.setData({ attachments: updatedAttachments }) try { const uploadResult = await cosUtil.uploadFile(attachment.path, { onProgress: (progress) => { // 更新上传进度 const progressAttachments = this.data.attachments.map(item => { if (item.id === attachment.id) { return { ...item, progress: Math.round(progress.percent) } } return item }) this.setData({ attachments: progressAttachments }) } }) // 上传成功 const successAttachments = this.data.attachments.map(item => { if (item.id === attachment.id) { return { ...item, uploading: false, uploaded: true, progress: 100, url: uploadResult.url, mediaId: uploadResult.key, error: null } } return item }) this.setData({ attachments: successAttachments }) } catch (uploadError) { console.error('上传附件失败:', uploadError) // 更新错误状态 const errorAttachments = this.data.attachments.map(item => { if (item.id === attachment.id) { return { ...item, uploading: false, uploaded: false, error: '上传失败' } } return item }) this.setData({ attachments: errorAttachments }) throw new Error(`附件 ${attachment.name} 上传失败`) } } } finally { wx.hideLoading() } }, // 删除附件 async onDeleteAttachment(e) { try { const index = e.currentTarget.dataset.index const attachment = this.data.attachments[index] const result = await wx.showModal({ title: '删除附件', content: `确定要删除"${attachment.name}"吗?`, confirmText: '删除', confirmColor: '#ff4757', cancelText: '取消' }) if (result.confirm) { wx.showLoading({ title: '正在删除...', mask: true }) // 如果有COS文件,尝试删除 if (attachment.mediaId) { try { await cosUtil.deleteFile(attachment.mediaId) } catch (deleteError) { console.warn('删除COS文件失败:', deleteError) } } // 从列表中移除 const attachments = this.data.attachments.filter((_, i) => i !== index) this.setData({ attachments }) wx.hideLoading() wx.showToast({ title: '删除成功', icon: 'success' }) } } catch (error) { console.error('删除附件失败:', error) wx.hideLoading() wx.showToast({ title: '删除失败', icon: 'error' }) } }, // 验证附件 validateAttachment(file) { const fileName = file.name.toLowerCase() const fileExtension = fileName.split('.').pop() const fileSize = file.size // 查找文件类型 let fileType = 'other' let config = null for (const [type, typeConfig] of Object.entries(this.data.fileTypeConfig)) { if (typeConfig.extensions.includes(fileExtension)) { fileType = type config = typeConfig break } } if (!config) { return { valid: false, message: `不支持的文件格式:${fileExtension.toUpperCase()}` } } // 检查文件大小 if (fileSize > config.maxSize) { return { valid: false, message: `文件过大,${fileType === 'video' ? '视频' : fileType === 'image' ? '图片' : '文档'}文件不能超过${this.formatFileSize(config.maxSize)}` } } return { valid: true, type: fileType, config: config } }, // 格式化文件大小 formatFileSize(bytes) { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] }, // 字段验证 validateField(field, value) { let isValid = true let errorMsg = '' switch (field) { case 'phone': const phoneReg = /^1[3-9]\d{9}$/ if (value && !phoneReg.test(value)) { isValid = false errorMsg = '请输入正确的手机号' } break case 'avatar': // 如果活动要求头像,则验证头像是否已上传 if (this.data.activity && this.data.activity.requireAvatar) { if (!this.data.formData.avatarUrl || !this.data.formData.avatarMediaId) { isValid = false errorMsg = '请上传头像' } } break case 'email': const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ if (value && !emailReg.test(value)) { isValid = false errorMsg = '请输入正确的邮箱地址' } break case 'idCard': const idCardReg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/ if (value && !idCardReg.test(value)) { isValid = false errorMsg = '请输入正确的身份证号' } break } if (!isValid) { this.setData({ [`errors.${field}`]: errorMsg }) } return isValid }, // 表单验证 validateForm() { const { formData } = this.data; const errors = {}; // 必填字段验证 if (!formData.name.trim()) { errors.name = '请输入姓名'; } if (!formData.phone.trim()) { errors.phone = '请输入手机号'; } else if (!/^1[3-9]\d{9}$/.test(formData.phone)) { errors.phone = '请输入正确的手机号'; } if (formData.gender === null) { errors.gender = '请选择性别'; } // 区域验证(可选) // 暂时不强制要求区域选择 // 头像验证(可选) // 暂时不强制要求头像上传 if (!formData.projectName.trim()) { errors.projectName = '请输入项目名称'; } if (!formData.description.trim()) { errors.description = '请输入项目描述'; } this.setData({ errors }); return Object.keys(errors).length === 0; }, // 提交前上传头像 async uploadAvatarBeforeSubmit() { if (!this.data.localAvatarPath) { return } try { // 显示上传进度 this.setData({ avatarUploading: true, avatarUploadProgress: 0 }) // 上传到COS const uploadResult = await cosUtil.uploadAvatar(this.data.localAvatarPath, { onProgress: (progress) => { this.setData({ avatarUploadProgress: Math.round(progress.percent) }) } }) // 上传成功,更新表单数据 this.setData({ 'formData.avatarUrl': uploadResult.url, 'formData.avatarMediaId': uploadResult.key, localAvatarPath: '', // 清空本地路径 avatarUploading: false, avatarUploadProgress: 0 }) console.log('头像上传成功:', uploadResult) } catch (error) { this.setData({ avatarUploading: false, avatarUploadProgress: 0 }) console.error('头像上传失败:', error) // 根据错误类型显示不同的提示 let errorMessage = '头像上传失败' if (error.message) { if (error.message.includes('网络')) { errorMessage = '网络连接失败,请检查网络后重试' } else if (error.message.includes('权限')) { errorMessage = '上传权限不足,请联系管理员' } else if (error.message.includes('空间')) { errorMessage = '存储空间不足,请稍后重试' } } throw new Error(errorMessage) } }, async onSubmit() { if (this.data.isSubmitting) return // 表单验证 if (!this.validateForm()) { wx.showToast({ title: '请检查表单信息', icon: 'none' }) return } this.setData({ isSubmitting: true }) try { const { formData } = this.data // 使用页面参数中的activityId,而不是从activity对象中获取 const activityId = this.data.activityId // 准备提交数据(不包含文件) const submitData = { activityId, playerInfo: { name: formData.name, phone: formData.phone, gender: formData.gender, birthDate: formData.birthDate, education: formData.education, introduction: formData.introduction, avatarMediaId: null // 先不传头像 }, regionId: formData.regionId, projectName: formData.projectName, description: formData.description } // 第一步:先提交注册数据到后台,获得注册ID const result = await this.submitRegistration(submitData) if (result.success && result.registrationId) { // 第二步:使用返回的ID信息上传头像和附件 await this.uploadFilesAfterRegistration({ registrationId: result.registrationId, playerId: result.playerId, userId: result.userId, activityPlayerId: result.activityPlayerId }) wx.showToast({ title: '报名成功', icon: 'success' }) setTimeout(() => { wx.navigateBack() }, 1500) } else { throw new Error(result.message || '提交失败') } } catch (error) { console.error('提交失败:', error) wx.showToast({ title: error.message || '提交失败,请重试', icon: 'none' }) } finally { this.setData({ isSubmitting: false }) } }, // 提交报名数据到后端(不包含文件) async submitRegistration(submitData) { const mutation = ` mutation SubmitActivityRegistration($input: ActivityRegistrationInput!) { submitActivityRegistration(input: $input) { success message registrationId playerId userId activityPlayerId } } `; const input = { activityId: parseInt(submitData.activityId), playerInfo: { name: submitData.playerInfo.name, phone: submitData.playerInfo.phone, gender: submitData.playerInfo.gender || 0, birthDate: submitData.playerInfo.birthDate || null, education: submitData.playerInfo.education || '', introduction: submitData.playerInfo.introduction || '', avatarMediaId: null // 先不传头像 }, regionId: submitData.regionId || null, projectName: submitData.projectName || '', description: submitData.description || '', attachmentMediaIds: [] // 先不传附件 }; const result = await graphqlRequest(mutation, { input }); if (result && result.submitActivityRegistration) { return result.submitActivityRegistration; } else { throw new Error('提交失败'); } }, // 注册成功后上传文件 async uploadFilesAfterRegistration(idInfo) { try { // 上传头像(如果有) if (this.data.localAvatarPath) { await this.uploadAvatarWithRegistrationId(idInfo) } // 上传附件(如果有) if (this.data.attachments.length > 0) { await this.uploadAttachmentsWithRegistrationId(idInfo) } } catch (error) { console.error('文件上传失败:', error) // 文件上传失败不影响注册成功,只是提示用户 wx.showToast({ title: '文件上传失败,但报名已成功', icon: 'none', duration: 3000 }) } }, // 使用注册ID上传头像 async uploadAvatarWithRegistrationId(idInfo) { try { this.setData({ avatarUploading: true, avatarUploadProgress: 0 }) // 第一步:上传到COS const uploadResult = await cosUtil.uploadAvatar(this.data.localAvatarPath, 'avatar.jpg', (progress) => { this.setData({ avatarUploadProgress: Math.round(progress.percent) }) }) // 第二步:保存媒体记录到数据库 await this.saveMediaRecord({ targetType: 7, // USER_AVATAR targetId: idInfo.userId, url: uploadResult.url, fileName: uploadResult.fileName || 'avatar.jpg', fileSize: uploadResult.fileSize, mediaType: 1 // 图片 }) this.setData({ avatarUploading: false, avatarUploadProgress: 0 }) console.log('头像上传成功:', uploadResult) } catch (error) { this.setData({ avatarUploading: false, avatarUploadProgress: 0 }) console.error('头像上传失败:', error) throw error } }, // 使用注册ID上传附件 async uploadAttachmentsWithRegistrationId(idInfo) { for (let i = 0; i < this.data.attachments.length; i++) { const attachment = this.data.attachments[i] if (!attachment.uploaded && attachment.localPath) { try { // 第一步:上传到COS const uploadResult = await cosUtil.uploadFile(attachment.localPath, 'attachment', attachment.name || 'attachment', (progress) => { this.setData({ [`attachments[${i}].uploadProgress`]: Math.round(progress.percent) }) }) // 第二步:保存媒体记录到数据库 await this.saveMediaRecord({ targetType: 5, // ACTIVITY_PLAYER_SUBMISSION targetId: idInfo.activityPlayerId, url: uploadResult.url, fileName: uploadResult.fileName || attachment.name, fileSize: uploadResult.fileSize, mediaType: this.getMediaType(attachment.name) // 根据文件扩展名判断类型 }) // 更新附件状态 this.setData({ [`attachments[${i}].uploaded`]: true, [`attachments[${i}].mediaId`]: uploadResult.key, [`attachments[${i}].url`]: uploadResult.url, [`attachments[${i}].uploadProgress`]: 0 }) console.log(`附件${i + 1}上传成功:`, uploadResult) } catch (error) { console.error(`附件${i + 1}上传失败:`, error) throw error } } } }, // 保存媒体记录到数据库 async saveMediaRecord(mediaData) { const mutation = ` mutation SaveMedia($input: MediaInput!) { saveMedia(input: $input) { id url fileName } } ` const variables = { input: { targetType: mediaData.targetType, targetId: mediaData.targetId, url: mediaData.url, fileName: mediaData.fileName, fileSize: mediaData.fileSize, mediaType: mediaData.mediaType } } try { const result = await app.graphqlRequest(mutation, variables) console.log('媒体记录保存成功:', result.saveMedia) return result.saveMedia } catch (error) { console.error('媒体记录保存失败:', error) throw error } }, // 根据文件名获取媒体类型 getMediaType(fileName) { if (!fileName) return 1 // 默认图片 const ext = fileName.toLowerCase().split('.').pop() const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'] const videoExts = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'] const audioExts = ['mp3', 'wav', 'aac', 'flac', 'ogg'] const docExts = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'] if (imageExts.includes(ext)) return 1 // 图片 if (videoExts.includes(ext)) return 2 // 视频 if (audioExts.includes(ext)) return 3 // 音频 if (docExts.includes(ext)) return 4 // 文档 return 5 // 其他 } })