Skip to content

Commit 7f3220a

Browse files
author
bakacai
authored
feat: 添加连接前后行为配置 (#15)
1 parent b3f9b1a commit 7f3220a

8 files changed

Lines changed: 222 additions & 3 deletions

File tree

‎src-tauri/src/commands/device.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,18 @@ pub async fn connect_device(
5555
port: Option<String>,
5656
baud_rate: Option<u32>,
5757
stub_path: String,
58+
before_operation: String,
59+
after_operation: String,
5860
) -> Result<bool, String> {
5961
let device_config = DeviceConfig {
6062
chip_type: chip_model,
6163
memory_type,
6264
port_name: port.ok_or("端口名不能为空")?,
6365
baud_rate: baud_rate.ok_or("波特率不能为空")?,
6466
stub_path,
67+
// keep before/after as strings for serde transport; parsing happens in tool factory
68+
before_operation,
69+
after_operation,
6570
};
6671

6772
// 创建 Tauri 进度回调

‎src-tauri/src/types/device.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ pub struct DeviceConfig {
1313
pub port_name: String,
1414
pub baud_rate: u32,
1515
pub stub_path: String,
16+
// use String for serde-friendly transport; parse to enums internally when needed
17+
pub before_operation: String,
18+
pub after_operation: String,
1619
}

‎src-tauri/src/utils/tool_factory.rs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ pub fn create_tool_instance_with_progress(
4545
_ => return Err(format!("不支持的芯片型号: {}", config.chip_type)),
4646
};
4747

48+
// parse before/after operation strings into enums
49+
let before_enum = match config.before_operation.as_str() {
50+
"default_reset" => BeforeOperation::DefaultReset,
51+
"no_reset" => BeforeOperation::NoReset,
52+
"no_reset_no_sync" => BeforeOperation::NoResetNoSync,
53+
_ => BeforeOperation::NoReset,
54+
};
55+
4856
let (stub_path, _temp_file) = prepare_stub_path(
4957
if !config.stub_path.is_empty() {
5058
Some(config.stub_path.as_str())
@@ -60,7 +68,7 @@ pub fn create_tool_instance_with_progress(
6068
// 创建 SifliToolBase (带进度回调)
6169
let base = SifliToolBase::new_with_external_stub(
6270
config.port_name.clone(),
63-
BeforeOperation::NoReset,
71+
before_enum,
6472
config.memory_type.to_lowercase(),
6573
config.baud_rate,
6674
1,

‎src/components/DeviceConnection.vue‎

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,83 @@
312312
{{ isConnected ? t('deviceConnection.disconnectDevice') : t('deviceConnection.connectDevice') }}
313313
</button>
314314

315+
<!-- 下载行为配置 -->
316+
<div class="card bg-base-100 shadow-sm p-3 mt-3">
317+
<div
318+
class="flex items-center justify-between cursor-pointer"
319+
@click="downloadPanelCollapsed = !downloadPanelCollapsed"
320+
>
321+
<div class="flex items-center gap-2">
322+
<span class="material-icons text-sm text-primary">download</span>
323+
<span class="font-bold text-sm">{{ t('deviceConnection.downloadBehavior.title') }}</span>
324+
</div>
325+
<div class="flex items-center gap-2 text-xs text-base-content/70">
326+
<span
327+
class="material-icons text-sm transition-transform duration-200"
328+
:class="{ 'rotate-180': !downloadPanelCollapsed }"
329+
>keyboard_arrow_down</span
330+
>
331+
</div>
332+
</div>
333+
334+
<transition name="fade-collapse">
335+
<div v-show="!downloadPanelCollapsed" class="mt-3 pt-2 border-t border-base-300/30 space-y-4">
336+
<div class="space-y-2">
337+
<div class="flex items-center gap-2 text-xs font-semibold text-base-content/80">
338+
<span class="w-1 h-4 rounded-full bg-primary/60"></span>
339+
<span>{{ t('deviceConnection.downloadBehavior.beforeLabel') }}</span>
340+
</div>
341+
<div class="flex flex-col gap-2">
342+
<label class="option-card" :class="{ 'option-card-active': downloadBefore === 'default_reset' }">
343+
<div class="flex items-center gap-2">
344+
<input type="radio" v-model="downloadBefore" value="default_reset" class="radio radio-sm" />
345+
<span class="material-icons text-xs text-primary">sync</span>
346+
<span class="text-xs">{{ t('deviceConnection.downloadBehavior.before.default_reset') }}</span>
347+
</div>
348+
</label>
349+
<label class="option-card" :class="{ 'option-card-active': downloadBefore === 'no_reset' }">
350+
<div class="flex items-center gap-2">
351+
<input type="radio" v-model="downloadBefore" value="no_reset" class="radio radio-sm" />
352+
<span class="material-icons text-xs text-primary">sync_disabled</span>
353+
<span class="text-xs">{{ t('deviceConnection.downloadBehavior.before.no_reset') }}</span>
354+
</div>
355+
</label>
356+
<label class="option-card" :class="{ 'option-card-active': downloadBefore === 'no_reset_no_sync' }">
357+
<div class="flex items-center gap-2">
358+
<input type="radio" v-model="downloadBefore" value="no_reset_no_sync" class="radio radio-sm" />
359+
<span class="material-icons text-xs text-primary">do_not_disturb</span>
360+
<span class="text-xs">{{ t('deviceConnection.downloadBehavior.before.no_reset_no_sync') }}</span>
361+
</div>
362+
</label>
363+
</div>
364+
</div>
365+
366+
<div class="space-y-2">
367+
<div class="flex items-center gap-2 text-xs font-semibold text-base-content/80">
368+
<span class="w-1 h-4 rounded-full bg-secondary/60"></span>
369+
<span>{{ t('deviceConnection.downloadBehavior.afterLabel') }}</span>
370+
</div>
371+
<div class="flex flex-col gap-2">
372+
<label class="option-card" :class="{ 'option-card-active': downloadAfter === 'soft_reset' }">
373+
<div class="flex items-center gap-2">
374+
<input type="radio" v-model="downloadAfter" value="soft_reset" class="radio radio-sm" />
375+
<span class="material-icons text-xs text-secondary">sync</span>
376+
<span class="text-xs">{{ t('deviceConnection.downloadBehavior.after.soft_reset') }}</span>
377+
</div>
378+
</label>
379+
<label class="option-card" :class="{ 'option-card-active': downloadAfter === 'no_reset' }">
380+
<div class="flex items-center gap-2">
381+
<input type="radio" v-model="downloadAfter" value="no_reset" class="radio radio-sm" />
382+
<span class="material-icons text-xs text-secondary">sync_disabled</span>
383+
<span class="text-xs">{{ t('deviceConnection.downloadBehavior.after.no_reset') }}</span>
384+
</div>
385+
</label>
386+
</div>
387+
</div>
388+
</div>
389+
</transition>
390+
</div>
391+
315392
<!-- stub应用开关 -->
316393
<div class="card bg-base-100 shadow-sm p-3 mt-3">
317394
<div class="flex items-center justify-between">
@@ -446,12 +523,13 @@
446523
</template>
447524

448525
<script setup lang="ts">
449-
import { onMounted, computed } from 'vue';
526+
import { onMounted, computed, ref } from 'vue';
450527
import { storeToRefs } from 'pinia';
451528
import { useI18n } from 'vue-i18n';
452529
import { invoke } from '@tauri-apps/api/core';
453530
import { useLogStore } from '../stores/logStore';
454531
import { useDeviceStore } from '../stores/deviceStore';
532+
import type { ResetBeforeMode, ResetAfterMode } from '../stores/deviceStore';
455533
import { useStubConfigStore } from '../stores/stubConfigStore';
456534
import { WindowManager } from '../services/windowManager';
457535
import type { ChipModel } from '../config/chips';
@@ -494,6 +572,19 @@ const stubConfigSummary = computed(() => {
494572
};
495573
});
496574
575+
// 下载行为面板
576+
const downloadPanelCollapsed = ref(true);
577+
578+
const downloadBefore = computed<ResetBeforeMode>({
579+
get: () => (deviceStore.downloadBehavior?.before ?? 'default_reset') as ResetBeforeMode,
580+
set: (v: ResetBeforeMode) => deviceStore.setDownloadBeforeBehavior(v),
581+
});
582+
583+
const downloadAfter = computed<ResetAfterMode>({
584+
get: () => (deviceStore.downloadBehavior?.after ?? 'no_reset') as ResetAfterMode,
585+
set: (v: ResetAfterMode) => deviceStore.setDownloadAfterBehavior(v),
586+
});
587+
497588
const {
498589
isConnected,
499590
isConnecting,
@@ -687,6 +778,16 @@ const connectDevice = async () => {
687778
if (isConnected.value) {
688779
// 断开连接
689780
deviceStore.setConnecting(true);
781+
if (downloadAfter.value === 'soft_reset') {
782+
// 如果选择了下载后软复位,则先发送软复位命令
783+
console.log('Sending soft reset command before disconnecting...');
784+
try {
785+
await invoke<void>('soft_reset');
786+
} catch (error) {
787+
logStore.addMessage(`${t('errors.softResetFailed')}: ${error}`, true);
788+
console.log(t('errors.softResetFailed'), error);
789+
}
790+
}
690791
try {
691792
await invoke<void>('disconnect_device');
692793
deviceStore.setConnected(false);
@@ -709,6 +810,8 @@ const connectDevice = async () => {
709810
chipModel: selectedChip.value!.id,
710811
memoryType: selectedMemoryType.value,
711812
interfaceType: selectedInterface.value,
813+
beforeOperation: downloadBefore.value,
814+
afterOperation: downloadAfter.value,
712815
};
713816
714817
if (selectedInterface.value === 'UART') {
@@ -835,6 +938,47 @@ const openLogWindow = async () => {
835938
width: 300px;
836939
}
837940
941+
.fade-collapse-enter-active,
942+
.fade-collapse-leave-active {
943+
transition:
944+
opacity 0.2s ease,
945+
max-height 0.2s ease;
946+
}
947+
948+
.fade-collapse-enter-from,
949+
.fade-collapse-leave-to {
950+
opacity: 0;
951+
max-height: 0;
952+
}
953+
954+
.fade-collapse-enter-to,
955+
.fade-collapse-leave-from {
956+
opacity: 1;
957+
max-height: 500px;
958+
}
959+
960+
.option-card {
961+
border: 1px solid hsl(var(--b3));
962+
background: linear-gradient(135deg, hsl(var(--b1)) 0%, hsl(var(--b2)) 100%);
963+
border-radius: 0.5rem;
964+
padding: 0.5rem 0.65rem;
965+
transition:
966+
border-color 0.2s ease,
967+
box-shadow 0.2s ease,
968+
transform 0.2s ease;
969+
}
970+
971+
.option-card:hover {
972+
border-color: hsl(var(--p));
973+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
974+
transform: translateY(-1px);
975+
}
976+
977+
.option-card-active {
978+
border-color: hsl(var(--p));
979+
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
980+
}
981+
838982
@keyframes pulse {
839983
0% {
840984
transform: scale(1);

‎src/i18n/locales/en.json‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,20 @@
344344
"pleaseCompleteConfig": "Please complete all necessary device configuration",
345345
"connectFailedMessage": "Failed to connect device, please check device connection and retry",
346346
"connectError": "Error occurred while connecting device",
347+
"downloadBehavior": {
348+
"title": "Behavior Settings",
349+
"beforeLabel": "Before Connect",
350+
"afterLabel": "Before Disconnect",
351+
"before": {
352+
"default_reset": "Default Reset Behavior",
353+
"no_reset": "No Reboot",
354+
"no_reset_no_sync": "No Reboot, No Stub Download"
355+
},
356+
"after": {
357+
"soft_reset": "Soft Reboot",
358+
"no_reset": "No Reboot"
359+
}
360+
},
347361
"stubConfig": {
348362
"title": "",
349363
"apply": "Apply Custom Stub Configuration",

‎src/i18n/locales/zh.json‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,20 @@
344344
"pleaseCompleteConfig": "请完成所有必要的设备配置",
345345
"connectFailedMessage": "连接设备失败,请检查设备连接后重试",
346346
"connectError": "连接设备时出错",
347+
"downloadBehavior": {
348+
"title": "行为设定",
349+
"beforeLabel": "连接前操作",
350+
"afterLabel": "断开前操作",
351+
"before": {
352+
"default_reset": "默认复位行为",
353+
"no_reset": "不重启",
354+
"no_reset_no_sync": "不重启且不下载 Stub"
355+
},
356+
"after": {
357+
"soft_reset": "软重启",
358+
"no_reset": "不重启"
359+
}
360+
},
347361
"stubConfig": {
348362
"title": "Stub配置",
349363
"apply": "应用自定义配置",

‎src/stores/deviceStore.ts‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { defineStore } from 'pinia';
22
import { load } from '@tauri-apps/plugin-store';
33
import type { ChipModel } from '../config/chips';
44
import { CHIP_MODELS } from '../config/chips';
5+
// 下载/重启行为类型
6+
export type ResetBeforeMode = 'default_reset' | 'no_reset' | 'no_reset_no_sync';
7+
export type ResetAfterMode = 'soft_reset' | 'no_reset';
58

69
interface PortInfo {
710
name: string;
@@ -60,6 +63,12 @@ export const useDeviceStore = defineStore('device', {
6063
SF32LB55: ['NOR', 'SD'],
6164
default: ['NOR'],
6265
} as Record<string, string[]>,
66+
67+
// 下载行为设置(保存到设备相关设置)
68+
downloadBehavior: {
69+
before: 'default_reset' as ResetBeforeMode,
70+
after: 'no_reset' as ResetAfterMode,
71+
},
6372
}),
6473

6574
getters: {
@@ -173,6 +182,17 @@ export const useDeviceStore = defineStore('device', {
173182
this.saveToStorage();
174183
},
175184

185+
// 下载行为设置
186+
setDownloadBeforeBehavior(value: ResetBeforeMode) {
187+
this.downloadBehavior = { ...this.downloadBehavior, before: value };
188+
this.saveToStorage();
189+
},
190+
191+
setDownloadAfterBehavior(value: ResetAfterMode) {
192+
this.downloadBehavior = { ...this.downloadBehavior, after: value };
193+
this.saveToStorage();
194+
},
195+
176196
// 下拉框状态管理
177197
setShowChipDropdown(show: boolean) {
178198
this.showChipDropdown = show;
@@ -275,6 +295,12 @@ export const useDeviceStore = defineStore('device', {
275295
this.baudRateInput = baudRateData.value;
276296
}
277297

298+
// 加载下载行为设置
299+
const downloadBehaviorData = await storeInstance.get('downloadBehavior');
300+
if (downloadBehaviorData?.value) {
301+
this.downloadBehavior = downloadBehaviorData.value;
302+
}
303+
278304
console.log('设备设置已从存储加载');
279305
} catch (error) {
280306
console.error('加载设备设置失败:', error);
@@ -314,6 +340,9 @@ export const useDeviceStore = defineStore('device', {
314340
// 保存波特率
315341
await storeInstance.set('baudRateInput', { value: this.baudRateInput });
316342

343+
// 保存下载行为设置
344+
await storeInstance.set('downloadBehavior', { value: this.downloadBehavior });
345+
317346
await storeInstance.save();
318347
} catch (error) {
319348
console.error('保存设备设置失败:', error);
@@ -338,6 +367,8 @@ export const useDeviceStore = defineStore('device', {
338367

339368
this.baudRateInput = '1000000';
340369

370+
this.downloadBehavior = { before: 'default_reset', after: 'no_reset' };
371+
341372
this.isConnected = false;
342373
this.isConnecting = false;
343374

‎src/views/AboutView.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
<div class="w-full text-center mt-auto py-4">
5252
<div class="flex items-center justify-center">
5353
<span class="material-icons text-xs text-gray-500 mr-1">copyright</span>
54-
<span class="text-sm text-gray-500">2025 SiFli Technologies(Nanjing) Co., Ltd</span>
54+
<span class="text-sm text-gray-500">2025-2026 SiFli Technologies(Nanjing) Co., Ltd</span>
5555
</div>
5656
</div>
5757
</div>

0 commit comments

Comments
 (0)