Commit d3944a78 by Neo Turing

本地

parent 422ec1b4
<!doctype html>
<html lang="zh-cmn-Hans">
<head>
<meta name="buildTime" content="2025-04-25 17:56:27">
<meta name="buildTime" content="2025-06-10 14:01:51">
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<title>VueDashboard</title>
<script type="module" crossorigin src="/Content/VueDashboardUi/VueDashboard1/assets/index-CsI39EZy.js"></script>
<link rel="stylesheet" crossorigin href="/Content/VueDashboardUi/VueDashboard1/assets/index-DhhgO4aJ.css">
<script type="module" crossorigin src="/Content/VueDashboardUi/VueDashboard1/assets/index-DQeQ-ejl.js"></script>
<link rel="stylesheet" crossorigin href="/Content/VueDashboardUi/VueDashboard1/assets/index-CIbUL_fo.css">
</head>
<body>
<div id="app"></div>
......
......@@ -19,6 +19,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
404: () => import("@/views/_builtin/404/index.vue"),
500: () => import("@/views/_builtin/500/index.vue"),
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
lac_sample: () => import("@/views/_builtin/lac/Sample/index.vue"),
lac_supplier_applylist: () => import("@/views/_builtin/lac/Supplier/ApplyList/index.vue"),
login: () => import("@/views/_builtin/login/index.vue"),
test: () => import("@/views/_builtin/test/index.vue"),
test_test1: () => import("@/views/_builtin/test/test1/index.vue"),
......
......@@ -63,6 +63,45 @@ export const generatedRoutes: GeneratedRoute[] = [
}
},
{
name: 'lac',
path: '/lac',
component: 'layout.base',
meta: {
title: 'lac',
i18nKey: 'route.lac'
},
children: [
{
name: 'lac_sample',
path: '/lac/sample',
component: 'view.lac_sample',
meta: {
title: 'lac_sample',
i18nKey: 'route.lac_sample'
}
},
{
name: 'lac_supplier',
path: '/lac/supplier',
meta: {
title: 'lac_supplier',
i18nKey: 'route.lac_supplier'
},
children: [
{
name: 'lac_supplier_applylist',
path: '/lac/supplier/applylist',
component: 'view.lac_supplier_applylist',
meta: {
title: 'lac_supplier_applylist',
i18nKey: 'route.lac_supplier_applylist'
}
}
]
}
]
},
{
name: 'login',
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
component: 'layout.blank$view.login',
......
......@@ -181,6 +181,10 @@ const routeMap: RouteMap = {
"500": "/500",
"home": "/home",
"iframe-page": "/iframe-page/:url",
"lac": "/lac",
"lac_sample": "/lac/sample",
"lac_supplier": "/lac/supplier",
"lac_supplier_applylist": "/lac/supplier/applylist",
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
"test": "/test",
"test_test1": "/test/test1",
......
......@@ -27,12 +27,17 @@ declare module 'vue' {
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NDataTable: typeof import('naive-ui')['NDataTable']
NDatePicker: typeof import('naive-ui')['NDatePicker']
NDescriptions: typeof import('naive-ui')['NDescriptions']
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDivider: typeof import('naive-ui')['NDivider']
NDrawer: typeof import('naive-ui')['NDrawer']
......@@ -43,6 +48,8 @@ declare module 'vue' {
NFormItem: typeof import('naive-ui')['NFormItem']
NGi: typeof import('naive-ui')['NGi']
NGrid: typeof import('naive-ui')['NGrid']
NGridItem: typeof import('naive-ui')['NGridItem']
NIcon: typeof import('naive-ui')['NIcon']
NInput: typeof import('naive-ui')['NInput']
NInputGroup: typeof import('naive-ui')['NInputGroup']
NInputNumber: typeof import('naive-ui')['NInputNumber']
......@@ -51,14 +58,23 @@ declare module 'vue' {
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NProgress: typeof import('naive-ui')['NProgress']
NRadio: typeof import('naive-ui')['NRadio']
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
NStatistic: typeof import('naive-ui')['NStatistic']
NStep: typeof import('naive-ui')['NStep']
NSteps: typeof import('naive-ui')['NSteps']
NSwitch: typeof import('naive-ui')['NSwitch']
NTab: typeof import('naive-ui')['NTab']
NTabs: typeof import('naive-ui')['NTabs']
NTag: typeof import('naive-ui')['NTag']
NText: typeof import('naive-ui')['NText']
NTooltip: typeof import('naive-ui')['NTooltip']
NUpload: typeof import('naive-ui')['NUpload']
NWatermark: typeof import('naive-ui')['NWatermark']
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
......
......@@ -35,6 +35,10 @@ declare module "@elegant-router/types" {
"500": "/500";
"home": "/home";
"iframe-page": "/iframe-page/:url";
"lac": "/lac";
"lac_sample": "/lac/sample";
"lac_supplier": "/lac/supplier";
"lac_supplier_applylist": "/lac/supplier/applylist";
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
"test": "/test";
"test_test1": "/test/test1";
......@@ -89,6 +93,7 @@ declare module "@elegant-router/types" {
| "500"
| "home"
| "iframe-page"
| "lac"
| "login"
| "test"
>;
......@@ -113,6 +118,8 @@ declare module "@elegant-router/types" {
| "404"
| "500"
| "iframe-page"
| "lac_sample"
| "lac_supplier_applylist"
| "login"
| "test"
| "test_test1"
......
<script setup lang="ts">
import { computed, defineEmits, defineProps, onMounted, ref, watch } from 'vue';
import { request } from '@/service/request';
// 主体框架内置的第三方方法通过window对象暴露、供外部组件使用
// 获取主题状态
const themeStore = (window as any).$themeStore;
// 获取消息提示
const message = (window as any).$message;
const dialog = (window as any).$dialog;
// 通过计算属性确定当前的主体色
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'
}));
interface PlanDetailItem {
id: number;
testItem: string;
standardValue: string;
allowableRange: string;
key?: string;
children?: PlanDetailItem[];
}
const props = defineProps({
standardKvid: {
type: String,
required: true
}
});
const emit = defineEmits(['cancel', 'confirm']);
// 当前可用的方案明细数据
const planDetails = ref<PlanDetailItem[]>([]);
// 已选中的行键数组
const checkedRowKeys = ref<(string | number)[]>([]);
// 加载状态
const loading = ref<boolean>(false);
// 获取测试详情数据
async function fetchTestDetails() {
if (!props.standardKvid) return;
try {
loading.value = true;
const { data, error } = await request({
url: '/Restful/Kivii.Standards.Entities.Detection/Query.json',
method: 'get',
params: { StandardKvid: props.standardKvid }
});
if (error) {
console.error('获取测试详情失败:', error);
message.error('获取测试详情失败');
return;
}
if (data && data.Results) {
// 处理树形结构数据
const treeData = processTreeData(data.Results);
planDetails.value = treeData;
message.success(`已加载${data.Results.length}个测试项目`);
} else {
message.error('获取测试数据失败');
}
} catch (error) {
message.error('获取测试详情失败');
console.error(error);
} finally {
loading.value = false;
}
}
// 处理树形结构数据
function processTreeData(data: any[]): PlanDetailItem[] {
// 创建ID到节点的映射
const idMap: Record<string, any> = {};
const result: any[] = [];
// 首先将所有节点添加到映射中
data.forEach((item, index) => {
const node = {
id: index + 1,
key: item.Kvid || `item-${index}`, // 使用Kvid作为唯一标识,如果没有则使用索引
testItem: item.Title || '未命名项目',
standardValue: item.StandardValue || item.QualifiedValue || '未设置',
allowableRange: item.AllowableRange || '±5%',
parentId: item.ParentKvid || null, // 假设API返回ParentKvid表示父节点ID
children: []
};
idMap[node.key] = node;
});
// 构建树形结构
Object.values(idMap).forEach(node => {
if (node.parentId && idMap[node.parentId]) {
// 如果有父节点,将当前节点添加到父节点的children中
idMap[node.parentId].children.push(node);
} else {
// 如果没有父节点,则是顶级节点
result.push(node);
}
});
// 处理空children数组
const cleanEmptyChildren = (nodes: any[]) => {
for (const node of nodes) {
if (node.children.length === 0) {
delete node.children;
} else {
cleanEmptyChildren(node.children);
}
}
};
cleanEmptyChildren(result);
return result;
}
// 监听standardKvid变化,重新获取数据
watch(
() => props.standardKvid,
newValue => {
if (newValue) {
fetchTestDetails();
// 重置选中状态
checkedRowKeys.value = [];
} else {
planDetails.value = [];
}
},
{ immediate: true }
);
// 处理行选择变化
function handleCheckedRowKeysChange(keys: (string | number)[]) {
checkedRowKeys.value = keys;
}
// 表格列定义
const columns = [
{
type: 'selection' as const
},
{ title: '测试项目', key: 'testItem', align: 'center' as const },
{ title: '标准值', key: 'standardValue', align: 'center' as const },
{ title: '允许偏差范围', key: 'allowableRange', align: 'center' as const }
];
// 取消选择
function cancel() {
emit('cancel');
}
// 确认选择
function confirm() {
if (checkedRowKeys.value.length === 0) {
message.warning('请至少选择一个测试项目');
return;
}
// 提取完整树形结构中被选中的节点和它们的子节点
const cloneTreeData = JSON.parse(JSON.stringify(planDetails.value));
// 过滤并标记选中的树节点
const filterSelectedNodes = (nodes: any[]): any[] => {
const result: any[] = [];
for (const node of nodes) {
const isSelected = checkedRowKeys.value.includes(node.key);
// 如果当前节点被选中或者有被选中的子节点,则保留
if (isSelected) {
// 创建一个节点副本,只保留需要的属性
const newNode: any = {
id: node.id,
key: node.key,
testItem: node.testItem,
standardValue: node.standardValue,
allowableRange: node.allowableRange
};
// 如果有子节点,递归处理
if (node.children && node.children.length > 0) {
const selectedChildren = filterSelectedNodes(node.children);
if (selectedChildren.length > 0) {
newNode.children = selectedChildren;
}
}
result.push(newNode);
} else if (node.children && node.children.length > 0) {
// 如果当前节点未被选中,但可能有被选中的子节点
const selectedChildren = filterSelectedNodes(node.children);
if (selectedChildren.length > 0) {
// 创建一个节点副本,并添加被选中的子节点
const newNode: any = {
id: node.id,
key: node.key,
testItem: node.testItem,
standardValue: node.standardValue,
allowableRange: node.allowableRange,
children: selectedChildren
};
result.push(newNode);
}
}
}
return result;
};
const selectedTreeData = filterSelectedNodes(cloneTreeData);
// 计算选中的项目总数
const countSelectedItems = (nodes: any[]): number => {
let count = 0;
for (const node of nodes) {
count += 1; // 计算当前节点
if (node.children && node.children.length > 0) {
count += countSelectedItems(node.children); // 递归计算子节点
}
}
return count;
};
const selectedCount = countSelectedItems(selectedTreeData);
// 将处理后的树形结构传递给父组件
emit('confirm', selectedTreeData);
message.success(`已选择${selectedCount}个测试项目`);
}
// 页面加载时获取数据
onMounted(() => {
// 删除此处的fetchTestDetails调用,避免重复调用API
// watch中的immediate:true已经确保了初始加载数据
});
</script>
<template>
<div class="plan-detail-selector">
<!--
<div class="header">
<div class="title">测试方案明细选择</div>
<n-space style="display: flex; align-items: center;">
<span class="plan-name">{{ planName }}</span>
<n-tag type="info">{{ checkedRowKeys.length }}项已选择</n-tag>
</n-space>
</div>
-->
<NAlert type="info" style="margin-bottom: 16px">
请选择需要进行比对测试的项目,选中的项目将被添加到测试数据表格中。
</NAlert>
<NDataTable
:columns="columns"
:data="planDetails"
:bordered="false"
:row-key="row => row.key"
:checked-row-keys="checkedRowKeys"
children-key="children"
:indent="20"
:max-height="300"
:min-height="300"
:pagination="false"
striped
:loading="loading"
@update:checked-row-keys="handleCheckedRowKeysChange"
></NDataTable>
<div class="footer">
<NSpace justify="end">
<NButton @click="cancel">取消</NButton>
<NButton type="primary" :disabled="checkedRowKeys.length === 0" @click="confirm">
确认选择({{ checkedRowKeys.length }})
</NButton>
</NSpace>
</div>
</div>
</template>
<style scoped>
.plan-detail-selector {
/* padding: 0 0 16px 0; */
height: 450px;
display: flex;
flex-direction: column;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.title {
font-size: 18px;
font-weight: bold;
color: #333;
}
.plan-name {
font-weight: bold;
color: #2080f0;
}
.footer {
padding-top: 10px;
/* padding-bottom: 16px; */
border-top: 1px solid #f0f0f0;
margin-top: auto;
}
</style>
<script setup lang="ts">
import { computed, h, ref, resolveComponent } from 'vue';
import type { UploadCustomRequestOptions } from 'naive-ui';
interface FileItem {
id: number;
name: string;
remark: string;
uploadTime?: string;
size?: string;
}
// 主体框架内置的第三方方法通过window对象暴露、供外部组件使用
// 获取消息提示
const message = (window as any).$message;
// 状态变量
const fileData = ref<FileItem[]>([
{
id: 1,
name: '2023年度财务报表.xlsx',
remark: '年度财务总结文件',
uploadTime: '2023-12-15',
size: '2.3MB'
},
{
id: 2,
name: '产品设计方案.docx',
remark: '产品设计初稿,待审核',
uploadTime: '2023-12-20',
size: '1.5MB'
},
{
id: 3,
name: '市场调研报告.pdf',
remark: '2023第四季度市场调研',
uploadTime: '2023-12-18',
size: '3.7MB'
}
]);
const loading = ref<boolean>(false);
const showPreviewModal = ref<boolean>(false);
const currentFile = ref<FileItem | null>(null);
// 表格列定义
const columns = ref([
{
title: '文件名称',
key: 'name',
width: '50%'
},
{
title: '上传日期',
key: 'uploadTime',
align: 'center' as const,
width: '30%'
},
{
title: '操作',
key: 'actions',
align: 'center' as const,
width: '20%',
render(row: FileItem) {
return h('div', { style: 'display: flex; gap: 8px; justify-content: center;' }, [
h(
resolveComponent('n-button'),
{
size: 'small',
type: 'info',
onClick: () => viewFile(row)
},
{ default: () => '预览' }
),
h(
resolveComponent('n-button'),
{
size: 'small',
type: 'error',
onClick: () => deleteFile(row)
},
{ default: () => '删除' }
)
]);
}
}
]);
// 查看文件
function viewFile(file: FileItem) {
currentFile.value = file;
showPreviewModal.value = true;
}
// 删除文件
function deleteFile(file: FileItem) {
message.success(`文件"${file.name}"已删除`);
fileData.value = fileData.value.filter(item => item.id !== file.id);
}
// 下载文件
function downloadFile(file: FileItem) {
message.success(`开始下载文件: ${file.name}`);
// 实际项目中这里应该调用后端API进行文件下载
}
// 处理文件上传
function handleUpload(options: UploadCustomRequestOptions) {
const { file } = options;
if (file) {
loading.value = true;
// 模拟上传延迟
setTimeout(() => {
const newFile: FileItem = {
id: Date.now(),
name: file.name,
remark: '新上传文件',
uploadTime: new Date().toISOString().split('T')[0],
size: formatFileSize(file.file?.size || 0)
};
fileData.value.unshift(newFile);
message.success(`文件"${file.name}"上传成功!`);
loading.value = false;
}, 1500);
}
}
// 格式化文件大小
function formatFileSize(size: number): string {
if (size < 1024) {
return `${size}B`;
} else if (size < 1024 * 1024) {
return `${(size / 1024).toFixed(1)}KB`;
}
return `${(size / (1024 * 1024)).toFixed(1)}MB`;
}
</script>
<template>
<div>
<NSpace vertical>
<NSpace justify="end" style="margin-bottom: 10px">
<NUpload action="#" :custom-request="handleUpload" :show-file-list="false">
<NButton type="primary">
<template #icon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18">
<path
fill="currentColor"
d="M19.35,10.04C18.67,6.59 15.64,4 12,4C9.11,4 6.6,5.64 5.35,8.04C2.34,8.36 0,10.91 0,14A6,6 0 0,0 6,20H19A5,5 0 0,0 24,15C24,12.36 21.95,10.22 19.35,10.04M19,18H6A4,4 0 0,1 2,14C2,11.95 3.53,10.24 5.56,10.03L6.63,9.92L7.13,8.97C8.08,7.14 9.94,6 12,6C14.62,6 16.88,7.86 17.39,10.43L17.69,11.93L19.22,12.04C20.78,12.14 22,13.45 22,15A3,3 0 0,1 19,18M8,13H10.55V16H13.45V13H16L12,9L8,13Z"
></path>
</svg>
</template>
上传文件
</NButton>
</NUpload>
</NSpace>
<NDataTable
:columns="columns"
:data="fileData"
:bordered="false"
:loading="loading"
max-height="500px"
></NDataTable>
</NSpace>
<!-- 文件预览对话框 -->
<NModal v-model:show="showPreviewModal" preset="card" title="文件预览" style="width: 50%" :mask-closable="true">
<div v-if="currentFile">
<NDescriptions label-placement="left" bordered>
<NDescriptionsItem label="文件名称">
{{ currentFile.name }}
</NDescriptionsItem>
<NDescriptionsItem label="上传日期">
{{ currentFile.uploadTime || '未记录' }}
</NDescriptionsItem>
</NDescriptions>
<div class="modal-footer">
<NButton type="primary" @click="downloadFile(currentFile)">下载文件</NButton>
<NButton @click="showPreviewModal = false">关闭</NButton>
</div>
</div>
</NModal>
</div>
</template>
<style scoped>
.section-title {
display: flex;
align-items: center;
font-size: 18px;
font-weight: bold;
margin-bottom: 16px;
color: #333;
}
.section-icon {
margin-right: 8px;
color: #333;
display: flex;
align-items: center;
justify-content: center;
}
.file-preview-content {
margin: 20px 0;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed #ccc;
border-radius: 4px;
}
.no-preview-message {
text-align: center;
color: #999;
}
.file-icon {
font-size: 48px;
opacity: 0.6;
}
.modal-footer {
margin-top: 20px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
</style>
<script setup lang="ts">
import { computed, defineEmits, defineProps, h, onMounted, ref, resolveComponent } from 'vue';
import { request } from '@/service/request';
import FileListView from './fileListView.vue';
// 主体框架内置的第三方方法通过window对象暴露、供外部组件使用
// 获取主题状态
const themeStore = (window as any).$themeStore;
// 获取消息提示
const message = (window as any).$message;
const dialog = (window as any).$dialog;
// 通过计算属性确定当前的主体色
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接口,但保留原有字段
interface FileItem {
id?: number;
name?: string; // 对应Title
type?: string;
status?: string; // 对应StatusType
remark?: string; // 对应Description
StandardKvid: string;
Kvid: string;
Title?: string;
Code?: string;
Unit?: string;
QualifiedValue?: string;
Description?: string;
StatusType?: string;
}
// API返回的数据项接口
interface ResultItem {
id?: number;
StandardKvid: string;
Kvid: string;
Title?: string;
Code?: string;
Unit?: string;
QualifiedValue?: string;
Description?: string;
StatusType?: string;
[key: string]: any;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = defineProps({
fileList: {
type: Array as () => FileItem[],
required: true
},
report: {
type: Object as () => any,
required: true
}
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emit = defineEmits(['update:file-list', 'update:reviewResult', 'update:reviewComment']);
// 状态变量
const titleOptions = ref<SelectOption[]>([]);
const selectedStandardKvid = ref<string>('');
const fileData = ref<FileItem[]>([]);
const loading = ref<boolean>(false);
// 控制fileListView模态框显示
const showFileListView = ref<boolean>(false);
// 根据选择的标题筛选数据
const filteredData = computed(() => {
if (!selectedStandardKvid.value) {
return [];
}
return fileData.value.filter(file => file.StandardKvid === selectedStandardKvid.value);
});
// 获取文件类型数据
async function fetchTitleOptions() {
try {
loading.value = true;
// 调用标准实体查询接口
const { data, error } = await request({
url: '/Restful/Kivii.Standards.Entities.Standard/Query.json',
method: 'get',
params: { Type: '文件审核' }
});
console.log('完整响应数据:', data);
if (error) {
console.error('获取文件类型失败:', error);
message.error('获取文件类型失败');
return;
}
// 添加数据存在性检查
if (data && data.Results) {
// 直接使用Results数组
titleOptions.value = data.Results.map((item: any) => ({
label: item.Title,
value: item.Kvid
}));
} else {
console.error('无效的数据格式:', data);
message.error('数据格式不正确');
}
} catch (error) {
message.error('获取文件类型失败');
console.error('获取文件类型失败:', error);
} finally {
loading.value = false;
}
}
// 获取文件详情数据
async function fetchFileDetails() {
if (!selectedStandardKvid.value) return;
try {
loading.value = true;
const { data, error } = await request({
url: '/Restful/Kivii.Standards.Entities.Detection/Query.json',
method: 'get',
params: { StandardKvid: selectedStandardKvid.value }
});
if (error) {
console.error('获取文件详情失败:', error);
message.error('获取文件详情失败');
return;
}
if (data && data.Results) {
createReportItems(data.Results);
} else {
message.error('获取文件数据失败');
}
} catch (error) {
message.error('获取文件详情失败');
console.error(error);
} finally {
loading.value = false;
}
}
async function createReportItems(reuslts: any) {
const { data, error } = await request({
url: '/Restful/Kivii.Lims.Entities.ReportItem/Detection.json',
method: 'post',
data: { Detections: reuslts, ReportKvid: props.report.Kvid }
});
if (error) {
console.error('项目创建失败:', error);
message.error(error.message);
return;
}
if (data && data.Results) {
// 保留完整的Results数组,同时添加必要的展示字段
fileData.value = data.Results.map((item: any) => ({
...item, // 保留所有原始字段
name: item.Title || '', // 添加展示用的name字段
remark: item.Description || item.QualifiedValue || '', // 添加展示用的remark字段
status: item.StatusType || 'pending' // 添加展示用的status字段
}));
// 向父组件更新文件列表
emit('update:file-list', fileData.value);
}
}
// 处理标题选择变化
function handleTitleChange(value: string) {
selectedStandardKvid.value = value;
fetchFileDetails();
}
// 页面加载时获取文件类型
onMounted(() => {
fetchTitleOptions();
});
// 表格列定义
const columns = ref<DataTableColumn<FileItem>[]>([
{
title: '文件名称',
key: 'name',
width: '35%'
},
{
title: '备注',
key: 'remark',
width: '20%'
},
{
title: '文件状态',
key: 'status',
width: '15%',
align: 'center',
render(row: FileItem) {
const statusMap: Record<string, { type: string; label: string }> = {
BeforeTest: { type: 'warning', label: '待上传' },
Asigning: { type: 'warning', label: '已上传' },
Testing: { type: 'success', label: '审核中' },
TestFinished: { type: 'warning', label: '不通过' },
TestCollected: { type: 'success', label: '已通过' }
};
const status = row.status || '';
const { type, label } = statusMap[status] || { type: 'default', label: status || '未知' };
return h(
resolveComponent('n-tag'),
{
type,
size: 'small'
},
{ default: () => label }
);
}
},
{
title: '操作',
key: 'actions',
width: 200,
align: 'center',
render(row: any) {
return h('div', { style: 'display: flex; gap: 8px; justify-content: center;' }, [
h(
resolveComponent('n-button'),
{
size: 'small',
onClick: () => viewFile(row)
},
{ default: () => '查看' }
),
h(
resolveComponent('n-button'),
{
size: 'small',
type: 'error',
onClick: () => deleteFileRecord(row)
},
{ default: () => '删除' }
)
]);
}
}
]);
// 查看文件
function viewFile(row: FileItem) {
// 可以根据需要将文件信息传递给fileListView组件
console.log(`查看文件: ${row.name}`);
showFileListView.value = true;
}
// 删除文件记录
function deleteFileRecord(row: FileItem) {
// 实际应该调用删除API
message.success(`文件"${row.name}"删除成功`);
// 从列表中移除
fileData.value = fileData.value.filter(item => item.Kvid !== row.Kvid);
// 向父组件更新文件列表
emit('update:file-list', fileData.value);
}
</script>
<template>
<div>
<div class="section-title">
<svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path
fill="currentColor"
d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M14,20V19C14,17.67 11.33,17 10,17C8.67,17 6,17.67 6,19V20H14M10,12A2,2 0 0,0 8,14A2,2 0 0,0 10,16A2,2 0 0,0 12,14A2,2 0 0,0 10,12Z"
></path>
</svg>
<span>审核文件提交</span>
</div>
<NFormItem label="选择文件类型">
<NSelect
v-model:value="selectedStandardKvid"
:options="titleOptions"
placeholder="选择文件类型"
@update:value="handleTitleChange"
></NSelect>
</NFormItem>
<NDataTable
:columns="columns"
:data="filteredData"
:bordered="false"
max-height="300px"
:loading="loading"
></NDataTable>
<!-- fileListView模态框 -->
<NModal v-model:show="showFileListView" preset="card" title="文件列表" style="width: 50%" :mask-closable="true">
<FileListView></FileListView>
</NModal>
</div>
</template>
<style scoped>
.section-title {
display: flex;
align-items: center;
font-size: 18px;
font-weight: bold;
margin-bottom: 16px;
color: #333;
}
.section-icon {
margin-right: 8px;
color: #333;
display: flex;
align-items: center;
justify-content: center;
}
.upload-hint {
color: #999;
font-size: 13px;
margin-top: 8px;
}
.mt-4 {
margin-top: 16px;
}
</style>
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