167167 {{ desktopOutputDirError }}
168168 </div >
169169 </div >
170+
171+ <div class =" px-3.5 py-3" >
172+ <div class =" flex flex-col gap-1.5 sm:flex-row sm:items-center sm:justify-between" >
173+ <div class =" min-w-0 flex-1" >
174+ <div class =" text-[13px] font-medium text-[#222]" >日志文件</div >
175+ <div class =" mt-0.5 text-[11px] text-[#909090] break-words" >{{ desktopLogFileText }}</div >
176+ </div >
177+ <button
178+ type =" button"
179+ class =" shrink-0 rounded-[6px] border border-[#e2e2e2] bg-white px-2 py-1 text-[12px] text-[#222] transition hover:bg-[#f9f9f9] disabled:cursor-not-allowed disabled:opacity-50"
180+ :disabled =" desktopLogFileLoading || desktopLogFileOpening"
181+ @click =" onOpenBackendLogFile"
182+ >
183+ {{ desktopLogFileOpening ? '打开中...' : '打开日志' }}
184+ </button >
185+ </div >
186+ <div v-if =" desktopLogFileError" class =" mt-1.5 text-[11px] text-red-600 whitespace-pre-wrap" >
187+ {{ desktopLogFileError }}
188+ </div >
189+ </div >
170190 </div >
171191 </section >
172192
271291<script setup>
272292import { DESKTOP_SETTING_AUTO_REALTIME_KEY , DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY , SNS_SETTING_USE_CACHE_KEY , readLocalBoolSetting , writeLocalBoolSetting } from ' ~/utils/desktop-settings'
273293import { readApiBaseOverride , writeApiBaseOverride } from ' ~/utils/api-settings'
294+ import { reportServerErrorFromError } from ' ~/utils/server-error-logging'
274295
275- defineProps ({
296+ const props = defineProps ({
276297 open: {
277298 type: Boolean ,
278299 default: false ,
@@ -331,6 +352,15 @@ const desktopOutputDirText = computed(() => {
331352 return v || ' —'
332353})
333354
355+ const desktopLogFilePath = ref (' ' )
356+ const desktopLogFileLoading = ref (false )
357+ const desktopLogFileOpening = ref (false )
358+ const desktopLogFileError = ref (' ' )
359+ const desktopLogFileText = computed (() => {
360+ const v = String (desktopLogFilePath .value || ' ' ).trim ()
361+ return v || ' —'
362+ })
363+
334364const switchTrackClass = (enabled , disabled = false ) => {
335365 if (disabled) return enabled ? ' bg-[#07b75b] opacity-50 cursor-not-allowed' : ' bg-[#d0d0d0] opacity-50 cursor-not-allowed'
336366 return enabled ? ' bg-[#07b75b] hover:brightness-95' : ' bg-[#d0d0d0] hover:brightness-95'
@@ -376,6 +406,24 @@ const onEscKeydown = (event) => {
376406 handleClose ()
377407}
378408
409+ const fetchAdminEndpoint = async (url , options = {}) => {
410+ const apiBase = useApiBase ()
411+ try {
412+ return await $fetch (url, {
413+ baseURL: apiBase,
414+ ... options,
415+ })
416+ } catch (e) {
417+ await reportServerErrorFromError (e, {
418+ method: options? .method || ' GET' ,
419+ requestUrl: url,
420+ source: ' SettingsDialog' ,
421+ apiBase,
422+ })
423+ throw e
424+ }
425+ }
426+
379427const refreshDesktopAutoLaunch = async () => {
380428 if (! process .client || typeof window === ' undefined' ) return
381429 if (! window .wechatDesktop ? .getAutoLaunch ) return
@@ -452,8 +500,7 @@ const refreshDesktopBackendPort = async () => {
452500 }
453501
454502 try {
455- const apiBase = useApiBase ()
456- const resp = await $fetch (' /admin/port' , { baseURL: apiBase })
503+ const resp = await fetchAdminEndpoint (' /admin/port' )
457504 const n = Number (resp? .port )
458505 const d = Number (resp? .default_port )
459506 if (Number .isInteger (d) && d >= 1 && d <= 65535 ) desktopBackendPortDefault .value = d
@@ -510,6 +557,34 @@ const onDesktopOpenOutputDir = async () => {
510557 }
511558}
512559
560+ const refreshBackendLogFileInfo = async () => {
561+ if (! process .client || typeof window === ' undefined' ) return
562+ desktopLogFileLoading .value = true
563+ desktopLogFileError .value = ' '
564+ try {
565+ const resp = await fetchAdminEndpoint (' /admin/log-file' )
566+ desktopLogFilePath .value = String (resp? .path || ' ' ).trim ()
567+ } catch (e) {
568+ desktopLogFileError .value = e? .message || ' 读取日志文件失败'
569+ } finally {
570+ desktopLogFileLoading .value = false
571+ }
572+ }
573+
574+ const onOpenBackendLogFile = async () => {
575+ if (! process .client || typeof window === ' undefined' ) return
576+ desktopLogFileOpening .value = true
577+ desktopLogFileError .value = ' '
578+ try {
579+ const resp = await fetchAdminEndpoint (' /admin/log-file/open' , { method: ' POST' })
580+ if (resp? .path ) desktopLogFilePath .value = String (resp .path || ' ' ).trim ()
581+ } catch (e) {
582+ desktopLogFileError .value = e? .message || ' 打开日志文件失败'
583+ } finally {
584+ desktopLogFileOpening .value = false
585+ }
586+ }
587+
513588const applyDesktopBackendPort = async () => {
514589 if (! process .client || typeof window === ' undefined' ) return
515590 const raw = String (desktopBackendPortInput .value || ' ' ).trim ()
@@ -526,10 +601,9 @@ const applyDesktopBackendPort = async () => {
526601 return
527602 }
528603
529- const currentApiBase = useApiBase ()
530604 let currentBackendPort = null
531605 try {
532- const info = await $fetch (' /admin/port' , { baseURL : currentApiBase } )
606+ const info = await fetchAdminEndpoint (' /admin/port' )
533607 const p = Number (info? .port )
534608 if (Number .isInteger (p) && p >= 1 && p <= 65535 ) currentBackendPort = p
535609 } catch {}
@@ -540,8 +614,7 @@ const applyDesktopBackendPort = async () => {
540614 })()
541615 const isUiServedByBackend = !! (currentBackendPort && uiPort === currentBackendPort)
542616
543- await $fetch (' /admin/port' , {
544- baseURL: currentApiBase,
617+ await fetchAdminEndpoint (' /admin/port' , {
545618 method: ' POST' ,
546619 body: { port: n },
547620 })
@@ -624,6 +697,11 @@ const onDesktopCheckUpdates = async () => {
624697 await desktopUpdate .manualCheck ()
625698}
626699
700+ watch (() => props .open , async (isOpen ) => {
701+ if (! isOpen) return
702+ await refreshBackendLogFileInfo ()
703+ }, { immediate: true })
704+
627705onMounted (async () => {
628706 if (process .client && typeof window !== ' undefined' ) {
629707 const isElectron = / electron/ i .test (String (navigator .userAgent || ' ' ))
0 commit comments