add export and import
This commit is contained in:
127
src/App.vue
127
src/App.vue
@@ -6,10 +6,31 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
<!-- 主操作按钮 -->
|
||||||
<button @click="startScan" :disabled="loading" class="scan-btn" :class="{ 'scanning': loading }">
|
<button @click="startScan" :disabled="loading" class="scan-btn" :class="{ 'scanning': loading }">
|
||||||
<span v-if="loading">🔄 正在深入排查系统底层...</span>
|
<span v-if="loading">🔄 正在深入排查系统底层...</span>
|
||||||
<span v-else>🔍 开始全面体检</span>
|
<span v-else>🔍 开始全面体检</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- 辅助操作按钮区 -->
|
||||||
|
<div class="secondary-actions">
|
||||||
|
<button class="secondary-btn" @click="triggerImport" :disabled="loading">
|
||||||
|
📂 导入报告
|
||||||
|
</button>
|
||||||
|
<button class="secondary-btn" @click="exportReport" :disabled="!report || loading">
|
||||||
|
💾 导出报告
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 隐藏的文件输入框 -->
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
ref="fileInput"
|
||||||
|
@change="handleFileImport"
|
||||||
|
accept=".json"
|
||||||
|
style="display: none"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-if="errorMsg" class="error-box">
|
<div v-if="errorMsg" class="error-box">
|
||||||
⚠️ {{ errorMsg }}
|
⚠️ {{ errorMsg }}
|
||||||
</div>
|
</div>
|
||||||
@@ -30,14 +51,14 @@
|
|||||||
<span class="label">CPU 型号</span>
|
<span class="label">CPU 型号</span>
|
||||||
<span class="value text-ellipsis" :title="report.hardware.cpu_name">{{ report.hardware.cpu_name }}</span>
|
<span class="value text-ellipsis" :title="report.hardware.cpu_name">{{ report.hardware.cpu_name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 新增:整机信息 (品牌机看这个) -->
|
<!-- 整机信息 (品牌机看这个) -->
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">整机型号 (System)</span>
|
<span class="label">整机型号 (System)</span>
|
||||||
<span class="value text-ellipsis" :title="report.hardware.sys_vendor + ' ' + report.hardware.sys_product">
|
<span class="value text-ellipsis" :title="report.hardware.sys_vendor + ' ' + report.hardware.sys_product">
|
||||||
{{ report.hardware.sys_vendor }} {{ report.hardware.sys_product }}
|
{{ report.hardware.sys_vendor }} {{ report.hardware.sys_product }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 原有:主板信息 (DIY看这个) -->
|
<!-- 主板信息 (DIY看这个) -->
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">主板型号 (BaseBoard)</span>
|
<span class="label">主板型号 (BaseBoard)</span>
|
||||||
<span class="value text-ellipsis" :title="report.hardware.mobo_vendor + ' ' + report.hardware.mobo_product">
|
<span class="value text-ellipsis" :title="report.hardware.mobo_vendor + ' ' + report.hardware.mobo_product">
|
||||||
@@ -195,6 +216,7 @@ import { invoke } from '@tauri-apps/api/core';
|
|||||||
const report = ref(null);
|
const report = ref(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const errorMsg = ref('');
|
const errorMsg = ref('');
|
||||||
|
const fileInput = ref(null); // 文件输入框的引用
|
||||||
|
|
||||||
const hasStorageDanger = computed(() => {
|
const hasStorageDanger = computed(() => {
|
||||||
return report.value?.storage.some(d => d.is_danger);
|
return report.value?.storage.some(d => d.is_danger);
|
||||||
@@ -228,16 +250,75 @@ function getBatteryColor(percentage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(wmiTime) {
|
function formatTime(wmiTime) {
|
||||||
// 如果是空值或者格式不对,直接返回
|
|
||||||
if (!wmiTime) return "Unknown Time";
|
if (!wmiTime) return "Unknown Time";
|
||||||
// 简单的容错处理,如果是标准 ISO 时间或自定义格式
|
|
||||||
return wmiTime.replace('T', ' ').substring(0, 19);
|
return wmiTime.replace('T', ' ').substring(0, 19);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 导出报告功能 ---
|
||||||
|
function exportReport() {
|
||||||
|
if (!report.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const dataStr = JSON.stringify(report.value, null, 2);
|
||||||
|
// 使用 Blob 创建文件对象
|
||||||
|
const blob = new Blob([dataStr], { type: "application/json" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// 创建临时链接触发下载
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
// 文件名包含时间戳,避免重名
|
||||||
|
const timestamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, "-");
|
||||||
|
link.download = `SystemDoctor_Report_${timestamp}.json`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
} catch (err) {
|
||||||
|
errorMsg.value = "导出失败: " + err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 导入报告功能 ---
|
||||||
|
function triggerImport() {
|
||||||
|
// 触发隐藏的文件输入框点击
|
||||||
|
fileInput.value.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileImport(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(e.target.result);
|
||||||
|
// 简单的格式校验:检查是否有关键字段
|
||||||
|
if (json && json.hardware && json.storage) {
|
||||||
|
report.value = json;
|
||||||
|
errorMsg.value = '';
|
||||||
|
} else {
|
||||||
|
errorMsg.value = "无效的报告文件:格式不正确或内容缺失。";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
errorMsg.value = "导入失败: 无法解析文件 (" + err.message + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
// 清空 value 以便允许重复选择同一个文件
|
||||||
|
event.target.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
async function startScan() {
|
async function startScan() {
|
||||||
|
// 1. 如果界面已有报告,先清空
|
||||||
|
if (report.value) {
|
||||||
|
report.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
errorMsg.value = '';
|
errorMsg.value = '';
|
||||||
report.value = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await invoke('run_diagnosis');
|
const result = await invoke('run_diagnosis');
|
||||||
@@ -267,7 +348,6 @@ body {
|
|||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 30px 20px;
|
padding: 30px 20px;
|
||||||
/* 关键修改:border-box 让 padding 包含在 height 内 */
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
@@ -298,6 +378,12 @@ h1 {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 35px;
|
margin-bottom: 35px;
|
||||||
|
gap: 15px; /* 增加按钮组之间的间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-btn {
|
.scan-btn {
|
||||||
@@ -334,8 +420,35 @@ h1 {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 辅助按钮样式 */
|
||||||
|
.secondary-btn {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
color: #606266;
|
||||||
|
padding: 8px 20px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-btn:hover:not(:disabled) {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-btn:disabled {
|
||||||
|
color: #c0c4cc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
.error-box {
|
.error-box {
|
||||||
margin-top: 15px;
|
margin-top: 5px;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
background-color: #ffeaea;
|
background-color: #ffeaea;
|
||||||
color: #c0392b;
|
color: #c0392b;
|
||||||
|
|||||||
Reference in New Issue
Block a user