883 lines
23 KiB
Vue
883 lines
23 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="achievement-form-page">
|
|||
|
|
<!-- 表单内容 -->
|
|||
|
|
<scroll-view scroll-y class="form-scroll-view">
|
|||
|
|
<view class="form-container">
|
|||
|
|
|
|||
|
|
<!-- 成果名称 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-label">
|
|||
|
|
<text class="label-text">成果名称</text>
|
|||
|
|
<text class="required-star">*</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="simple-text-wrapper">
|
|||
|
|
<textarea
|
|||
|
|
v-model="formData.jycgmc"
|
|||
|
|
placeholder="请输入成果名称..."
|
|||
|
|
class="simple-textarea"
|
|||
|
|
:maxlength="200"
|
|||
|
|
auto-height
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 解决的主要问题 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-label">
|
|||
|
|
<text class="label-text">解决的主要问题</text>
|
|||
|
|
<text class="required-star">*</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="simple-text-wrapper">
|
|||
|
|
<textarea
|
|||
|
|
v-model="formData.jjdywt"
|
|||
|
|
placeholder="请输入解决的主要问题..."
|
|||
|
|
class="simple-textarea"
|
|||
|
|
:maxlength="2000"
|
|||
|
|
auto-height
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 最终成果 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-label">
|
|||
|
|
<text class="label-text">最终成果</text>
|
|||
|
|
<text class="required-star">*</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="simple-text-wrapper">
|
|||
|
|
<textarea
|
|||
|
|
v-model="formData.zzcg"
|
|||
|
|
placeholder="请输入最终成果内容..."
|
|||
|
|
class="simple-textarea"
|
|||
|
|
:maxlength="5000"
|
|||
|
|
auto-height
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 添加附件 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-label">
|
|||
|
|
<text class="label-text">添加附件</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="file-upload-wrapper">
|
|||
|
|
<ImageVideoUpload
|
|||
|
|
v-model:file-list="fileList"
|
|||
|
|
:max-file-count="30"
|
|||
|
|
:enable-image="false"
|
|||
|
|
:enable-video="false"
|
|||
|
|
:enable-file="true"
|
|||
|
|
:allowed-file-types="allowedFileTypes"
|
|||
|
|
:upload-api="customUploadApi"
|
|||
|
|
@file-upload-success="onFileUploadSuccess"
|
|||
|
|
@upload-progress="onUploadProgress"
|
|||
|
|
@file-upload-error="onFileUploadError"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 提交人 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-label">
|
|||
|
|
<text class="label-text">提交人</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="readonly-input-wrapper">
|
|||
|
|
<text class="readonly-text">{{ formData.jsxm }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 提交时间 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-label">
|
|||
|
|
<text class="label-text">提交时间</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="readonly-input-wrapper">
|
|||
|
|
<text class="readonly-text">{{ formData.createdTime }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
</view>
|
|||
|
|
</scroll-view>
|
|||
|
|
|
|||
|
|
<!-- 底部固定操作按钮 -->
|
|||
|
|
<view class="bottom-actions">
|
|||
|
|
<view class="action-btn cancel-btn" @click="cancelForm">
|
|||
|
|
<text class="btn-text">取消</text>
|
|||
|
|
</view>
|
|||
|
|
<view
|
|||
|
|
class="action-btn submit-btn"
|
|||
|
|
:class="{ 'disabled': isUploading }"
|
|||
|
|
@click="submitAchievement"
|
|||
|
|
>
|
|||
|
|
<text class="btn-text">{{ isEdit ? '保存' : '提交' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 文件上传遮罩层 -->
|
|||
|
|
<view v-if="isUploading" class="upload-mask">
|
|||
|
|
<view class="upload-content">
|
|||
|
|
<view class="upload-icon">
|
|||
|
|
<text class="icon-text">📤</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="upload-title">文件上传中...</view>
|
|||
|
|
<view class="upload-status">{{ uploadStatus }}</view>
|
|||
|
|
<view class="upload-progress">
|
|||
|
|
<view class="progress-bar">
|
|||
|
|
<view
|
|||
|
|
class="progress-fill"
|
|||
|
|
:style="{ width: uploadProgress + '%' }"
|
|||
|
|
></view>
|
|||
|
|
</view>
|
|||
|
|
<text class="progress-text">{{ uploadProgress }}%</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="upload-tip">请等待文件上传完成后再提交</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { ref, reactive, onMounted } from "vue";
|
|||
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|||
|
|
import { jycgSaveApi, jycgFindByIdApi, jycgUploadFileApi } from "@/api/base/jycgApi";
|
|||
|
|
import { useUserStore } from "@/store/modules/user";
|
|||
|
|
import { ImageVideoUpload, type FileItem } from "@/components/ImageVideoUpload";
|
|||
|
|
import { attachmentUpload } from "@/api/system/upload";
|
|||
|
|
|
|||
|
|
const userStore = useUserStore();
|
|||
|
|
const { getJs, getUser } = userStore;
|
|||
|
|
|
|||
|
|
// 是否为编辑模式
|
|||
|
|
const isEdit = ref(false);
|
|||
|
|
const isLoading = ref(false);
|
|||
|
|
|
|||
|
|
// 文件上传状态管理
|
|||
|
|
const isUploading = ref(false);
|
|||
|
|
const uploadProgress = ref(0);
|
|||
|
|
const uploadStatus = ref('');
|
|||
|
|
|
|||
|
|
// 表单数据
|
|||
|
|
const formData = reactive({
|
|||
|
|
id: '', // 成果ID(编辑时使用)
|
|||
|
|
jycgmc: '', // 成果名称
|
|||
|
|
jjdywt: '', // 解决的主要问题
|
|||
|
|
jjgc: '', // 解决过程
|
|||
|
|
zzcg: '', // 最终成果
|
|||
|
|
jsId: '', // 教师ID
|
|||
|
|
jsxm: '', // 教师姓名(提交人)
|
|||
|
|
jyjbId: '', // 教研组ID
|
|||
|
|
createdTime: '', // 创建时间(提交时间)
|
|||
|
|
fileName: '', // 文件名
|
|||
|
|
fileUrl: '', // 文件路径
|
|||
|
|
fileFormat: '', // 文件格式
|
|||
|
|
remark: '' // 备注
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 文件上传数据
|
|||
|
|
const fileList = ref<FileItem[]>([]);
|
|||
|
|
|
|||
|
|
// 允许的文件类型(支持所有格式)
|
|||
|
|
const allowedFileTypes = [
|
|||
|
|
// 图片格式
|
|||
|
|
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg', 'tiff', 'ico',
|
|||
|
|
// 视频格式
|
|||
|
|
'mp4', 'mov', 'avi', 'wmv', 'flv', 'mkv', 'webm', '3gp', 'm4v',
|
|||
|
|
// 音频格式
|
|||
|
|
'mp3', 'wav', 'aac', 'ogg', 'flac', 'm4a', 'wma',
|
|||
|
|
// 文档格式
|
|||
|
|
'pdf', 'doc', 'docx', 'txt', 'rtf', 'wps',
|
|||
|
|
// 表格格式
|
|||
|
|
'xls', 'xlsx', 'csv', 'et',
|
|||
|
|
// 演示格式
|
|||
|
|
'ppt', 'pptx', 'dps',
|
|||
|
|
// 压缩格式
|
|||
|
|
'zip', 'rar', '7z', 'tar', 'gz',
|
|||
|
|
// 代码格式
|
|||
|
|
'html', 'htm', 'css', 'js', 'json', 'xml'
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 自定义上传API,保持原始文件名
|
|||
|
|
const customUploadApi = async (filePath: string) => {
|
|||
|
|
try {
|
|||
|
|
const result: any = await attachmentUpload(filePath as any);
|
|||
|
|
|
|||
|
|
console.log('上传接口返回结果:', result);
|
|||
|
|
|
|||
|
|
// 判断上传是否成功:resultCode === 1 且 success === true
|
|||
|
|
if (result && result.resultCode === 1 && result.success === true && result.result && result.result.length > 0) {
|
|||
|
|
const uploadedFile = result.result[0];
|
|||
|
|
|
|||
|
|
// 从文件路径中提取原始文件名(去掉UUID前缀)
|
|||
|
|
const originalFileName = filePath.split('/').pop() || 'unknown';
|
|||
|
|
|
|||
|
|
console.log('文件上传成功,准备返回结果');
|
|||
|
|
|
|||
|
|
// 返回包含原始文件名的结果
|
|||
|
|
return {
|
|||
|
|
...result,
|
|||
|
|
result: [{
|
|||
|
|
...uploadedFile,
|
|||
|
|
originalFileName: originalFileName,
|
|||
|
|
fileName: originalFileName
|
|||
|
|
}]
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 上传失败
|
|||
|
|
console.error('上传失败,接口返回:', result);
|
|||
|
|
throw new Error(result?.message || '文件上传失败');
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('上传失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 文件上传成功回调
|
|||
|
|
const onFileUploadSuccess = (file: FileItem, index: number) => {
|
|||
|
|
console.log('=== 文件上传成功回调 ===');
|
|||
|
|
console.log('接收到的文件对象:', file);
|
|||
|
|
console.log('文件索引:', index);
|
|||
|
|
console.log('当前fileList长度:', fileList.value.length);
|
|||
|
|
console.log('当前fileList内容:', fileList.value);
|
|||
|
|
|
|||
|
|
// 从服务器返回的URL中提取真实文件名
|
|||
|
|
if (file.url) {
|
|||
|
|
const urlParts = file.url.split('/');
|
|||
|
|
const serverFileName = urlParts[urlParts.length - 1];
|
|||
|
|
|
|||
|
|
console.log('解析的文件信息:', {
|
|||
|
|
url: file.url,
|
|||
|
|
serverFileName,
|
|||
|
|
originalName: file.originalName
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 更新文件信息
|
|||
|
|
if (fileList.value[index]) {
|
|||
|
|
console.log('更新前的文件项:', fileList.value[index]);
|
|||
|
|
|
|||
|
|
fileList.value[index].url = file.url; // 确保URL被正确设置
|
|||
|
|
fileList.value[index].name = serverFileName;
|
|||
|
|
// 保持原始文件名用于显示和保存
|
|||
|
|
fileList.value[index].originalName = file.originalName || serverFileName;
|
|||
|
|
|
|||
|
|
console.log('更新后的文件项:', fileList.value[index]);
|
|||
|
|
} else {
|
|||
|
|
console.error('文件索引超出范围或文件不存在:', index, fileList.value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 立即测试文件信息收集
|
|||
|
|
console.log('=== 文件信息收集测试 ===');
|
|||
|
|
console.log('当前文件列表:', fileList.value);
|
|||
|
|
console.log('收集的文件URLs:', getFileUrls());
|
|||
|
|
console.log('收集的文件名:', getFileNames());
|
|||
|
|
console.log('收集的文件格式:', getFileFormats());
|
|||
|
|
|
|||
|
|
// 检查是否所有文件都已上传完成(真正的上传成功)
|
|||
|
|
const allFilesUploaded = fileList.value.every(f => f.url);
|
|||
|
|
console.log('所有文件是否已上传:', allFilesUploaded);
|
|||
|
|
|
|||
|
|
if (allFilesUploaded) {
|
|||
|
|
// 延迟关闭遮罩层,让用户看到上传完成的提示
|
|||
|
|
setTimeout(() => {
|
|||
|
|
isUploading.value = false;
|
|||
|
|
uploadProgress.value = 0;
|
|||
|
|
uploadStatus.value = '';
|
|||
|
|
console.log('所有文件上传成功,关闭遮罩层');
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.error('文件URL为空:', file);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 文件上传进度回调
|
|||
|
|
const onUploadProgress = (type: string, current: number, total: number) => {
|
|||
|
|
console.log('上传进度:', { type, current, total });
|
|||
|
|
isUploading.value = true;
|
|||
|
|
uploadProgress.value = Math.round((current / total) * 100);
|
|||
|
|
uploadStatus.value = `正在上传文件 ${current}/${total}`;
|
|||
|
|
|
|||
|
|
// 注意:不在这里关闭遮罩层,因为进度100%不代表接口返回成功
|
|||
|
|
// 遮罩层的关闭在 onFileUploadSuccess 回调中处理
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 文件上传错误回调
|
|||
|
|
const onFileUploadError = (error: any, index: number) => {
|
|||
|
|
console.error('文件上传失败:', error, index);
|
|||
|
|
uploadStatus.value = '文件上传失败,请重试';
|
|||
|
|
|
|||
|
|
// 延迟隐藏遮罩层
|
|||
|
|
setTimeout(() => {
|
|||
|
|
isUploading.value = false;
|
|||
|
|
uploadProgress.value = 0;
|
|||
|
|
uploadStatus.value = '';
|
|||
|
|
}, 2000);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取文件URL列表
|
|||
|
|
const getFileUrls = () => {
|
|||
|
|
const urls = fileList.value
|
|||
|
|
.filter(file => file.url)
|
|||
|
|
.map(file => file.url)
|
|||
|
|
.filter((url): url is string => !!url);
|
|||
|
|
return urls.length > 0 ? urls.join(',') : '';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取文件名列表(不包含扩展名)
|
|||
|
|
const getFileNames = () => {
|
|||
|
|
const names = fileList.value
|
|||
|
|
.filter(file => file.originalName || file.name)
|
|||
|
|
.map(file => {
|
|||
|
|
const fullName = file.originalName || file.name || '';
|
|||
|
|
// 去除扩展名,只保留文件名
|
|||
|
|
return fullName.replace(/\.[^/.]+$/, '');
|
|||
|
|
})
|
|||
|
|
.filter((name): name is string => !!name);
|
|||
|
|
return names.length > 0 ? names.join(',') : '';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取文件格式列表
|
|||
|
|
const getFileFormats = () => {
|
|||
|
|
const formats = fileList.value
|
|||
|
|
.filter(file => file.originalName || file.name)
|
|||
|
|
.map(file => (file.originalName || file.name)?.split('.').pop()?.toLowerCase() || '')
|
|||
|
|
.filter(format => !!format);
|
|||
|
|
return formats.length > 0 ? formats.join(',') : '';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 取消表单
|
|||
|
|
const cancelForm = () => {
|
|||
|
|
uni.showModal({
|
|||
|
|
title: '确认取消',
|
|||
|
|
content: '确定要取消并返回吗?未保存的内容将丢失',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 提交成果
|
|||
|
|
const submitAchievement = async () => {
|
|||
|
|
// 检查文件上传状态
|
|||
|
|
if (isUploading.value) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '文件正在上传中,请稍候...',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证必填项
|
|||
|
|
if (!formData.jycgmc) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '请输入成果名称',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!formData.jjdywt) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '请输入解决的主要问题',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!formData.zzcg) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '请输入最终成果',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
uni.showLoading({ title: isEdit.value ? '保存中...' : '提交中...' });
|
|||
|
|
|
|||
|
|
// 收集所有文件URL和文件名
|
|||
|
|
const fileUrls = getFileUrls();
|
|||
|
|
const fileNames = getFileNames();
|
|||
|
|
const fileFormats = getFileFormats();
|
|||
|
|
|
|||
|
|
console.log('=== 提交前文件信息收集 ===');
|
|||
|
|
console.log('当前文件列表:', fileList.value);
|
|||
|
|
console.log('文件列表长度:', fileList.value.length);
|
|||
|
|
console.log('文件列表详情:', fileList.value.map((file, index) => ({
|
|||
|
|
index,
|
|||
|
|
url: file.url,
|
|||
|
|
name: file.name,
|
|||
|
|
originalName: file.originalName,
|
|||
|
|
hasUrl: !!file.url,
|
|||
|
|
hasOriginalName: !!file.originalName
|
|||
|
|
})));
|
|||
|
|
|
|||
|
|
console.log('收集的文件信息:', {
|
|||
|
|
fileUrls,
|
|||
|
|
fileNames,
|
|||
|
|
fileFormats
|
|||
|
|
});
|
|||
|
|
console.log('fileName字段(不含扩展名):', fileNames);
|
|||
|
|
console.log('fileFormat字段(仅扩展名):', fileFormats);
|
|||
|
|
console.log('fileUrl字段(文件路径):', fileUrls);
|
|||
|
|
|
|||
|
|
// 检查每个文件的信息收集过程
|
|||
|
|
fileList.value.forEach((file, index) => {
|
|||
|
|
console.log(`文件${index + 1}信息收集:`, {
|
|||
|
|
url: file.url,
|
|||
|
|
name: file.name,
|
|||
|
|
originalName: file.originalName,
|
|||
|
|
getFileUrls: file.url ? file.url : '无URL',
|
|||
|
|
getFileNames: (file.originalName || file.name) ? (file.originalName || file.name || '').replace(/\.[^/.]+$/, '') : '无名称',
|
|||
|
|
getFileFormats: (file.originalName || file.name) ? (file.originalName || file.name || '').split('.').pop()?.toLowerCase() : '无格式'
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
...formData,
|
|||
|
|
fileUrl: fileUrls,
|
|||
|
|
fileName: fileNames,
|
|||
|
|
fileFormat: fileFormats, // 添加文件格式
|
|||
|
|
status: 'A' // 已发布状态
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log(`${isEdit.value ? '更新' : '新增'}成果参数:`, params);
|
|||
|
|
|
|||
|
|
await jycgSaveApi(params);
|
|||
|
|
|
|||
|
|
uni.hideLoading();
|
|||
|
|
uni.showToast({
|
|||
|
|
title: isEdit.value ? '保存成功' : '提交成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 返回列表页
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}, 1500);
|
|||
|
|
} catch (error) {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
console.error(`${isEdit.value ? '保存' : '提交'}失败:`, error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: isEdit.value ? '保存失败' : '提交失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 页面加载时接收路由参数
|
|||
|
|
onLoad(async (options: any) => {
|
|||
|
|
console.log('页面加载参数:', options);
|
|||
|
|
|
|||
|
|
// 接收教研组ID
|
|||
|
|
if (options && options.jyjbId) {
|
|||
|
|
formData.jyjbId = options.jyjbId;
|
|||
|
|
console.log('接收到的教研组ID:', formData.jyjbId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 接收成果ID(编辑模式)
|
|||
|
|
if (options && options.id) {
|
|||
|
|
isEdit.value = true;
|
|||
|
|
formData.id = options.id;
|
|||
|
|
console.log('编辑模式,成果ID:', formData.id);
|
|||
|
|
|
|||
|
|
// 加载成果详情
|
|||
|
|
await loadAchievementDetail(options.id);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 加载成果详情
|
|||
|
|
const loadAchievementDetail = async (id: string) => {
|
|||
|
|
isLoading.value = true;
|
|||
|
|
try {
|
|||
|
|
uni.showLoading({ title: '加载中...' });
|
|||
|
|
|
|||
|
|
const response: any = await jycgFindByIdApi({ id });
|
|||
|
|
|
|||
|
|
if (response && response.result) {
|
|||
|
|
const detail = response.result;
|
|||
|
|
|
|||
|
|
console.log('从后端获取的详情数据:', detail);
|
|||
|
|
console.log('文件相关字段:', {
|
|||
|
|
fileUrl: detail.fileUrl,
|
|||
|
|
fileName: detail.fileName,
|
|||
|
|
fileFormat: detail.fileFormat
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 回显基本信息
|
|||
|
|
formData.id = detail.id || '';
|
|||
|
|
formData.jycgmc = detail.jycgmc || '';
|
|||
|
|
formData.jjdywt = detail.jjdywt || '';
|
|||
|
|
formData.jjgc = detail.jjgc || '';
|
|||
|
|
formData.zzcg = detail.zzcg || '';
|
|||
|
|
formData.jsId = detail.jsId || '';
|
|||
|
|
formData.jsxm = detail.jsxm || '';
|
|||
|
|
formData.jyjbId = detail.jyjbId || '';
|
|||
|
|
|
|||
|
|
// 处理时间格式(保持完整时间戳)
|
|||
|
|
if (detail.createdTime) {
|
|||
|
|
formData.createdTime = detail.createdTime; // 保持完整时间戳
|
|||
|
|
} else {
|
|||
|
|
formData.createdTime = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
formData.remark = detail.remark || '';
|
|||
|
|
|
|||
|
|
// 回显附件
|
|||
|
|
console.log('开始回显附件,fileUrl:', detail.fileUrl);
|
|||
|
|
if (detail.fileUrl) {
|
|||
|
|
const urls = detail.fileUrl.split(',').filter(Boolean);
|
|||
|
|
const names = detail.fileName ? detail.fileName.split(',').filter(Boolean) : [];
|
|||
|
|
const formats = detail.fileFormat ? detail.fileFormat.split(',').filter(Boolean) : [];
|
|||
|
|
|
|||
|
|
console.log('解析的文件数据:', {
|
|||
|
|
urls,
|
|||
|
|
names,
|
|||
|
|
formats
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 清空现有文件列表
|
|||
|
|
fileList.value = [];
|
|||
|
|
|
|||
|
|
// 回显所有文件
|
|||
|
|
urls.forEach((url: string, index: number) => {
|
|||
|
|
const fileName = names[index] || url.split('/').pop() || '';
|
|||
|
|
const fileFormat = formats[index] || fileName.split('.').pop()?.toLowerCase() || '';
|
|||
|
|
// 组合完整的文件名用于显示
|
|||
|
|
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
|
|||
|
|
|
|||
|
|
console.log(`文件${index + 1}:`, {
|
|||
|
|
url: url.trim(),
|
|||
|
|
fileName,
|
|||
|
|
fileFormat,
|
|||
|
|
fullFileName
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
fileList.value.push({
|
|||
|
|
url: url.trim(),
|
|||
|
|
name: fullFileName,
|
|||
|
|
originalName: fullFileName, // 回显时使用完整文件名
|
|||
|
|
type: 'document', // 默认为文档类型
|
|||
|
|
extension: fileFormat
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('回显后的文件列表:', fileList.value);
|
|||
|
|
} else {
|
|||
|
|
console.log('没有文件需要回显');
|
|||
|
|
fileList.value = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('成果详情加载成功:', formData);
|
|||
|
|
console.log('附件回显:', { fileList: fileList.value });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
uni.hideLoading();
|
|||
|
|
} catch (error) {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
console.error('加载成果详情失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '加载失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 加载失败返回列表页
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}, 1500);
|
|||
|
|
} finally {
|
|||
|
|
isLoading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 页面加载时初始化
|
|||
|
|
onMounted(() => {
|
|||
|
|
// 编辑模式下不需要设置默认值(会从详情中获取)
|
|||
|
|
if (isEdit.value) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 新增模式:设置默认日期为今天
|
|||
|
|
const today = new Date();
|
|||
|
|
const year = today.getFullYear();
|
|||
|
|
const month = String(today.getMonth() + 1).padStart(2, '0');
|
|||
|
|
const day = String(today.getDate()).padStart(2, '0');
|
|||
|
|
formData.createdTime = `${year}-${month}-${day}`;
|
|||
|
|
|
|||
|
|
// 新增模式:设置提交人信息
|
|||
|
|
console.log('教师信息 getJs:', getJs);
|
|||
|
|
formData.jsId = getJs.id || '';
|
|||
|
|
formData.jsxm = getJs.jsxm || '';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.achievement-form-page {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
height: 100vh;
|
|||
|
|
background-color: #f5f7fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 表单滚动区域
|
|||
|
|
.form-scroll-view {
|
|||
|
|
flex: 1;
|
|||
|
|
height: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-container {
|
|||
|
|
padding: 20px 16px 100px 16px; // 底部留出空间给固定按钮
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 表单区域
|
|||
|
|
.form-section {
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
|
|||
|
|
.section-label {
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 4px;
|
|||
|
|
|
|||
|
|
.label-text {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #2d3748;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.required-star {
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #ef4444;
|
|||
|
|
font-weight: bold;
|
|||
|
|
line-height: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 日期输入
|
|||
|
|
.date-input-wrapper {
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e2e8f0;
|
|||
|
|
padding: 12px 16px;
|
|||
|
|
|
|||
|
|
.date-picker {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只读输入框
|
|||
|
|
.readonly-input-wrapper {
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e2e8f0;
|
|||
|
|
padding: 12px 16px;
|
|||
|
|
|
|||
|
|
.readonly-text {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 简单文本区域
|
|||
|
|
.simple-text-wrapper {
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e2e8f0;
|
|||
|
|
overflow: hidden;
|
|||
|
|
|
|||
|
|
.simple-textarea {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 120px;
|
|||
|
|
padding: 16px;
|
|||
|
|
border: none;
|
|||
|
|
outline: none;
|
|||
|
|
font-size: 14px;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
color: #2d3748;
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
resize: none;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文件上传
|
|||
|
|
.file-upload-wrapper {
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 底部固定操作按钮
|
|||
|
|
.bottom-actions {
|
|||
|
|
position: fixed;
|
|||
|
|
bottom: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
background-color: #fff;
|
|||
|
|
border-top: 1px solid #e8ecf1;
|
|||
|
|
padding: 12px 16px;
|
|||
|
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
|
|||
|
|
z-index: 1000;
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
|
|||
|
|
.action-btn {
|
|||
|
|
flex: 1;
|
|||
|
|
height: 44px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
|
|||
|
|
.btn-text {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #ffffff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
transform: translateY(1px);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cancel-btn {
|
|||
|
|
background-color: #6b7280;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background-color: #4b5563;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.submit-btn {
|
|||
|
|
background-color: #10b981;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background-color: #059669;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 深度选择器样式
|
|||
|
|
:deep(.uni-datetime-picker) {
|
|||
|
|
width: 100%;
|
|||
|
|
|
|||
|
|
.uni-date {
|
|||
|
|
background-color: transparent;
|
|||
|
|
border: none;
|
|||
|
|
padding: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uni-date__x-input {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #2d3748;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文件上传遮罩层样式
|
|||
|
|
.upload-mask {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
z-index: 9999;
|
|||
|
|
|
|||
|
|
.upload-content {
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 40px 32px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
min-width: 280px;
|
|||
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|||
|
|
|
|||
|
|
.upload-icon {
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
|
|||
|
|
.icon-text {
|
|||
|
|
font-size: 48px;
|
|||
|
|
animation: bounce 1.5s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-title {
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #2d3748;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-status {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #718096;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-progress {
|
|||
|
|
width: 100%;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
|
|||
|
|
.progress-bar {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 8px;
|
|||
|
|
background-color: #e2e8f0;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
|
|||
|
|
.progress-fill {
|
|||
|
|
height: 100%;
|
|||
|
|
background: linear-gradient(90deg, #0ea5e9 0%, #0284c7 100%);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: width 0.3s ease;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.progress-text {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #0ea5e9;
|
|||
|
|
font-weight: 600;
|
|||
|
|
text-align: center;
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-tip {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #a0aec0;
|
|||
|
|
text-align: center;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交按钮禁用状态
|
|||
|
|
.action-btn.disabled {
|
|||
|
|
opacity: 0.5;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 弹跳动画
|
|||
|
|
@keyframes bounce {
|
|||
|
|
0%, 100% {
|
|||
|
|
transform: translateY(0);
|
|||
|
|
}
|
|||
|
|
50% {
|
|||
|
|
transform: translateY(-8px);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|