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>
|
<template>
|
||||||
<div class="h-full overflow-y-auto">
|
<div class="h-full overflow-y-auto bg-surface-50/50 dark:bg-surface-900/50 relative">
|
||||||
<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="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">
|
<section class="glass-panel rounded-2xl p-1 mb-8">
|
||||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||||
<User :size="18" />
|
<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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4 space-y-4">
|
<div class="p-6 space-y-6">
|
||||||
<!-- 设备名称 -->
|
<!-- 设备名称 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between group">
|
||||||
<span class="text-gray-600 dark:text-gray-400">设备名称</span>
|
<div>
|
||||||
<div class="flex items-center gap-2">
|
<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">
|
<template v-if="isEditingName">
|
||||||
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
v-model="editedName"
|
v-model="editedName"
|
||||||
type="text"
|
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"
|
@keyup.enter="saveName"
|
||||||
|
ref="nameInput"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-1">
|
||||||
<button
|
<button
|
||||||
@click="saveName"
|
@click="saveName"
|
||||||
:disabled="isSaving"
|
: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>
|
||||||
|
<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>
|
||||||
<template v-else>
|
<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 }}
|
{{ deviceStore.localDevice?.deviceName }}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
@click="startEditName"
|
@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>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full h-px bg-surface-100 dark:bg-surface-700/50"></div>
|
||||||
|
|
||||||
<!-- 设备 ID -->
|
<!-- 设备 ID -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-gray-600 dark:text-gray-400">设备 ID</span>
|
<span class="text-sm font-medium text-surface-700 dark:text-surface-300">设备 ID</span>
|
||||||
<span class="font-mono text-sm text-gray-500 dark:text-gray-400">
|
<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?.slice(0, 8) }}...
|
{{ deviceStore.localDevice?.deviceId }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- IP 地址 -->
|
<!-- IP 地址 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-gray-600 dark:text-gray-400">IP 地址</span>
|
<span class="text-sm font-medium text-surface-700 dark:text-surface-300">IP 地址</span>
|
||||||
<span class="font-mono text-gray-900 dark:text-white">
|
<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 || '未知' }}
|
{{ deviceStore.localDevice?.ip || '获取中...' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 端口 -->
|
<!-- 端口 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-gray-600 dark:text-gray-400">服务端口</span>
|
<span class="text-sm font-medium text-surface-700 dark:text-surface-300">服务端口</span>
|
||||||
<span class="text-gray-900 dark:text-white">
|
<div class="flex gap-2 text-sm text-surface-500">
|
||||||
WS: {{ deviceStore.config?.wsPort }} / HTTP: {{ deviceStore.config?.httpPort }}
|
<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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 外观设置 -->
|
<!-- 外观设置 -->
|
||||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700 mb-6">
|
<section class="glass-panel rounded-2xl p-1 mb-8">
|
||||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||||
<Sun :size="18" />
|
<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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-6">
|
||||||
<span class="text-gray-600 dark:text-gray-400 block mb-3">主题模式</span>
|
<span class="text-sm font-medium text-surface-700 dark:text-surface-300 block mb-4">主题模式</span>
|
||||||
<div class="flex gap-2">
|
<div class="grid grid-cols-3 gap-4">
|
||||||
<button
|
<button
|
||||||
@click="setTheme('system')"
|
@click="setTheme('system')"
|
||||||
:class="[
|
class="relative group flex flex-col items-center justify-center gap-3 p-4 rounded-xl border-2 transition-all duration-300"
|
||||||
'flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors',
|
: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'"
|
||||||
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'
|
|
||||||
]"
|
|
||||||
>
|
>
|
||||||
<Monitor :size="18" />
|
<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'">
|
||||||
<span>跟随系统</span>
|
<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>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@click="setTheme('light')"
|
@click="setTheme('light')"
|
||||||
:class="[
|
class="relative group flex flex-col items-center justify-center gap-3 p-4 rounded-xl border-2 transition-all duration-300"
|
||||||
'flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors',
|
: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'"
|
||||||
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'
|
|
||||||
]"
|
|
||||||
>
|
>
|
||||||
<Sun :size="18" />
|
<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'">
|
||||||
<span>明亮</span>
|
<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>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@click="setTheme('dark')"
|
@click="setTheme('dark')"
|
||||||
:class="[
|
class="relative group flex flex-col items-center justify-center gap-3 p-4 rounded-xl border-2 transition-all duration-300"
|
||||||
'flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border transition-colors',
|
: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'"
|
||||||
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'
|
|
||||||
]"
|
|
||||||
>
|
>
|
||||||
<Moon :size="18" />
|
<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'">
|
||||||
<span>暗黑</span>
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 文件设置 -->
|
<!-- 文件设置 -->
|
||||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700 mb-6">
|
<section class="glass-panel rounded-2xl p-1 mb-8">
|
||||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||||
<FolderOpen :size="18" />
|
<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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4 space-y-4">
|
<div class="p-6">
|
||||||
<!-- 下载目录 -->
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div class="flex-1 mr-4">
|
||||||
<span class="text-gray-600 dark:text-gray-400">下载保存位置</span>
|
<span class="text-sm font-medium text-surface-700 dark:text-surface-300 block">默认下载目录</span>
|
||||||
<p class="text-sm text-gray-400 dark:text-gray-500 mt-0.5">
|
<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 }}
|
{{ deviceStore.config?.downloadDir }}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@click="selectDownloadDir"
|
@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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 关于 -->
|
<!-- 关于 -->
|
||||||
<section class="bg-white dark:bg-gray-800 rounded-xl border border-surface-200 dark:border-gray-700">
|
<section class="glass-panel rounded-2xl p-1 mb-12">
|
||||||
<div class="p-4 border-b border-surface-100 dark:border-gray-700">
|
<div class="px-5 py-4 border-b border-surface-100 dark:border-surface-700/50">
|
||||||
<h2 class="font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
<h2 class="font-semibold text-lg text-surface-900 dark:text-white flex items-center gap-2.5">
|
||||||
<Info :size="18" />
|
<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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4 space-y-4">
|
<div class="p-6 space-y-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-gray-600 dark:text-gray-400">应用名称</span>
|
<span class="text-surface-600 dark:text-surface-400">应用名称</span>
|
||||||
<span class="font-medium text-gray-900 dark:text-white">Flash Send</span>
|
<span class="font-semibold text-surface-900 dark:text-white">Flash Send</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full h-px bg-surface-100 dark:bg-surface-700/50"></div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-gray-600 dark:text-gray-400">版本</span>
|
<span class="text-surface-600 dark:text-surface-400">当前版本</span>
|
||||||
<span class="text-gray-900 dark:text-white">1.0.0</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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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