update:重构消息系统,新增微信风格会话列表
parent
1d41132a29
commit
112c648773
@ -0,0 +1,23 @@
|
||||
# Run second Flash Send dev instance for local testing
|
||||
# Port offset: +100
|
||||
|
||||
$env:FLASH_SEND_PORT_OFFSET = "100"
|
||||
$env:FLASH_SEND_INSTANCE = "LocalTest2"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host " Flash Send - Dev Instance 2" -ForegroundColor White
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Port Config:" -ForegroundColor Yellow
|
||||
Write-Host " UDP Broadcast: 53317 (shared)" -ForegroundColor Gray
|
||||
Write-Host " UDP Listen: 53417 (53317 + 100)" -ForegroundColor Gray
|
||||
Write-Host " WebSocket: 53418 (53318 + 100)" -ForegroundColor Gray
|
||||
Write-Host " HTTP: 53419 (53319 + 100)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "Make sure Instance 1 is running in another terminal" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
Set-Location $PSScriptRoot\..
|
||||
|
||||
pnpm tauri dev
|
||||
@ -1,273 +1,313 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { useDeviceStore } from '@/stores/deviceStore'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import {
|
||||
User,
|
||||
FolderOpen,
|
||||
Info,
|
||||
Shield,
|
||||
Check,
|
||||
Edit2,
|
||||
Sun,
|
||||
Moon,
|
||||
Monitor
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const deviceStore = useDeviceStore()
|
||||
|
||||
const isEditingName = ref(false)
|
||||
const editedName = ref('')
|
||||
const isSaving = ref(false)
|
||||
|
||||
// 主题设置 ('system' | 'light' | 'dark')
|
||||
const themeMode = ref(localStorage.getItem('theme') || 'system')
|
||||
|
||||
// 应用主题
|
||||
function applyTheme(mode) {
|
||||
const root = document.documentElement
|
||||
if (mode === 'system') {
|
||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
root.classList.toggle('dark', isDark)
|
||||
} else {
|
||||
root.classList.toggle('dark', mode === 'dark')
|
||||
}
|
||||
}
|
||||
|
||||
// 设置主题
|
||||
function setTheme(mode) {
|
||||
themeMode.value = mode
|
||||
localStorage.setItem('theme', mode)
|
||||
applyTheme(mode)
|
||||
}
|
||||
|
||||
// 监听系统主题变化
|
||||
watch(themeMode, (mode) => applyTheme(mode), { immediate: true })
|
||||
|
||||
// 开始编辑名称
|
||||
function startEditName() {
|
||||
editedName.value = deviceStore.localDevice?.deviceName || ''
|
||||
isEditingName.value = true
|
||||
}
|
||||
|
||||
// 保存名称
|
||||
async function saveName() {
|
||||
if (!editedName.value.trim()) return
|
||||
|
||||
isSaving.value = true
|
||||
try {
|
||||
await deviceStore.updateDeviceName(editedName.value.trim())
|
||||
isEditingName.value = false
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择下载目录
|
||||
async function selectDownloadDir() {
|
||||
const selected = await open({
|
||||
directory: true,
|
||||
multiple: false,
|
||||
defaultPath: deviceStore.config?.downloadDir,
|
||||
})
|
||||
|
||||
if (selected) {
|
||||
await deviceStore.updateDownloadDir(selected)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
editedName.value = deviceStore.localDevice?.deviceName || ''
|
||||
|
||||
// 初始化主题
|
||||
applyTheme(themeMode.value)
|
||||
|
||||
// 监听系统主题变化
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (themeMode.value === 'system') {
|
||||
applyTheme('system')
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full overflow-y-auto">
|
||||
<div class="max-w-2xl mx-auto p-6">
|
||||
<h1 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">设置</h1>
|
||||
<div class="h-full overflow-y-auto bg-surface-50/50 dark:bg-surface-900/50 relative">
|
||||
<!-- 背景装饰 -->
|
||||
<div class="absolute top-0 right-0 w-[500px] h-[500px] bg-primary-200/20 dark:bg-primary-900/10 rounded-full blur-[100px] pointer-events-none -translate-y-1/2 translate-x-1/2"></div>
|
||||
<div class="absolute bottom-0 left-0 w-[300px] h-[300px] bg-primary-300/10 dark:bg-primary-800/5 rounded-full blur-[80px] pointer-events-none translate-y-1/3 -translate-x-1/3"></div>
|
||||
|
||||
<div class="max-w-3xl mx-auto p-8 relative z-10 animate-fade-in">
|
||||
<header class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-surface-900 dark:text-white tracking-tight">设置</h1>
|
||||
<p class="text-surface-500 dark:text-surface-400 mt-2">管理您的设备偏好和应用配置</p>
|
||||
</header>
|
||||
|
||||
<!-- 设备信息 -->
|
||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700 mb-6">
|
||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<User :size="18" />
|
||||
<section class="glass-panel rounded-2xl p-1 mb-8">
|
||||
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||
<div class="p-2 rounded-lg bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400">
|
||||
<User :size="20" />
|
||||
</div>
|
||||
设备信息
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-4">
|
||||
<div class="p-6 space-y-6">
|
||||
<!-- 设备名称 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">设备名称</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center justify-between group">
|
||||
<div>
|
||||
<span class="text-sm font-medium text-surface-700 dark:text-surface-300 block">设备名称</span>
|
||||
<p class="text-xs text-surface-400 dark:text-surface-500 mt-0.5">其他设备将看到此名称</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<template v-if="isEditingName">
|
||||
<div class="relative">
|
||||
<input
|
||||
v-model="editedName"
|
||||
type="text"
|
||||
class="px-3 py-1 border border-surface-200 dark:border-gray-600 dark:bg-gray-700 rounded-lg focus:outline-none focus:border-primary-400 dark:text-white"
|
||||
class="px-4 py-2 bg-surface-50 dark:bg-surface-800 border border-primary-500 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-500/20 text-surface-900 dark:text-white w-48 transition-all"
|
||||
@keyup.enter="saveName"
|
||||
ref="nameInput"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
@click="saveName"
|
||||
:disabled="isSaving"
|
||||
class="p-1.5 rounded-lg bg-primary-500 text-white hover:bg-primary-600 disabled:opacity-50"
|
||||
class="p-2 rounded-lg bg-primary-500 text-white hover:bg-primary-600 disabled:opacity-50 transition-colors shadow-md shadow-primary-500/20"
|
||||
title="保存"
|
||||
>
|
||||
<Check :size="16" />
|
||||
<Check :size="18" />
|
||||
</button>
|
||||
<button
|
||||
@click="isEditingName = false"
|
||||
class="p-2 rounded-lg bg-surface-100 dark:bg-surface-700 text-surface-500 hover:bg-surface-200 dark:hover:bg-surface-600 transition-colors"
|
||||
title="取消"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="font-medium text-gray-900 dark:text-white">
|
||||
<span class="font-semibold text-surface-900 dark:text-white text-lg tracking-tight">
|
||||
{{ deviceStore.localDevice?.deviceName }}
|
||||
</span>
|
||||
<button
|
||||
@click="startEditName"
|
||||
class="p-1.5 rounded-lg hover:bg-surface-100 dark:hover:bg-gray-700 text-gray-400"
|
||||
class="p-2 rounded-lg hover:bg-surface-100 dark:hover:bg-surface-700 text-surface-400 hover:text-primary-500 transition-all opacity-0 group-hover:opacity-100"
|
||||
title="编辑名称"
|
||||
>
|
||||
<Edit2 :size="16" />
|
||||
<Edit2 :size="18" />
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full h-px bg-surface-100 dark:bg-surface-700/50"></div>
|
||||
|
||||
<!-- 设备 ID -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">设备 ID</span>
|
||||
<span class="font-mono text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ deviceStore.localDevice?.deviceId?.slice(0, 8) }}...
|
||||
<span class="text-sm font-medium text-surface-700 dark:text-surface-300">设备 ID</span>
|
||||
<span class="font-mono text-sm bg-surface-100 dark:bg-surface-800 px-3 py-1 rounded-md text-surface-600 dark:text-surface-400 select-all">
|
||||
{{ deviceStore.localDevice?.deviceId }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- IP 地址 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">IP 地址</span>
|
||||
<span class="font-mono text-gray-900 dark:text-white">
|
||||
{{ deviceStore.localDevice?.ip || '未知' }}
|
||||
<span class="text-sm font-medium text-surface-700 dark:text-surface-300">IP 地址</span>
|
||||
<span class="font-mono text-sm bg-surface-100 dark:bg-surface-800 px-3 py-1 rounded-md text-surface-600 dark:text-surface-400">
|
||||
{{ deviceStore.localDevice?.ip || '获取中...' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 端口 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">服务端口</span>
|
||||
<span class="text-gray-900 dark:text-white">
|
||||
WS: {{ deviceStore.config?.wsPort }} / HTTP: {{ deviceStore.config?.httpPort }}
|
||||
</span>
|
||||
<span class="text-sm font-medium text-surface-700 dark:text-surface-300">服务端口</span>
|
||||
<div class="flex gap-2 text-sm text-surface-500">
|
||||
<span class="bg-surface-50 dark:bg-surface-800/50 px-2 py-0.5 rounded border border-surface-200 dark:border-surface-700">WS: {{ deviceStore.config?.wsPort }}</span>
|
||||
<span class="bg-surface-50 dark:bg-surface-800/50 px-2 py-0.5 rounded border border-surface-200 dark:border-surface-700">HTTP: {{ deviceStore.config?.httpPort }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 外观设置 -->
|
||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700 mb-6">
|
||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<Sun :size="18" />
|
||||
<section class="glass-panel rounded-2xl p-1 mb-8">
|
||||
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||
<div class="p-2 rounded-lg bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400">
|
||||
<Sun :size="20" />
|
||||
</div>
|
||||
外观设置
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<span class="text-gray-600 dark:text-gray-400 block mb-3">主题模式</span>
|
||||
<div class="flex gap-2">
|
||||
<div class="p-6">
|
||||
<span class="text-sm font-medium text-surface-700 dark:text-surface-300 block mb-4">主题模式</span>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<button
|
||||
@click="setTheme('system')"
|
||||
:class="[
|
||||
'flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors',
|
||||
themeMode === 'system'
|
||||
? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
|
||||
: 'border-surface-200 dark:border-gray-600 hover:bg-surface-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
|
||||
]"
|
||||
class="relative group flex flex-col items-center justify-center gap-3 p-4 rounded-xl border-2 transition-all duration-300"
|
||||
:class="themeMode === 'system' ? 'border-primary-500 bg-primary-50/50 dark:bg-primary-900/20' : 'border-transparent bg-surface-50 dark:bg-surface-800 hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
>
|
||||
<Monitor :size="18" />
|
||||
<span>跟随系统</span>
|
||||
<div class="w-10 h-10 rounded-full flex items-center justify-center transition-colors" :class="themeMode === 'system' ? 'bg-primary-100 dark:bg-primary-800 text-primary-600 dark:text-primary-300' : 'bg-surface-200 dark:bg-surface-700 text-surface-500'">
|
||||
<Monitor :size="20" />
|
||||
</div>
|
||||
<span class="text-sm font-medium" :class="themeMode === 'system' ? 'text-primary-700 dark:text-primary-300' : 'text-surface-600 dark:text-surface-400'">跟随系统</span>
|
||||
<div v-if="themeMode === 'system'" class="absolute top-2 right-2 text-primary-500"><CheckCircleIcon class="w-5 h-5" /></div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="setTheme('light')"
|
||||
:class="[
|
||||
'flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors',
|
||||
themeMode === 'light'
|
||||
? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
|
||||
: 'border-surface-200 dark:border-gray-600 hover:bg-surface-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
|
||||
]"
|
||||
class="relative group flex flex-col items-center justify-center gap-3 p-4 rounded-xl border-2 transition-all duration-300"
|
||||
:class="themeMode === 'light' ? 'border-primary-500 bg-primary-50/50 dark:bg-primary-900/20' : 'border-transparent bg-surface-50 dark:bg-surface-800 hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
>
|
||||
<Sun :size="18" />
|
||||
<span>明亮</span>
|
||||
<div class="w-10 h-10 rounded-full flex items-center justify-center transition-colors" :class="themeMode === 'light' ? 'bg-primary-100 dark:bg-primary-800 text-primary-600 dark:text-primary-300' : 'bg-surface-200 dark:bg-surface-700 text-surface-500'">
|
||||
<Sun :size="20" />
|
||||
</div>
|
||||
<span class="text-sm font-medium" :class="themeMode === 'light' ? 'text-primary-700 dark:text-primary-300' : 'text-surface-600 dark:text-surface-400'">明亮模式</span>
|
||||
<div v-if="themeMode === 'light'" class="absolute top-2 right-2 text-primary-500"><CheckCircleIcon class="w-5 h-5" /></div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="setTheme('dark')"
|
||||
:class="[
|
||||
'flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors',
|
||||
themeMode === 'dark'
|
||||
? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
|
||||
: 'border-surface-200 dark:border-gray-600 hover:bg-surface-50 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
|
||||
]"
|
||||
class="relative group flex flex-col items-center justify-center gap-3 p-4 rounded-xl border-2 transition-all duration-300"
|
||||
:class="themeMode === 'dark' ? 'border-primary-500 bg-primary-50/50 dark:bg-primary-900/20' : 'border-transparent bg-surface-50 dark:bg-surface-800 hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
>
|
||||
<Moon :size="18" />
|
||||
<span>暗黑</span>
|
||||
<div class="w-10 h-10 rounded-full flex items-center justify-center transition-colors" :class="themeMode === 'dark' ? 'bg-primary-100 dark:bg-primary-800 text-primary-600 dark:text-primary-300' : 'bg-surface-200 dark:bg-surface-700 text-surface-500'">
|
||||
<Moon :size="20" />
|
||||
</div>
|
||||
<span class="text-sm font-medium" :class="themeMode === 'dark' ? 'text-primary-700 dark:text-primary-300' : 'text-surface-600 dark:text-surface-400'">暗黑模式</span>
|
||||
<div v-if="themeMode === 'dark'" class="absolute top-2 right-2 text-primary-500"><CheckCircleIcon class="w-5 h-5" /></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 文件设置 -->
|
||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700 mb-6">
|
||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<FolderOpen :size="18" />
|
||||
<section class="glass-panel rounded-2xl p-1 mb-8">
|
||||
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||
<div class="p-2 rounded-lg bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400">
|
||||
<FolderOpen :size="20" />
|
||||
</div>
|
||||
文件设置
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-4">
|
||||
<!-- 下载目录 -->
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<span class="text-gray-600 dark:text-gray-400">下载保存位置</span>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500 mt-0.5">
|
||||
<div class="flex-1 mr-4">
|
||||
<span class="text-sm font-medium text-surface-700 dark:text-surface-300 block">默认下载目录</span>
|
||||
<div class="mt-2 p-3 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 flex items-center gap-2 text-sm text-surface-600 dark:text-surface-400 break-all">
|
||||
<FolderOpen :size="16" class="shrink-0 opacity-50" />
|
||||
{{ deviceStore.config?.downloadDir }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click="selectDownloadDir"
|
||||
class="px-4 py-2 bg-surface-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-surface-200 dark:hover:bg-gray-600 transition-colors"
|
||||
class="shrink-0 px-4 py-2.5 bg-white dark:bg-surface-700 border border-surface-200 dark:border-surface-600 text-surface-700 dark:text-surface-200 rounded-xl hover:bg-surface-50 dark:hover:bg-surface-600 hover:border-surface-300 dark:hover:border-surface-500 transition-all shadow-sm font-medium"
|
||||
>
|
||||
更改
|
||||
更改目录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 关于 -->
|
||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700">
|
||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<Info :size="18" />
|
||||
关于
|
||||
<section class="glass-panel rounded-2xl p-1 mb-12">
|
||||
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||
<div class="p-2 rounded-lg bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400">
|
||||
<Info :size="20" />
|
||||
</div>
|
||||
关于应用
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-4">
|
||||
<div class="p-6 space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">应用名称</span>
|
||||
<span class="font-medium text-gray-900 dark:text-white">Flash Send</span>
|
||||
<span class="text-surface-600 dark:text-surface-400">应用名称</span>
|
||||
<span class="font-semibold text-surface-900 dark:text-white">Flash Send</span>
|
||||
</div>
|
||||
|
||||
<div class="w-full h-px bg-surface-100 dark:bg-surface-700/50"></div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">版本</span>
|
||||
<span class="text-gray-900 dark:text-white">1.0.0</span>
|
||||
<span class="text-surface-600 dark:text-surface-400">当前版本</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded-full bg-surface-100 dark:bg-surface-800 text-xs font-bold text-surface-600 dark:text-surface-400 uppercase">Beta</span>
|
||||
<span class="font-mono text-surface-900 dark:text-white">v1.0.0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, nextTick } from 'vue'
|
||||
import { useDeviceStore } from '@/stores/deviceStore'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import {
|
||||
User,
|
||||
FolderOpen,
|
||||
Info,
|
||||
Check,
|
||||
Edit2,
|
||||
Sun,
|
||||
Moon,
|
||||
Monitor,
|
||||
CheckCircle as CheckCircleIcon
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const deviceStore = useDeviceStore()
|
||||
|
||||
const isEditingName = ref(false)
|
||||
const editedName = ref('')
|
||||
const isSaving = ref(false)
|
||||
const nameInput = ref(null)
|
||||
|
||||
// 主题设置 ('system' | 'light' | 'dark')
|
||||
const themeMode = ref(localStorage.getItem('theme') || 'system')
|
||||
|
||||
// 应用主题
|
||||
function applyTheme(mode) {
|
||||
const root = document.documentElement
|
||||
if (mode === 'system') {
|
||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
root.classList.toggle('dark', isDark)
|
||||
} else {
|
||||
root.classList.toggle('dark', mode === 'dark')
|
||||
}
|
||||
}
|
||||
|
||||
// 设置主题
|
||||
function setTheme(mode) {
|
||||
themeMode.value = mode
|
||||
localStorage.setItem('theme', mode)
|
||||
applyTheme(mode)
|
||||
}
|
||||
|
||||
// 监听系统主题变化
|
||||
watch(themeMode, (mode) => applyTheme(mode), { immediate: true })
|
||||
|
||||
// 开始编辑名称
|
||||
function startEditName() {
|
||||
editedName.value = deviceStore.localDevice?.deviceName || ''
|
||||
isEditingName.value = true
|
||||
nextTick(() => {
|
||||
nameInput.value?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
// 保存名称
|
||||
async function saveName() {
|
||||
if (!editedName.value.trim()) return
|
||||
|
||||
isSaving.value = true
|
||||
try {
|
||||
await deviceStore.updateDeviceName(editedName.value.trim())
|
||||
isEditingName.value = false
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择下载目录
|
||||
async function selectDownloadDir() {
|
||||
const selected = await open({
|
||||
directory: true,
|
||||
multiple: false,
|
||||
defaultPath: deviceStore.config?.downloadDir,
|
||||
})
|
||||
|
||||
if (selected) {
|
||||
await deviceStore.updateDownloadDir(selected)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
editedName.value = deviceStore.localDevice?.deviceName || ''
|
||||
|
||||
// 初始化主题
|
||||
applyTheme(themeMode.value)
|
||||
|
||||
// 监听系统主题变化
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (themeMode.value === 'system') {
|
||||
applyTheme('system')
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue