Commit 8cbb38c2 by Neo Turing

feat: 更新LAC文件列表视图和文件审核组件

parent 4ff0ebd7
...@@ -101,7 +101,11 @@ ...@@ -101,7 +101,11 @@
</n-descriptions-item> </n-descriptions-item>
</n-descriptions> </n-descriptions>
<div class="modal-footer"> <div class="modal-footer">
<n-button type="primary" @click="downloadSingleFile(currentFile!)"> <n-button
type="primary"
@click="currentFile && downloadSingleFile(currentFile)"
:disabled="!currentFile"
>
下载文件 下载文件
</n-button> </n-button>
<n-button @click="showPreviewModal = false"> <n-button @click="showPreviewModal = false">
...@@ -115,7 +119,7 @@ ...@@ -115,7 +119,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { h, ref, computed, resolveComponent, watch, withDefaults, onBeforeUnmount } from 'vue'; import { h, ref, computed, resolveComponent, watch, withDefaults, onBeforeUnmount } from 'vue';
import type { DataTableColumns } from 'naive-ui'; // import type { DataTableColumns } from 'naive-ui';
interface FileItem { interface FileItem {
Kvid: string; Kvid: string;
...@@ -650,8 +654,19 @@ function formatFileSize(size: number): string { ...@@ -650,8 +654,19 @@ function formatFileSize(size: number): string {
// 关闭并传递文件数据 // 关闭并传递文件数据
function closeWithFileData() { function closeWithFileData() {
emit('update-files', fileData.value); emit('update-files', fileData.value);
emit('close'); emit('close', fileData.value); // 在关闭时传递当前的文件数据
}
// 获取当前文件列表的方法
function getCurrentFileList() {
return fileData.value;
} }
// 暴露方法给父组件使用
defineExpose({
getCurrentFileList,
fileData
});
</script> </script>
<style scoped> <style scoped>
......
<script setup lang="ts"> <script setup lang="ts">
import { computed, defineEmits, defineProps, h,reactive, onMounted,withDefaults, ref, resolveComponent, watch } from 'vue'; import { computed, defineEmits, defineProps, h,reactive, onMounted,withDefaults, ref, resolveComponent, watch } from 'vue';
import FileListView from './fileListView.vue'; // '/codes/fileListView.vue'; import FileListView from './fileListView.vue';//'/codes/Vue3/Lab/FileListView.vue'; //'./fileListView.vue';
// 主体框架内置的第三方方法通过window对象暴露、供外部组件使用 // 主体框架内置的第三方方法通过window对象暴露、供外部组件使用
const axios = (window as any).$axios; const axios = (window as any).$axios;
...@@ -12,18 +12,6 @@ const message = (window as any).$message; ...@@ -12,18 +12,6 @@ const message = (window as any).$message;
const dialog = (window as any).$dialog; const dialog = (window as any).$dialog;
// 通过计算属性确定当前的主体色 // 通过计算属性确定当前的主体色
const isDarkMode = computed(() => themeStore?.darkMode || false); const isDarkMode = computed(() => themeStore?.darkMode || false);
// 根据主题计算文字颜色
const textColor = computed(() => (isDarkMode.value ? 'rgba(255, 255, 255, 0.85)' : 'rgba(0, 0, 0, 0.85)'));
const legendTextColor = computed(() => (isDarkMode.value ? 'rgba(255, 255, 255, 0.65)' : 'rgba(0, 0, 0, 0.65)'));
const borderColor = computed(() => (isDarkMode.value ? '#303030' : '#ffffff'));
// CSS变量
const cssVars = computed(() => ({
'--text-color': textColor.value,
'--bg-color': isDarkMode.value ? '#1f1f1f' : '#f5f5f5',
'--panel-bg': isDarkMode.value ? '#262626' : '#ffffff',
'--card-bg': isDarkMode.value ? '#303030' : '#ffffff',
'--border-color': isDarkMode.value ? '#434343' : '#e8e8e8'
}));
// 修改FileItem接口,但保留原有字段 // 修改FileItem接口,但保留原有字段
interface FileItem { interface FileItem {
...@@ -37,7 +25,7 @@ interface FileItem { ...@@ -37,7 +25,7 @@ interface FileItem {
Description?: string; Description?: string;
StatusType?: string; StatusType?: string;
ReportKvid?: string; ReportKvid?: string;
[key: string]: any; // 添加索引签名,以支持可能出现的其他字段 [key: string]: any;
} }
// 定义Item对象的接口 // 定义Item对象的接口
...@@ -50,7 +38,7 @@ interface ItemProps { ...@@ -50,7 +38,7 @@ interface ItemProps {
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
item?: ItemProps; item?: ItemProps;
active?: boolean; // 新增:是否激活状态,用于按需加载 active?: boolean;
}>(), }>(),
{ {
item: () => ({}), item: () => ({}),
...@@ -59,30 +47,82 @@ const props = withDefaults( ...@@ -59,30 +47,82 @@ const props = withDefaults(
); );
// 状态变量 // 状态变量
const titleOptions = ref<any[]>([]); const titleOptions = ref<any[]>([]);
const selectedStandard = reactive<any>({}); // 存储选中的完整对象 /**
const fileData = ref<FileItem[]>([]); // 改回ref,避免过度响应式 * 🔸 选中的标准对象(可能未充分使用)
* @description 存储选中文件类型的完整对象信息
* @usage ⚠️ 在handleTitleChange中填充,但后续业务逻辑中使用频率较低
*/
const selectedStandard = reactive<any>({});
/**
* 文件审核数据列表
* @description 存储当前显示的所有文件审核记录
*/
const fileData = ref<FileItem[]>([]);
/**
* 加载状态标识
* @description 控制各种异步操作的加载状态显示
*/
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
// 存储API返回的详细数据 /**
* 存储从props传入的详细数据
* @description 保存父组件传递的item数据的响应式副本
*/
const detailedData = reactive<ItemProps>({}); const detailedData = reactive<ItemProps>({});
// 控制fileListView模态框显示
/**
* 控制FileListView模态框显示状态
* @description 控制文件管理模态框的开关
*/
const showFileListView = ref<boolean>(false); const showFileListView = ref<boolean>(false);
// 存储当前选中的文件行数据
/**
* 当前选中的文件行数据
* @description 存储用户点击查看的文件行完整数据
*/
const currentFileRow = ref<FileItem | null>(null); const currentFileRow = ref<FileItem | null>(null);
// 使用ref来避免计算属性的循环更新问题 /**
* FileListView组件引用
* @description 用于调用子组件的方法和访问子组件数据
*/
const fileListViewRef = ref<any>(null);
/**
* 🔸 选中的标准Kvid(可能未充分使用)
* @description 存储当前选择的文件类型ID
* @usage ⚠️ 主要在handleTitleChange和fetchFileDetails中使用
*/
const selectedStandardKvid = ref<string>(''); const selectedStandardKvid = ref<string>('');
// 计算是否有数据存在,用于控制NSelect状态 /**
* 计算是否存在文件数据
* @description 用于控制文件类型选择器的禁用状态和清空按钮的显示
* @returns {boolean} 是否有现有的文件审核数据
* @usage 控制NSelect的disabled状态和清空按钮的显示条件
*/
const hasExistingData = computed(() => { const hasExistingData = computed(() => {
return fileData.value.length > 0; return fileData.value.length > 0;
}); });
// 根据选择的标题筛选数据 - 优化性能 /**
* 🔸 过滤后的数据(当前无过滤逻辑)
* @description 原计划用于数据过滤,当前直接返回所有数据
* @returns {FileItem[]} 文件数据数组
* @usage ⚠️ 绑定到NDataTable的:data属性,但无实际过滤功能
*/
const filteredData = computed(() => { const filteredData = computed(() => {
return fileData.value; return fileData.value;
}); });
// 辅助函数:将API的StatusType映射为显示状态 /**
* 状态类型映射函数
* @description 将API返回的StatusType字符串映射为中文显示状态
* @param {string} statusType - API返回的状态类型字符串
* @returns {string} 对应的中文状态显示名称
* @usage 在数据处理和状态显示时使用
*/
const mapStatusType = (statusType: string) => { const mapStatusType = (statusType: string) => {
const statusMap: { [key: string]: string } = { const statusMap: { [key: string]: string } = {
Unsupported: '草稿', Unsupported: '草稿',
...@@ -96,21 +136,33 @@ const mapStatusType = (statusType: string) => { ...@@ -96,21 +136,33 @@ const mapStatusType = (statusType: string) => {
return statusMap[statusType] || '草稿'; return statusMap[statusType] || '草稿';
}; };
// 辅助函数:根据状态获取颜色 /**
* 状态颜色获取函数
* @description 根据状态类型返回对应的UI显示颜色
* @param {string} status - 状态类型字符串
* @returns {string} 对应状态的十六进制颜色值
* @usage 在表格和标签组件中设置状态颜色
*/
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
const statusColorMap: { [key: string]: string } = { const statusColorMap: { [key: string]: string } = {
BeforeTest: '#f56a00', // 橙色 - 待处理 BeforeTest: '#f56a00',
Asigning: '#1890ff', // 蓝色 - 进行中 Asigning: '#1890ff',
Testing: '#722ed1', // 紫色 - 重要评审 Testing: '#722ed1',
BeforeReview: '#eb2f96', // 粉红色 - 测试阶段 BeforeReview: '#eb2f96',
TestFinished: '#13c2c2', // 青色 - 即将完成 TestFinished: '#13c2c2',
TestCollected: '#52c41a' // 绿色 - 已完成 TestCollected: '#52c41a'
}; };
return statusColorMap[status] || '#666'; return statusColorMap[status] || '#666';
}; };
// 状态映射函数(为模态框标题使用) /**
const getStatusInfo = (status: number) => { * 状态信息获取函数
* @description 根据数字状态码返回包含标签、颜色和类型的状态信息对象
* @param {any} status - 数字状态码
* @returns {object} 包含label、color、type的状态信息对象
* @usage 主要用于模态框标题和状态标签显示
*/
const getStatusInfo = (status: any) => {
const statusMap = { const statusMap = {
0: { label: '草稿', color: '#909399', type: 'default' }, 0: { label: '草稿', color: '#909399', type: 'default' },
100: { label: '待上传', color: '#E6A23C', type: 'warning' }, 100: { label: '待上传', color: '#E6A23C', type: 'warning' },
...@@ -118,7 +170,14 @@ const getStatusInfo = (status: number) => { ...@@ -118,7 +170,14 @@ const getStatusInfo = (status: number) => {
200: { label: '审核中', color: '#F56C6C', type: 'error' }, 200: { label: '审核中', color: '#F56C6C', type: 'error' },
250: { label: '待复核', color: '#722ED1', type: 'warning' }, 250: { label: '待复核', color: '#722ED1', type: 'warning' },
300: { label: '不通过', color: '#FF7A45', type: 'error' }, 300: { label: '不通过', color: '#FF7A45', type: 'error' },
2147483647: { label: '已通过', color: '#67C23A', type: 'success' } 2147483647: { label: '已通过', color: '#67C23A', type: 'success' },
Unsupported: { label: '草稿', color: '#909399', type: 'default' },
BeforeTest: { label: '待上传', color: '#E6A23C', type: 'warning' },
Asigning: { label: '已上传', color: '#409EFF', type: 'info' },
Testing: { label: '审核中', color: '#F56C6C', type: 'error' },
BeforeReview: { label: '待复核', color: '#722ED1', type: 'warning' },
TestFinished: { label: '不通过', color: '#FF7A45', type: 'error' },
TestCollected: { label: '已通过', color: '#67C23A', type: 'success' }
}; };
return statusMap[status as keyof typeof statusMap] || { return statusMap[status as keyof typeof statusMap] || {
...@@ -128,49 +187,52 @@ const getStatusInfo = (status: number) => { ...@@ -128,49 +187,52 @@ const getStatusInfo = (status: number) => {
}; };
}; };
// 计算当前行的只读状态 /**
* 🔸 计算当前行的只读状态(可能未使用)
* @description 判断当前选中行是否为只读状态(状态值大于150)
* @returns {boolean} 是否为只读状态
* @usage ⚠️ 在模板中可能被引用,但实际业务逻辑中可能不需要
*/
const isCurrentRowReadonly = computed(() => { const isCurrentRowReadonly = computed(() => {
return currentFileRow.value && currentFileRow.value.Status > 150; return currentFileRow.value && currentFileRow.value.Status > 150;
}); });
// 获取文件类型数据 /**
* 获取文件类型选项数据
* @description 从标准实体接口查询文件审核类型数据,用于下拉选择器
* @returns {Promise<void>} 无返回值,结果存储在titleOptions.value中
* @usage 在组件初始化和激活时调用,用于填充文件类型下拉选择器
*/
async function fetchTitleOptions() { async function fetchTitleOptions() {
console.log('🔄 FileReview: 开始加载文件类型数据');
try { try {
loading.value = true; loading.value = true;
// 调用标准实体查询接口
const response = await axios.post('/Restful/Kivii.Standards.Entities.Standard/Query.json?Type=文件审核'); const response = await axios.post('/Restful/Kivii.Standards.Entities.Standard/Query.json?Type=文件审核');
// console.log('完整响应数据:', response.data);
// 添加数据存在性检查
if (response.data && response.data.Results) { if (response.data && response.data.Results) {
// 将API返回的数据转换为NSelect需要的格式
titleOptions.value = response.data.Results.map((item: any, index: number) => ({ titleOptions.value = response.data.Results.map((item: any, index: number) => ({
label: item.Title, label: item.Title,
value: item.Kvid, value: item.Kvid,
// 可以选择以下任一方式存储完整对象信息:
// 方式1:直接将完整对象作为额外属性
fullObject: item fullObject: item
})); }));
} else { } else {
// console.error('无效的数据格式:', response.data);
message.error('数据格式不正确'); message.error('数据格式不正确');
} }
} catch (error) { } catch (error) {
message.error('获取文件类型失败'); message.error('获取文件类型失败');
// console.error('获取文件类型失败:', error);
} finally { } finally {
loading.value = false; loading.value = false;
} }
} }
// 获取文件详情数据 /**
* 🔸 获取文件详情数据(可能未使用)
* @description 基于选中的标准Kvid创建文件审核数据记录
* @returns {Promise<void>} 无返回值,结果存储在fileData.value中
* @usage ⚠️ 当前代码中通过handleTitleChange调用,但实际业务流程可能不使用此方法
*/
async function fetchFileDetails() { async function fetchFileDetails() {
if (!selectedStandardKvid.value) return; if (!selectedStandardKvid.value) return;
// 如果数据已存在,直接返回(UI已有提示)
if(fileData.value.length > 0) { if(fileData.value.length > 0) {
return; return;
} }
...@@ -194,10 +256,7 @@ async function fetchFileDetails() { ...@@ -194,10 +256,7 @@ async function fetchFileDetails() {
const resp=await axios.post('/Restful/Kivii.Lims.Entities.ReportItem/Detection.json', data); const resp=await axios.post('/Restful/Kivii.Lims.Entities.ReportItem/Detection.json', data);
if(resp.data&&resp.data.Results){ if(resp.data&&resp.data.Results){
fileData.value = resp.data.Results.map((item: any) => ({ fileData.value = resp.data.Results.map((item: any) => ({
// 直接使用API字段名
...item, ...item,
// 保留转换后的自定义字段
Status: mapStatusType(item.StatusType), Status: mapStatusType(item.StatusType),
StatusColor: getStatusColor(item.StatusType) StatusColor: getStatusColor(item.StatusType)
})); }));
...@@ -209,42 +268,39 @@ async function fetchFileDetails() { ...@@ -209,42 +268,39 @@ async function fetchFileDetails() {
} }
} catch (error) { } catch (error) {
message.error('获取文件详情失败'); message.error('获取文件详情失败');
// console.error(error);
} finally { } finally {
loading.value = false; loading.value = false;
} }
} }
// 使用报告Kvid获取数据 /**
* 根据报告Kvid获取文件审核数据
* @description 使用报告ID查询已存在的文件审核数据记录
* @param {string} reportKvid - 报告的唯一标识符
* @returns {Promise<void>} 无返回值,结果存储在fileData.value中
* @usage 在组件初始化和props变化时调用,获取现有的文件审核数据
*/
async function fetchDataByReportKvid(reportKvid: string) { async function fetchDataByReportKvid(reportKvid: string) {
if (!reportKvid) return; if (!reportKvid) return;
if (reportKvid === detailedData.Kvid && fileData.value.length > 0) { if (reportKvid === detailedData.Kvid && fileData.value.length > 0) {
console.log('🔄 FileReview: 跳过重复请求,使用已缓存的数据');
return; return;
} }
console.log('🔄 FileReview: 开始获取文件审核数据,ReportKvid:', reportKvid);
try { try {
loading.value = true; loading.value = true;
const response = await axios.post('/Restful/Kivii.Lims.Entities.ReportItem/QueryEx.json', { const response = await axios.get('/Restful/Kivii.Lims.Entities.ReportItem/QueryEx.json', {
params: {
ReportKvid: reportKvid, ReportKvid: reportKvid,
OrderBy:'SortId,SortIdEx,Kvid', OrderBy:'SortId,SortIdEx,Kvid',
WorkGroupName:'文件审核' WorkGroupName:'文件审核'
}
}); });
if (response.data && response.data.Results&&response.data.Results.length>0) { if (response.data && response.data.Results&&response.data.Results.length>0) {
// 直接使用API返回的数据
// fileData.value = response.data.Results;
// fileData.value.forEach((item: any) => {
// item.StatusType = mapStatusType(item.StatusType);
// });
fileData.value = response.data.Results.map((item: any) => ({ fileData.value = response.data.Results.map((item: any) => ({
// 直接使用API字段名
...item, ...item,
// 保留转换后的自定义字段
Status: mapStatusType(item.StatusType), Status: mapStatusType(item.StatusType),
StatusColor: getStatusColor(item.StatusType) StatusColor: getStatusColor(item.StatusType)
})); }));
...@@ -253,25 +309,27 @@ async function fetchDataByReportKvid(reportKvid: string) { ...@@ -253,25 +309,27 @@ async function fetchDataByReportKvid(reportKvid: string) {
message.info('暂无文件审核数据'); message.info('暂无文件审核数据');
} }
} catch (error) { } catch (error) {
// console.error('获取文件审核数据失败:', error);
message.error('获取文件审核数据失败,请稍后重试'); message.error('获取文件审核数据失败,请稍后重试');
} finally { } finally {
loading.value = false; loading.value = false;
} }
} }
// 处理标题选择变化 /**
* 🔸 处理文件类型选择变化(可能未使用)
* @description 当用户选择不同的文件类型时触发,同步选中值并获取详情
* @param {string} value - 选中的文件类型Kvid
* @returns {void} 无返回值
* @usage ⚠️ 绑定到NSelect的@update:value事件,但实际业务可能不需要此功能
*/
function handleTitleChange(value: string) { function handleTitleChange(value: string) {
selectedStandardKvid.value = value; // 同步选中值 selectedStandardKvid.value = value;
// 获取选中项的完整对象信息
const selectedOption = titleOptions.value.find(option => option.value === value); const selectedOption = titleOptions.value.find(option => option.value === value);
if (selectedOption) { if (selectedOption) {
// 清空现有属性
Object.keys(selectedStandard).forEach(key => { Object.keys(selectedStandard).forEach(key => {
delete selectedStandard[key]; delete selectedStandard[key];
}); });
// 添加新属性
Object.keys(selectedOption.fullObject).forEach(key => { Object.keys(selectedOption.fullObject).forEach(key => {
selectedStandard[key] = selectedOption.fullObject[key]; selectedStandard[key] = selectedOption.fullObject[key];
}); });
...@@ -294,25 +352,20 @@ watch( ...@@ -294,25 +352,20 @@ watch(
); );
// 页面加载时按需获取数据
onMounted(() => { onMounted(() => {
// 只有在激活状态下才加载数据
if (props.active) { if (props.active) {
fetchTitleOptions(); fetchTitleOptions();
// 如果有kvid,获取数据
if (props.item && props.item.Kvid && props.item.Kvid !== detailedData.Kvid) { if (props.item && props.item.Kvid && props.item.Kvid !== detailedData.Kvid) {
fetchDataByReportKvid(props.item.Kvid); fetchDataByReportKvid(props.item.Kvid);
} }
} }
}); });
// 监听active属性变化,实现按需加载
watch( watch(
() => props.active, () => props.active,
(newActive) => { (newActive) => {
if (newActive && titleOptions.value.length === 0) { if (newActive && titleOptions.value.length === 0) {
// 第一次激活且数据未加载时,加载数据
fetchTitleOptions(); fetchTitleOptions();
if (props.item && props.item.Kvid && props.item.Kvid !== detailedData.Kvid) { if (props.item && props.item.Kvid && props.item.Kvid !== detailedData.Kvid) {
...@@ -323,25 +376,17 @@ watch( ...@@ -323,25 +376,17 @@ watch(
{ immediate: true } { immediate: true }
); );
// 存储最新的文件列表数据 /**
* 🔸 最新文件列表数据(备用存储)
* @description 备用存储FileListView的实时文件数据,主要通过关闭事件获取数据
* @usage ⚠️ 作为数据同步的备用方案,当前主要逻辑已转移到handleModalClose
*/
const latestFileListData = ref<any[]>([]); const latestFileListData = ref<any[]>([]);
// 监听FileListView模态框的显示状态 /**
watch( * 表格列配置定义
() => showFileListView.value, * @description 定义NDataTable的列结构、渲染方式和操作按钮
(newValue, oldValue) => { */
// 当模态框从显示变为隐藏时
if (oldValue === true && newValue === false) {
console.log('FileListView模态框已关闭,最新文件数据:', latestFileListData.value);
// 这里可以添加您需要的其他处理逻辑
// 例如:调用API保存数据、更新其他组件状态等
handleFileListClosed(latestFileListData.value);
}
}
);
// 表格列定义
const columns = ref<any[]>([ const columns = ref<any[]>([
{ {
title: '序号', title: '序号',
...@@ -442,20 +487,26 @@ const columns = ref<any[]>([ ...@@ -442,20 +487,26 @@ const columns = ref<any[]>([
} }
]); ]);
// 查看文件 /**
* 查看文件详情
* @description 打开文件列表模态框,显示指定行的文件详情和管理界面
* @param {FileItem} row - 选中的文件行数据
* @returns {void} 无返回值
* @usage 绑定到表格操作列的"附件"按钮点击事件
*/
function viewFile(row: FileItem) { function viewFile(row: FileItem) {
// 将整个row数据存储到currentFileRow中
currentFileRow.value = row; currentFileRow.value = row;
console.log('传递给FileListView的数据:', row);
// 清空之前的文件列表数据
latestFileListData.value = []; latestFileListData.value = [];
// 显示模态框
showFileListView.value = true; showFileListView.value = true;
} }
// 封装删除实体的方法 /**
* 批量删除实体数据
* @description 通用的删除方法,支持批量删除多个文件审核记录
* @param {string[]} kvids - 要删除的记录ID数组
* @returns {Promise<boolean>} 删除操作是否成功
* @usage 被deleteFileRecord和clearAllFileData方法调用
*/
async function deleteEntities(kvids: string[]) { async function deleteEntities(kvids: string[]) {
if (!kvids || kvids.length === 0) { if (!kvids || kvids.length === 0) {
message.error('请选择要删除的项目'); message.error('请选择要删除的项目');
...@@ -477,7 +528,6 @@ async function deleteEntities(kvids: string[]) { ...@@ -477,7 +528,6 @@ async function deleteEntities(kvids: string[]) {
return false; return false;
} }
} catch (error) { } catch (error) {
console.error('删除文件失败:', error);
message.error('删除文件失败,请稍后重试'); message.error('删除文件失败,请稍后重试');
return false; return false;
} finally { } finally {
...@@ -485,23 +535,32 @@ async function deleteEntities(kvids: string[]) { ...@@ -485,23 +535,32 @@ async function deleteEntities(kvids: string[]) {
} }
} }
// 删除文件记录 /**
* 删除单个文件记录
* @description 删除表格中指定的单个文件审核记录
* @param {FileItem} row - 要删除的文件行数据
* @returns {Promise<void>} 无返回值
* @usage 绑定到表格操作列的"删除"按钮确认回调
*/
async function deleteFileRecord(row: FileItem) { async function deleteFileRecord(row: FileItem) {
if (!row.Kvid) { if (!row.Kvid) {
message.error('无效的文件ID'); message.error('无效的文件ID');
return; return;
} }
// 调用删除接口
const success = await deleteEntities([row.Kvid]); const success = await deleteEntities([row.Kvid]);
if (success) { if (success) {
// 从列表中移除
fileData.value = fileData.value.filter(item => item.Kvid !== row.Kvid); fileData.value = fileData.value.filter(item => item.Kvid !== row.Kvid);
} }
} }
// 清空所有文件数据 /**
* 清空所有文件数据
* @description 删除当前列表中的所有文件审核记录,并重置相关状态
* @returns {void} 无返回值
* @usage 绑定到"清空数据"按钮的点击事件
*/
function clearAllFileData() { function clearAllFileData() {
dialog.warning({ dialog.warning({
title: '确认删除', title: '确认删除',
...@@ -514,7 +573,6 @@ function clearAllFileData() { ...@@ -514,7 +573,6 @@ function clearAllFileData() {
return; return;
} }
// 收集所有Kvid
const kvids = fileData.value.map(item => item.Kvid).filter(kvid => kvid); const kvids = fileData.value.map(item => item.Kvid).filter(kvid => kvid);
if (kvids.length === 0) { if (kvids.length === 0) {
...@@ -522,13 +580,11 @@ function clearAllFileData() { ...@@ -522,13 +580,11 @@ function clearAllFileData() {
return; return;
} }
// 调用删除接口
const success = await deleteEntities(kvids); const success = await deleteEntities(kvids);
if (success) { if (success) {
fileData.value = []; fileData.value = [];
selectedStandardKvid.value = ''; selectedStandardKvid.value = '';
// 清空selectedStandard对象
Object.keys(selectedStandard).forEach(key => { Object.keys(selectedStandard).forEach(key => {
delete selectedStandard[key]; delete selectedStandard[key];
}); });
...@@ -537,44 +593,83 @@ function clearAllFileData() { ...@@ -537,44 +593,83 @@ function clearAllFileData() {
}); });
} }
// 处理模态框关闭 /**
* 防重复调用标志
* @description 防止模态框关闭事件重复触发数据处理逻辑
*/
const isProcessingClose = ref(false);
/**
* 处理模态框关闭事件
* @description 模态框关闭后获取FileListView中的文件数据并进行状态更新处理
* @returns {void} 无返回值
* @usage 绑定到NModal的@after-leave事件,防重复调用机制
*/
function handleModalClose() { function handleModalClose() {
if (isProcessingClose.value) {
return;
}
try {
isProcessingClose.value = true;
if (fileListViewRef.value && currentFileRow.value) {
const currentFiles = fileListViewRef.value.getCurrentFileList?.() || [];
handleFileListClosed(currentFiles);
}
currentFileRow.value = null; currentFileRow.value = null;
} finally {
setTimeout(() => {
isProcessingClose.value = false;
}, 100);
}
} }
// 处理FileListView实时文件列表更新(实时同步数据) /**
function handleFileListUpdate(files: any[]) { * 🔸 处理FileListView组件关闭事件(功能简化)
console.log('FileReview: 接收到文件列表实时更新', files); * @description 简单关闭模态框,实际数据处理由handleModalClose负责
* @param {any[]} files - 可选的文件数据参数(当前未使用)
// 存储最新的文件列表数据,用于界面关闭时处理 * @returns {void} 无返回值
latestFileListData.value = [...files]; // 深拷贝确保数据独立性 * @usage ⚠️ 绑定到FileListView的@close事件,但主要逻辑已转移到handleModalClose
*/
function handleFileListViewClose(files?: any[]) {
showFileListView.value = false;
}
console.log(`📁 实时数据同步: ${files.length} 个文件`); /**
* 🔸 处理FileListView实时文件更新(备用方法)
* @description 实时同步FileListView中的文件数据变化(主要通过关闭事件获取数据)
* @param {any[]} files - 更新的文件数据数组
* @returns {void} 无返回值
* @usage ⚠️ 绑定到FileListView的@update-files事件,作为备用数据同步方式
*/
function handleFileListUpdate(files: any[]) {
latestFileListData.value = [...files];
} }
// 处理FileListView界面关闭后的数据处理 /**
* 处理FileListView关闭后的数据处理
* @description 根据FileListView中的文件数量自动更新文件审核记录的状态
* @param {any[]} files - 从FileListView获取的文件数据数组
* @returns {Promise<void>} 无返回值
* @usage 被handleModalClose调用,根据文件数量自动设置状态(0个文件=待上传,>0个文件=已上传)
*/
async function handleFileListClosed(files: any[]) { async function handleFileListClosed(files: any[]) {
console.log('🔒 FileListView界面已关闭,开始处理文件数据集合');
// 检查当前行状态,如果超过150则直接返回
if (!currentFileRow.value || currentFileRow.value.Status > 150) { if (!currentFileRow.value || currentFileRow.value.Status > 150) {
console.log('⚠️ 当前状态不允许更新,Status:', currentFileRow.value?.Status);
return; return;
} }
const fileCount = files.length; const fileCount = files.length;
try { try {
// 根据文件数量确定状态值
const newStatus = fileCount <= 0 ? 100 : 150; const newStatus = fileCount <= 0 ? 100 : 150;
if(currentFileRow.value.Status===newStatus){ const statusInfo=getStatusInfo(newStatus);
// message.info('文件状态未发生变化,无需更新'); if(currentFileRow.value.Status===statusInfo.label){
return; return;
} }
console.log(`📋 准备更新状态: 文件数量=${fileCount}, 新状态=${newStatus}`);
// 调用更新接口 const response = await axios.post('/Restful/Kivii.Lims.Entities.ReportItem/UpdateEx2.json', {
const response = await axios.post('/Restful/Kivii.Lims.Entities.ReportItem/Update.json', {
Item: { Item: {
Kvid: currentFileRow.value.Kvid, Kvid: currentFileRow.value.Kvid,
Status: newStatus Status: newStatus
...@@ -582,39 +677,21 @@ async function handleFileListClosed(files: any[]) { ...@@ -582,39 +677,21 @@ async function handleFileListClosed(files: any[]) {
}); });
if (response.data && response.data.Results && response.data.Results.length > 0) { if (response.data && response.data.Results && response.data.Results.length > 0) {
// 更新成功,同步本地状态
currentFileRow.value.Status = newStatus;
// 同时更新状态显示文本和颜色
const statusText = newStatus === 100 ? '待上传' : '已上传'; const statusText = newStatus === 100 ? '待上传' : '已上传';
const statusColor = newStatus === 100 ? '#E6A23C' : '#409EFF';
currentFileRow.value.Status = statusText;
currentFileRow.value.StatusColor = statusColor;
// 在fileData数组中找到对应项并更新 const itemIndex = fileData.value.findIndex(item => item.Kvid === response.data.Results[0].Kvid);
const itemIndex = fileData.value.findIndex(item => item.Kvid === currentFileRow.value?.Kvid);
if (itemIndex !== -1) { if (itemIndex !== -1) {
fileData.value[itemIndex].Status = statusText; fileData.value[itemIndex].Status = mapStatusType(response.data.Results[0].StatusType);
fileData.value[itemIndex].StatusColor = statusColor; fileData.value[itemIndex].StatusColor = getStatusColor(response.data.Results[0].StatusType);
} }
message.success(`状态更新成功:${statusText} (${fileCount}个文件)`); message.success(`状态更新成功:${statusText} (${fileCount}个文件)`);
console.log('✅ 状态更新成功:', {
记录ID: currentFileRow.value.Kvid,
文件数量: fileCount,
新状态: newStatus,
状态描述: statusText
});
} else { } else {
message.error('状态更新失败:' + (response.data?.Message || '未知错误')); message.error('状态更新失败:' + (response.data?.Message || '未知错误'));
console.error('❌ 状态更新失败:', response.data);
} }
} catch (error) { } catch (error) {
message.error('状态更新失败,请稍后重试'); message.error('状态更新失败,请稍后重试');
console.error('❌ 状态更新异常:', error);
} }
} }
</script> </script>
...@@ -695,11 +772,14 @@ async function handleFileListClosed(files: any[]) { ...@@ -695,11 +772,14 @@ async function handleFileListClosed(files: any[]) {
<n-space align="center" :size="12"> <n-space align="center" :size="12">
<span class="modal-title-compact">文件详情 - {{ currentFileRow?.Title || '文件列表' }}</span> <span class="modal-title-compact">文件详情 - {{ currentFileRow?.Title || '文件列表' }}</span>
<n-tag <n-tag
:type="getStatusInfo(currentFileRow?.Status || 0).type as any"
:color="{ color: getStatusInfo(currentFileRow?.Status || 0).color }"
size="small" size="small"
:style="{
backgroundColor: getStatusInfo(currentFileRow?.Status).color,
color: 'white',
border: 'none'
}"
> >
{{ getStatusInfo(currentFileRow?.Status || 0).label }} {{ getStatusInfo(currentFileRow?.Status).label }}
</n-tag> </n-tag>
<span v-if="isCurrentRowReadonly" class="readonly-hint-modal"> <span v-if="isCurrentRowReadonly" class="readonly-hint-modal">
(当前状态不允许上传/删除操作) (当前状态不允许上传/删除操作)
...@@ -710,8 +790,9 @@ async function handleFileListClosed(files: any[]) { ...@@ -710,8 +790,9 @@ async function handleFileListClosed(files: any[]) {
<FileListView <FileListView
v-if="currentFileRow" v-if="currentFileRow"
ref="fileListViewRef"
:owner-item="currentFileRow" :owner-item="currentFileRow"
@close="showFileListView = false" @close="handleFileListViewClose"
@update-files="handleFileListUpdate" @update-files="handleFileListUpdate"
></FileListView> ></FileListView>
</NModal> </NModal>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment