You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
509 lines
15 KiB
509 lines
15 KiB
<template>
|
|
<!-- 体测记录编辑弹窗 - 使用view类型 -->
|
|
<view v-if="isVisible" class="fitness-record-popup" @click.stop="handleMaskClick">
|
|
<view class="popup-container" @click.stop>
|
|
<view class="popup-header">
|
|
<view class="popup-title">{{ isEditing ? '编辑体测记录' : '新增体测记录' }}</view>
|
|
<view class="popup-close" @click="close">✕</view>
|
|
</view>
|
|
<view class="fitness-record-form">
|
|
<view class="form-section">
|
|
<view class="form-item">
|
|
<view class="form-label">测试日期</view>
|
|
<view class="form-input">
|
|
<input type="date" v-model="recordData.test_date" placeholder="请选择测试日期" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<view class="form-label">身高(cm)</view>
|
|
<view class="form-input">
|
|
<input type="number" v-model="recordData.height" placeholder="请输入身高" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<view class="form-label">体重(kg)</view>
|
|
<view class="form-input">
|
|
<input type="number" v-model="recordData.weight" placeholder="请输入体重" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<view class="form-label">体测报告</view>
|
|
<view class="file-upload-area">
|
|
<view class="upload-btn" @click="selectPDFFiles">
|
|
<view class="upload-icon">📁</view>
|
|
<view class="upload-text">选择PDF文件</view>
|
|
</view>
|
|
|
|
<!-- 已选择的PDF文件列表 -->
|
|
<view v-if="recordData.pdf_files && recordData.pdf_files.length > 0"
|
|
class="selected-files">
|
|
<view v-for="(pdf, index) in recordData.pdf_files" :key="index"
|
|
class="selected-file-item">
|
|
<view class="file-info" @click="previewPDF(pdf)">
|
|
<view class="file-icon">📄</view>
|
|
<view class="file-name">{{ pdf.name }}</view>
|
|
<view class="file-size">{{ formatFileSize(pdf.size) }}</view>
|
|
</view>
|
|
<view class="file-remove" @click="removePDFFile(index)">✕</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="popup-footer">
|
|
<view class="popup-btn cancel-btn" @click="close">取消</view>
|
|
<view class="popup-btn confirm-btn" @click="confirm">确认</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'FitnessRecordPopup',
|
|
props: {
|
|
resourceId: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
studentId: {
|
|
type: [String, Number],
|
|
default: ''
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
isVisible: false,
|
|
isEditing: false,
|
|
recordData: {
|
|
id: null,
|
|
test_date: '',
|
|
height: '',
|
|
weight: '',
|
|
pdf_files: []
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
// 打开新增弹窗
|
|
openAdd() {
|
|
this.isEditing = false
|
|
this.resetData()
|
|
this.recordData.test_date = this.getCurrentDate()
|
|
this.isVisible = true
|
|
},
|
|
|
|
// 打开编辑弹窗
|
|
openEdit(record) {
|
|
this.isEditing = true
|
|
this.recordData = {
|
|
id: record.id,
|
|
test_date: record.test_date,
|
|
height: record.height,
|
|
weight: record.weight,
|
|
pdf_files: [...(record.pdf_files || [])]
|
|
}
|
|
this.isVisible = true
|
|
},
|
|
|
|
// 关闭弹窗
|
|
close() {
|
|
this.isVisible = false
|
|
this.resetData()
|
|
this.$emit('close')
|
|
},
|
|
|
|
// 点击遮罩关闭
|
|
handleMaskClick() {
|
|
this.close()
|
|
},
|
|
|
|
// 重置数据
|
|
resetData() {
|
|
this.recordData = {
|
|
id: null,
|
|
test_date: '',
|
|
height: '',
|
|
weight: '',
|
|
pdf_files: []
|
|
}
|
|
},
|
|
|
|
// 确认保存
|
|
async confirm() {
|
|
try {
|
|
// 表单验证
|
|
if (!this.recordData.test_date) {
|
|
uni.showToast({
|
|
title: '请选择测试日期',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
if (!this.recordData.height) {
|
|
uni.showToast({
|
|
title: '请输入身高',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
if (!this.recordData.weight) {
|
|
uni.showToast({
|
|
title: '请输入体重',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
uni.showLoading({
|
|
title: '保存中...',
|
|
mask: true
|
|
})
|
|
|
|
// 确保resource_id和student_id不为空
|
|
console.log('当前resourceId:', this.resourceId)
|
|
console.log('当前studentId:', this.studentId)
|
|
console.log('父组件传递的resourceId:', this.$props.resourceId)
|
|
console.log('父组件传递的studentId:', this.$props.studentId)
|
|
|
|
if (!this.resourceId) {
|
|
uni.showToast({
|
|
title: '缺少学生资源ID,请稍后重试',
|
|
icon: 'none'
|
|
})
|
|
uni.hideLoading()
|
|
return
|
|
}
|
|
|
|
if (!this.studentId) {
|
|
uni.showToast({
|
|
title: '缺少学生ID,请稍后重试',
|
|
icon: 'none'
|
|
})
|
|
uni.hideLoading()
|
|
return
|
|
}
|
|
|
|
const params = {
|
|
resource_id: this.resourceId,
|
|
student_id: this.studentId, // 使用正确的student_id
|
|
test_date: this.recordData.test_date,
|
|
height: this.recordData.height,
|
|
weight: this.recordData.weight,
|
|
// 将PDF文件转换为服务器需要的格式 - 使用server_path或url
|
|
physical_test_report: this.recordData.pdf_files
|
|
.map(file => file.server_path || file.url)
|
|
.filter(path => path)
|
|
.join(',') // 用逗号连接多个文件路径
|
|
}
|
|
|
|
if (this.isEditing) {
|
|
params.id = this.recordData.id
|
|
}
|
|
|
|
console.log('保存体测记录参数:', params)
|
|
|
|
// 触发确认事件,将数据传递给父组件
|
|
this.$emit('confirm', {
|
|
isEditing: this.isEditing,
|
|
data: params
|
|
})
|
|
|
|
uni.hideLoading()
|
|
this.close()
|
|
|
|
} catch (error) {
|
|
console.error('执行保存体测记录失败:', error)
|
|
uni.showToast({
|
|
title: '保存失败,请重试',
|
|
icon: 'none'
|
|
})
|
|
uni.hideLoading()
|
|
}
|
|
},
|
|
|
|
// 选择PDF文件
|
|
selectPDFFiles() {
|
|
// 动态检测并选择合适的文件选择API
|
|
let chooseFileMethod = null;
|
|
|
|
// #ifdef MP-WEIXIN
|
|
// 微信小程序优先使用 chooseMessageFile
|
|
if (typeof uni.chooseMessageFile === 'function') {
|
|
chooseFileMethod = uni.chooseMessageFile;
|
|
} else if (typeof wx !== 'undefined' && typeof wx.chooseMessageFile === 'function') {
|
|
chooseFileMethod = wx.chooseMessageFile;
|
|
}
|
|
// #endif
|
|
|
|
// #ifndef MP-WEIXIN
|
|
// 其他平台使用 chooseFile
|
|
if (typeof uni.chooseFile === 'function') {
|
|
chooseFileMethod = uni.chooseFile;
|
|
}
|
|
// #endif
|
|
|
|
// 如果没有找到合适的方法,显示错误提示
|
|
if (!chooseFileMethod) {
|
|
uni.showModal({
|
|
title: '不支持文件选择',
|
|
content: '当前平台不支持文件选择功能,请手动输入文件信息或联系技术支持',
|
|
showCancel: false
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 调用选择的文件选择方法
|
|
chooseFileMethod({
|
|
count: 5,
|
|
type: 'file',
|
|
extension: ['pdf'],
|
|
success: async (res) => {
|
|
console.log('选择的文件:', res.tempFiles)
|
|
await this.handleSelectedFiles(res.tempFiles)
|
|
},
|
|
fail: (err) => {
|
|
console.error('选择文件失败:', err)
|
|
// 提供更详细的错误信息
|
|
let errorMsg = '选择文件失败';
|
|
if (err.errMsg) {
|
|
if (err.errMsg.includes('cancel')) {
|
|
errorMsg = '用户取消了文件选择';
|
|
} else if (err.errMsg.includes('limit')) {
|
|
errorMsg = '文件数量或大小超出限制';
|
|
} else {
|
|
errorMsg = '选择文件失败: ' + err.errMsg;
|
|
}
|
|
}
|
|
|
|
uni.showToast({
|
|
title: errorMsg,
|
|
icon: 'none',
|
|
duration: 3000
|
|
})
|
|
}
|
|
})
|
|
},
|
|
|
|
// 处理选择的文件(提取公共逻辑)
|
|
async handleSelectedFiles(tempFiles) {
|
|
// 显示上传进度
|
|
uni.showLoading({
|
|
title: '上传中...',
|
|
mask: true
|
|
})
|
|
|
|
let successCount = 0
|
|
let totalCount = tempFiles.length
|
|
|
|
for (let file of tempFiles) {
|
|
// 检查文件类型和扩展名
|
|
const isValidPDF = this.isValidPDFFile(file)
|
|
|
|
if (isValidPDF) {
|
|
try {
|
|
// 立即上传PDF文件到服务器(使用通用文档上传接口)
|
|
const uploadResult = await this.uploadPdfFile(file)
|
|
if (uploadResult && uploadResult.code === 1) {
|
|
const pdfFile = {
|
|
id: Date.now() + Math.random(),
|
|
name: file.name,
|
|
size: file.size,
|
|
url: uploadResult.data.url, // 使用标准响应中的url字段
|
|
server_path: uploadResult.data.url, // 服务器可访问路径
|
|
upload_time: new Date().toLocaleString(),
|
|
ext: uploadResult.data.ext || 'pdf',
|
|
original_name: uploadResult.data.name || file.name
|
|
}
|
|
this.recordData.pdf_files.push(pdfFile)
|
|
successCount++
|
|
} else {
|
|
console.error('文件上传失败:', uploadResult)
|
|
uni.showToast({
|
|
title: uploadResult.msg || '文件上传失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error('上传PDF文件失败:', error)
|
|
uni.showToast({
|
|
title: '文件上传失败: ' + (error.msg || error.message || '网络异常'),
|
|
icon: 'none',
|
|
duration: 3000
|
|
})
|
|
}
|
|
} else {
|
|
uni.showToast({
|
|
title: '请选择PDF格式文件',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
|
|
uni.hideLoading()
|
|
|
|
// 显示上传结果
|
|
if (successCount > 0) {
|
|
uni.showToast({
|
|
title: `成功上传 ${successCount}/${totalCount} 个文件`,
|
|
icon: 'success'
|
|
})
|
|
}
|
|
},
|
|
|
|
// 验证文件是否为有效的PDF文件
|
|
isValidPDFFile(file) {
|
|
// 检查MIME类型
|
|
if (file.type && file.type === 'application/pdf') {
|
|
return true
|
|
}
|
|
|
|
// 检查文件扩展名(作为备用验证)
|
|
const fileName = file.name || ''
|
|
const extension = fileName.toLowerCase().split('.').pop()
|
|
if (extension === 'pdf') {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
},
|
|
|
|
// 上传PDF文件到服务器(使用体测专用PDF上传接口)
|
|
async uploadPdfFile(file) {
|
|
const { Api_url } = require('@/common/config.js')
|
|
const token = uni.getStorageSync('token') || ''
|
|
|
|
console.log('开始上传PDF文件:', {
|
|
fileName: file.name,
|
|
fileSize: file.size,
|
|
filePath: file.path,
|
|
uploadUrl: Api_url + '/xy/physicalTest/uploadPdf',
|
|
hasToken: !!token
|
|
})
|
|
|
|
return new Promise((resolve, reject) => {
|
|
uni.uploadFile({
|
|
url: Api_url + '/xy/physicalTest/uploadPdf', // 使用体测专用PDF上传接口
|
|
filePath: file.path,
|
|
name: 'file',
|
|
header: {
|
|
'token': token
|
|
// 不设置 Content-Type,让uni-app自动设置为 multipart/form-data
|
|
},
|
|
success: (res) => {
|
|
console.log('PDF上传原始响应:', res)
|
|
let response
|
|
try {
|
|
// 去除 BOM 字符并解析 JSON
|
|
response = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}')
|
|
console.log('PDF上传解析后响应:', response)
|
|
} catch (e) {
|
|
console.error('PDF上传响应解析失败:', e, 'raw response:', res.data)
|
|
reject({
|
|
code: 0,
|
|
msg: '服务器响应格式错误: ' + res.data,
|
|
error: e
|
|
})
|
|
return
|
|
}
|
|
|
|
if (response.code === 1) {
|
|
console.log('PDF上传成功:', response.data)
|
|
// 根据体测PDF上传接口的响应格式处理
|
|
resolve({
|
|
code: 1,
|
|
msg: response.msg || '上传成功',
|
|
data: {
|
|
url: response.data.file_url, // 后端返回的 file_url 字段
|
|
name: response.data.file_name || file.name,
|
|
ext: 'pdf',
|
|
size: response.data.file_size || file.size
|
|
}
|
|
})
|
|
} else if (response.code === 401) {
|
|
console.error('PDF上传认证失败:', response)
|
|
uni.showToast({
|
|
title: response.msg || '登录已过期',
|
|
icon: 'none'
|
|
})
|
|
setTimeout(() => {
|
|
uni.navigateTo({ url: '/pages/student/login/login' })
|
|
}, 1500)
|
|
reject(response)
|
|
} else {
|
|
console.error('PDF上传失败响应:', response)
|
|
reject({
|
|
code: response.code || 0,
|
|
msg: response.msg || 'PDF上传失败'
|
|
})
|
|
}
|
|
},
|
|
fail: (err) => {
|
|
console.error('PDF上传网络失败:', err)
|
|
reject({
|
|
code: 0,
|
|
msg: err.errMsg || '网络异常,请检查网络连接',
|
|
error: err
|
|
})
|
|
}
|
|
})
|
|
})
|
|
},
|
|
|
|
// 移除PDF文件
|
|
removePDFFile(index) {
|
|
this.recordData.pdf_files.splice(index, 1)
|
|
},
|
|
|
|
// 预览PDF文件
|
|
previewPDF(pdf) {
|
|
console.log('预览PDF:', pdf)
|
|
|
|
// 使用uni.openDocument预览PDF
|
|
uni.openDocument({
|
|
filePath: pdf.url,
|
|
fileType: 'pdf',
|
|
showMenu: true,
|
|
success: (res) => {
|
|
console.log('PDF预览成功:', res)
|
|
},
|
|
fail: (err) => {
|
|
console.error('PDF预览失败:', err)
|
|
uni.showToast({
|
|
title: '预览失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
})
|
|
},
|
|
|
|
// 获取当前日期
|
|
getCurrentDate() {
|
|
const now = new Date()
|
|
const year = now.getFullYear()
|
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
|
const day = String(now.getDate()).padStart(2, '0')
|
|
return `${year}-${month}-${day}`
|
|
},
|
|
|
|
// 格式化文件大小
|
|
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(2)) + ' ' + sizes[i]
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
@import './fitness-record-popup.less';
|
|
</style>
|