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" >
446523</template >
447524
448525<script setup lang="ts">
449- import { onMounted , computed } from ' vue' ;
526+ import { onMounted , computed , ref } from ' vue' ;
450527import { storeToRefs } from ' pinia' ;
451528import { useI18n } from ' vue-i18n' ;
452529import { invoke } from ' @tauri-apps/api/core' ;
453530import { useLogStore } from ' ../stores/logStore' ;
454531import { useDeviceStore } from ' ../stores/deviceStore' ;
532+ import type { ResetBeforeMode , ResetAfterMode } from ' ../stores/deviceStore' ;
455533import { useStubConfigStore } from ' ../stores/stubConfigStore' ;
456534import { WindowManager } from ' ../services/windowManager' ;
457535import 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+
497588const {
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 );
0 commit comments