修改部分样式

This commit is contained in:
2025-08-17 13:25:15 +08:00
parent aa2a552820
commit f45aa01cf0
17 changed files with 14003 additions and 13556 deletions

216
API.md
View File

@@ -33,31 +33,31 @@ connect_to_player(config: ConnectionConfig) -> Result<ApiResponse<PlayerState>,
**参数:** **参数:**
```json ```json
{ {
"host": "192.168.1.100", "host": "192.168.1.100",
"port": 8080, "port": 8080,
"timeout": 10, "timeout": 10,
"autoReconnect": true, "autoReconnect": true,
"reconnectInterval": 5 "reconnectInterval": 5
} }
``` ```
**返回:** **返回:**
```json ```json
{ {
"success": true, "success": true,
"data": { "data": {
"connectionStatus": "connected", "connectionStatus": "connected",
"playbackStatus": "stopped", "playbackStatus": "stopped",
"currentVideo": null, "currentVideo": null,
"position": 0.0, "position": 0.0,
"duration": 0.0, "duration": 0.0,
"volume": 50.0, "volume": 50.0,
"isLooping": false, "isLooping": false,
"isFullscreen": false, "isFullscreen": false,
"playlist": [], "playlist": [],
"currentPlaylistIndex": null "currentPlaylistIndex": null
}, },
"message": "成功连接到视频播放器" "message": "成功连接到视频播放器"
} }
``` ```
@@ -88,76 +88,76 @@ send_playback_command(command: PlaybackCommand) -> Result<ApiResponse<()>, Strin
1. **播放** 1. **播放**
```json ```json
{ {
"type": "play" "type": "play"
} }
``` ```
2. **暂停** 2. **暂停**
```json ```json
{ {
"type": "pause" "type": "pause"
} }
``` ```
3. **停止** 3. **停止**
```json ```json
{ {
"type": "stop" "type": "stop"
} }
``` ```
4. **跳转** 4. **跳转**
```json ```json
{ {
"type": "seek", "type": "seek",
"position": 60.5 "position": 60.5
} }
``` ```
5. **设置音量** 5. **设置音量**
```json ```json
{ {
"type": "setVolume", "type": "setVolume",
"volume": 75.0 "volume": 75.0
} }
``` ```
6. **设置循环** 6. **设置循环**
```json ```json
{ {
"type": "setLoop", "type": "setLoop",
"enabled": true "enabled": true
} }
``` ```
7. **切换全屏** 7. **切换全屏**
```json ```json
{ {
"type": "toggleFullscreen" "type": "toggleFullscreen"
} }
``` ```
8. **加载视频** 8. **加载视频**
```json ```json
{ {
"type": "loadVideo", "type": "loadVideo",
"path": "/path/to/video.mp4" "path": "/path/to/video.mp4"
} }
``` ```
9. **设置播放列表** 9. **设置播放列表**
```json ```json
{ {
"type": "setPlaylist", "type": "setPlaylist",
"videos": ["/path/to/video1.mp4", "/path/to/video2.mp4"] "videos": ["/path/to/video1.mp4", "/path/to/video2.mp4"]
} }
``` ```
10. **播放列表中的指定视频** 10. **播放列表中的指定视频**
```json ```json
{ {
"type": "playFromPlaylist", "type": "playFromPlaylist",
"index": 0 "index": 0
} }
``` ```
@@ -266,30 +266,30 @@ async fn send_status_update(status: PlayerState) -> Result<(), Box<dyn Error>>
```json ```json
{ {
"type": "status", "type": "status",
"data": { "data": {
"playbackStatus": "playing|paused|stopped|loading", "playbackStatus": "playing|paused|stopped|loading",
"currentVideo": { "currentVideo": {
"path": "/path/to/current/video.mp4", "path": "/path/to/current/video.mp4",
"title": "Video Title", "title": "Video Title",
"duration": 3600.0, "duration": 3600.0,
"size": 1024000000, "size": 1024000000,
"format": "mp4" "format": "mp4"
}, },
"position": 120.5, "position": 120.5,
"duration": 3600.0, "duration": 3600.0,
"volume": 75.0, "volume": 75.0,
"isLooping": false, "isLooping": false,
"isFullscreen": true, "isFullscreen": true,
"playlist": [ "playlist": [
{ {
"path": "/path/to/video1.mp4", "path": "/path/to/video1.mp4",
"title": "Video 1", "title": "Video 1",
"duration": 1800.0 "duration": 1800.0
} }
], ],
"currentPlaylistIndex": 0 "currentPlaylistIndex": 0
} }
} }
``` ```
@@ -300,23 +300,23 @@ async fn send_status_update(status: PlayerState) -> Result<(), Box<dyn Error>>
#### 视频播放完成 #### 视频播放完成
```json ```json
{ {
"type": "event", "type": "event",
"event": "playbackFinished", "event": "playbackFinished",
"data": { "data": {
"videoPath": "/path/to/finished/video.mp4" "videoPath": "/path/to/finished/video.mp4"
} }
} }
``` ```
#### 播放错误 #### 播放错误
```json ```json
{ {
"type": "event", "type": "event",
"event": "playbackError", "event": "playbackError",
"data": { "data": {
"error": "Failed to load video file", "error": "Failed to load video file",
"videoPath": "/path/to/problematic/video.mp4" "videoPath": "/path/to/problematic/video.mp4"
} }
} }
``` ```
@@ -337,55 +337,55 @@ async fn send_status_update(status: PlayerState) -> Result<(), Box<dyn Error>>
### PlayerState ### PlayerState
```typescript ```typescript
interface PlayerState { interface PlayerState {
connectionStatus: 'connected' | 'connecting' | 'disconnected' | { error: string } connectionStatus: "connected" | "connecting" | "disconnected" | { error: string }
playbackStatus: 'playing' | 'paused' | 'stopped' | 'loading' playbackStatus: "playing" | "paused" | "stopped" | "loading"
currentVideo?: VideoInfo currentVideo?: VideoInfo
position: number position: number
duration: number duration: number
volume: number volume: number
isLooping: boolean isLooping: boolean
isFullscreen: boolean isFullscreen: boolean
playlist: VideoInfo[] playlist: VideoInfo[]
currentPlaylistIndex?: number currentPlaylistIndex?: number
} }
``` ```
### VideoInfo ### VideoInfo
```typescript ```typescript
interface VideoInfo { interface VideoInfo {
path: string path: string
title: string title: string
duration?: number duration?: number
size?: number size?: number
format?: string format?: string
} }
``` ```
### ConnectionConfig ### ConnectionConfig
```typescript ```typescript
interface ConnectionConfig { interface ConnectionConfig {
host: string host: string
port: number port: number
timeout: number timeout: number
autoReconnect: boolean autoReconnect: boolean
reconnectInterval: number reconnectInterval: number
} }
``` ```
### AppSettings ### AppSettings
```typescript ```typescript
interface AppSettings { interface AppSettings {
connection: ConnectionConfig connection: ConnectionConfig
defaultVolume: number defaultVolume: number
defaultLoop: boolean defaultLoop: boolean
autoFullscreen: boolean autoFullscreen: boolean
playbackEndBehavior: 'stop' | 'next' | 'repeat' playbackEndBehavior: "stop" | "next" | "repeat"
theme: string theme: string
language: string language: string
showNotifications: boolean showNotifications: boolean
debugMode: boolean debugMode: boolean
cacheSize: number cacheSize: number
proxy?: string proxy?: string
} }
``` ```
@@ -403,9 +403,9 @@ interface AppSettings {
### 错误响应格式 ### 错误响应格式
```json ```json
{ {
"success": false, "success": false,
"error": "CONNECTION_FAILED", "error": "CONNECTION_FAILED",
"message": "无法连接到视频播放器 192.168.1.100:8080" "message": "无法连接到视频播放器 192.168.1.100:8080"
} }
``` ```
@@ -443,18 +443,18 @@ use futures_util::{StreamExt, SinkExt};
async fn start_player_server() -> Result<(), Box<dyn Error>> { async fn start_player_server() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind("0.0.0.0:8080").await?; let listener = TcpListener::bind("0.0.0.0:8080").await?;
println!("视频播放器服务器启动在 0.0.0.0:8080"); println!("视频播放器服务器启动在 0.0.0.0:8080");
while let Ok((stream, _)) = listener.accept().await { while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(handle_connection(stream)); tokio::spawn(handle_connection(stream));
} }
Ok(()) Ok(())
} }
async fn handle_connection(stream: TcpStream) -> Result<(), Box<dyn Error>> { async fn handle_connection(stream: TcpStream) -> Result<(), Box<dyn Error>> {
let ws_stream = accept_async(stream).await?; let ws_stream = accept_async(stream).await?;
let (mut ws_sender, mut ws_receiver) = ws_stream.split(); let (mut ws_sender, mut ws_receiver) = ws_stream.split();
while let Some(msg) = ws_receiver.next().await { while let Some(msg) = ws_receiver.next().await {
match msg? { match msg? {
Message::Text(text) => { Message::Text(text) => {
@@ -469,7 +469,7 @@ async fn handle_connection(stream: TcpStream) -> Result<(), Box<dyn Error>> {
_ => {} _ => {}
} }
} }
Ok(()) Ok(())
} }
``` ```

View File

@@ -20,7 +20,7 @@ error[E0599]: no method named `emit` found for struct `tauri::Window` in the cur
// 修复前 // 修复前
use tauri::{State, Window}; use tauri::{State, Window};
// 修复后 // 修复后
use tauri::{Emitter, State, Window}; use tauri::{Emitter, State, Window};
``` ```
@@ -131,7 +131,7 @@ cargo build # 完整构建
以下功能完全正常工作: 以下功能完全正常工作:
1. **WebSocket 连接管理** 1. **WebSocket 连接管理**
2. **设置持久化存储** 2. **设置持久化存储**
3. **播放命令发送** 3. **播放命令发送**
4. **状态管理和同步** 4. **状态管理和同步**
5. **视频信息获取** 5. **视频信息获取**

View File

@@ -22,14 +22,14 @@
- 进度条和音量控制 - 进度条和音量控制
- 循环播放、全屏等高级功能 - 循环播放、全屏等高级功能
- 播放列表管理 - 播放列表管理
-**设置页面 (`/settings`)**:全面的应用配置 -**设置页面 (`/settings`)**:全面的应用配置
- 连接设置IP地址、端口、超时等 - 连接设置IP地址、端口、超时等
- 播放设置(默认音量、循环播放等) - 播放设置(默认音量、循环播放等)
- 界面设置(主题、语言等) - 界面设置(主题、语言等)
- 高级设置(调试模式、缓存等) - 高级设置(调试模式、缓存等)
- 设置的导入导出功能 - 设置的导入导出功能
-**关于页面 (`/about`)**:应用信息展示 -**关于页面 (`/about`)**:应用信息展示
- 应用基本信息 - 应用基本信息
- 技术栈展示 - 技术栈展示
@@ -218,7 +218,7 @@ cargo build # 构建 Rust 后端
该视频控制器项目成功搭建了一个现代化的桌面应用基础架构,具备: 该视频控制器项目成功搭建了一个现代化的桌面应用基础架构,具备:
- 完整的用户界面和用户体验 - 完整的用户界面和用户体验
- 健壮的后端API和服务架构 - 健壮的后端API和服务架构
- 详细的开发文档和规范 - 详细的开发文档和规范
- 良好的可扩展性和维护性 - 良好的可扩展性和维护性

View File

@@ -9,6 +9,10 @@ export default defineAppConfig({
colors: { colors: {
primary: "blue", primary: "blue",
neutral: "zinc" neutral: "zinc"
},
icons: {
light: "ph-sun",
dark: "ph-moon"
} }
} }
}); });

View File

@@ -1,5 +1,5 @@
@import "tailwindcss"; @import "tailwindcss";
@import "@nuxt/ui"; @import "@nuxt/ui-pro";
@theme { @theme {
--font-heading: "Montserrat", sans-serif; --font-heading: "Montserrat", sans-serif;

View File

@@ -1,11 +1,20 @@
<template> <template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex"> <div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex">
<!-- 左侧边栏 --> <!-- 左侧边栏 -->
<aside class="w-40 bg-white dark:bg-gray-800 shadow-sm border-r border-gray-200 dark:border-gray-700 flex flex-col h-screen"> <aside
<div class="p-6 border-b border-gray-200 dark:border-gray-700"> class="w-40 bg-white dark:bg-gray-800 shadow-sm border-r border-gray-200 dark:border-gray-700 flex flex-col h-screen fixed top-0 left-0 z-30"
>
<div class="flex items-center justify-between p-3 border-b border-gray-200 dark:border-gray-700">
<h1 class="text-xl font-semibold text-gray-900 dark:text-white"> <h1 class="text-xl font-semibold text-gray-900 dark:text-white">
视频控制器 视频控制器
</h1> </h1>
<div class="mx-auto mt-1">
<UColorModeButton size="xs">
<template #fallback>
<UButton loading variant="ghost" color="neutral" />
</template>
</UColorModeButton>
</div>
</div> </div>
<nav class="flex-1 p-4"> <nav class="flex-1 p-4">
@@ -13,8 +22,7 @@
<li> <li>
<NuxtLink <NuxtLink
to="/" to="/"
class="flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors" class="flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors" :class="$route.path === '/'
:class="$route.path === '/'
? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200' ? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'" : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'"
> >
@@ -25,8 +33,7 @@
<li> <li>
<NuxtLink <NuxtLink
to="/settings" to="/settings"
class="flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors" class="flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors" :class="$route.path === '/settings'
:class="$route.path === '/settings'
? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200' ? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'" : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'"
> >
@@ -36,13 +43,11 @@
</li> </li>
</ul> </ul>
</nav> </nav>
<!-- 关于 --> <!-- 关于 -->
<div class="p-4 border-t border-gray-200 dark:border-gray-700"> <div class="p-4 ">
<NuxtLink <NuxtLink
to="/about" to="/about"
class="flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors" class="flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors" :class="$route.path === '/about'
:class="$route.path === '/about'
? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200' ? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'" : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'"
> >
@@ -53,7 +58,7 @@
</aside> </aside>
<!-- 主体区域 --> <!-- 主体区域 -->
<main class="flex-1 overflow-auto"> <main class="flex-1 overflow-auto ml-40">
<div class="p-6"> <div class="p-6">
<slot /> <slot />
</div> </div>

View File

@@ -3,7 +3,9 @@
<!-- 应用信息 --> <!-- 应用信息 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">应用信息</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
应用信息
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
@@ -11,13 +13,19 @@
<UIcon name="i-heroicons-tv" class="w-8 h-8 text-blue-600 dark:text-blue-400" /> <UIcon name="i-heroicons-tv" class="w-8 h-8 text-blue-600 dark:text-blue-400" />
</div> </div>
<div> <div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">{{ appInfo.name }}</h3> <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
<p class="text-gray-600 dark:text-gray-400">版本 {{ appInfo.version }}</p> {{ appInfo.name }}
</h3>
<p class="text-gray-600 dark:text-gray-400">
版本 {{ appInfo.version }}
</p>
</div> </div>
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<p class="text-gray-700 dark:text-gray-300">{{ appInfo.description }}</p> <p class="text-gray-700 dark:text-gray-300">
{{ appInfo.description }}
</p>
<p class="text-sm text-gray-600 dark:text-gray-400"> <p class="text-sm text-gray-600 dark:text-gray-400">
构建时间: {{ appInfo.buildDate }} 构建时间: {{ appInfo.buildDate }}
</p> </p>
@@ -31,57 +39,63 @@
<!-- 技术栈 --> <!-- 技术栈 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">技术栈</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
技术栈
</h2>
</template> </template>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-3"> <div class="space-y-3">
<h4 class="font-medium text-gray-900 dark:text-white">前端技术</h4> <h4 class="font-medium text-gray-900 dark:text-white">
前端技术
</h4>
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-green-100 dark:bg-green-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-green-100 dark:bg-green-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-green-500 rounded"></div> <div class="w-3 h-3 bg-green-500 rounded" />
</div> </div>
<span class="text-sm">Nuxt 4 - 全栈 Vue.js 框架</span> <span class="text-sm">Nuxt 4 - 全栈 Vue.js 框架</span>
</div> </div>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-blue-100 dark:bg-blue-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-blue-100 dark:bg-blue-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-blue-500 rounded"></div> <div class="w-3 h-3 bg-blue-500 rounded" />
</div> </div>
<span class="text-sm">Vue 3 - 渐进式 JavaScript 框架</span> <span class="text-sm">Vue 3 - 渐进式 JavaScript 框架</span>
</div> </div>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-cyan-100 dark:bg-cyan-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-cyan-100 dark:bg-cyan-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-cyan-500 rounded"></div> <div class="w-3 h-3 bg-cyan-500 rounded" />
</div> </div>
<span class="text-sm">Tailwind CSS - 原子化 CSS 框架</span> <span class="text-sm">Tailwind CSS - 原子化 CSS 框架</span>
</div> </div>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-emerald-100 dark:bg-emerald-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-emerald-100 dark:bg-emerald-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-emerald-500 rounded"></div> <div class="w-3 h-3 bg-emerald-500 rounded" />
</div> </div>
<span class="text-sm">Nuxt UI - 现代化 UI 组件库</span> <span class="text-sm">Nuxt UI - 现代化 UI 组件库</span>
</div> </div>
</div> </div>
</div> </div>
<div class="space-y-3"> <div class="space-y-3">
<h4 class="font-medium text-gray-900 dark:text-white">后端技术</h4> <h4 class="font-medium text-gray-900 dark:text-white">
后端技术
</h4>
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-orange-100 dark:bg-orange-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-orange-100 dark:bg-orange-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-orange-500 rounded"></div> <div class="w-3 h-3 bg-orange-500 rounded" />
</div> </div>
<span class="text-sm">Tauri 2 - 跨平台桌面应用框架</span> <span class="text-sm">Tauri 2 - 跨平台桌面应用框架</span>
</div> </div>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-red-100 dark:bg-red-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-red-100 dark:bg-red-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-red-500 rounded"></div> <div class="w-3 h-3 bg-red-500 rounded" />
</div> </div>
<span class="text-sm">Rust - 系统级编程语言</span> <span class="text-sm">Rust - 系统级编程语言</span>
</div> </div>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-6 h-6 bg-purple-100 dark:bg-purple-900 rounded flex items-center justify-center"> <div class="w-6 h-6 bg-purple-100 dark:bg-purple-900 rounded flex items-center justify-center">
<div class="w-3 h-3 bg-purple-500 rounded"></div> <div class="w-3 h-3 bg-purple-500 rounded" />
</div> </div>
<span class="text-sm">WebSocket - 实时通信协议</span> <span class="text-sm">WebSocket - 实时通信协议</span>
</div> </div>
@@ -93,7 +107,9 @@
<!-- 功能特性 --> <!-- 功能特性 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">功能特性</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
功能特性
</h2>
</template> </template>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-2"> <div class="space-y-2">
@@ -138,11 +154,15 @@
<!-- 系统要求 --> <!-- 系统要求 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">系统要求</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
系统要求
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<div> <div>
<h4 class="font-medium text-gray-900 dark:text-white mb-2">支持的操作系统</h4> <h4 class="font-medium text-gray-900 dark:text-white mb-2">
支持的操作系统
</h4>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="flex items-center space-x-2 text-sm"> <div class="flex items-center space-x-2 text-sm">
<UIcon name="i-heroicons-computer-desktop" class="w-5 h-5 text-gray-500" /> <UIcon name="i-heroicons-computer-desktop" class="w-5 h-5 text-gray-500" />
@@ -158,9 +178,11 @@
</div> </div>
</div> </div>
</div> </div>
<div> <div>
<h4 class="font-medium text-gray-900 dark:text-white mb-2">网络要求</h4> <h4 class="font-medium text-gray-900 dark:text-white mb-2">
网络要求
</h4>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1"> <ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li> 与视频播放器处于同一局域网</li> <li> 与视频播放器处于同一局域网</li>
<li> TCP/IP 网络连接</li> <li> TCP/IP 网络连接</li>
@@ -173,7 +195,9 @@
<!-- 开源许可 --> <!-- 开源许可 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">开源许可</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
开源许可
</h2>
</template> </template>
<div class="space-y-3"> <div class="space-y-3">
<p class="text-sm text-gray-600 dark:text-gray-400"> <p class="text-sm text-gray-600 dark:text-gray-400">
@@ -195,7 +219,9 @@
<!-- 反馈和支持 --> <!-- 反馈和支持 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">反馈和支持</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
反馈和支持
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<p class="text-sm text-gray-600 dark:text-gray-400"> <p class="text-sm text-gray-600 dark:text-gray-400">
@@ -221,32 +247,32 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
interface AppInfo { interface AppInfo {
name: string name: string
version: string version: string
description: string description: string
author: string author: string
buildDate: string buildDate: string
}
const { app } = useAppConfig()
// 应用信息
const appInfo = ref<AppInfo>({
name: app.name,
version: app.version,
description: app.description,
author: app.author,
buildDate: new Date().toLocaleDateString('zh-CN')
})
// 页面加载时获取构建信息
onMounted(async () => {
try {
// TODO: 调用 Tauri API 获取应用构建信息
console.log('获取应用信息')
} catch (error) {
console.error('获取应用信息失败:', error)
} }
})
const { app } = useAppConfig();
// 应用信息
const appInfo = ref<AppInfo>({
name: app.name,
version: app.version,
description: app.description,
author: app.author,
buildDate: new Date().toLocaleDateString("zh-CN")
});
// 页面加载时获取构建信息
onMounted(async () => {
try {
// TODO: 调用 Tauri API 获取应用构建信息
console.log("获取应用信息");
} catch (error) {
console.error("获取应用信息失败:", error);
}
});
</script> </script>

View File

@@ -3,20 +3,22 @@
<!-- 连接状态 --> <!-- 连接状态 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">连接状态</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
连接状态
</h2>
</template> </template>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div :class="connectionStatus === 'connected' ? 'bg-green-500' : connectionStatus === 'connecting' ? 'bg-yellow-500' : 'bg-red-500'" class="w-3 h-3 rounded-full"></div> <div :class="connectionStatus === 'connected' ? 'bg-green-500' : connectionStatus === 'connecting' ? 'bg-yellow-500' : 'bg-red-500'" class="w-3 h-3 rounded-full" />
<span class="text-sm font-medium text-gray-700 dark:text-gray-300"> <span class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ connectionStatusText }} {{ connectionStatusText }}
</span> </span>
</div> </div>
<UButton <UButton
@click="toggleConnection"
:variant="connectionStatus === 'connected' ? 'soft' : 'solid'" :variant="connectionStatus === 'connected' ? 'soft' : 'solid'"
:color="connectionStatus === 'connected' ? 'red' : 'blue'" :color="connectionStatus === 'connected' ? 'red' : 'blue'"
size="sm" size="sm"
@click="toggleConnection"
> >
{{ connectionStatus === 'connected' ? '断开连接' : '连接' }} {{ connectionStatus === 'connected' ? '断开连接' : '连接' }}
</UButton> </UButton>
@@ -29,16 +31,22 @@
<!-- 视频预览区域 --> <!-- 视频预览区域 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">视频预览</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
视频预览
</h2>
</template> </template>
<div class="bg-black rounded-lg aspect-video flex items-center justify-center"> <div class="bg-black rounded-lg aspect-video flex items-center justify-center">
<div v-if="currentVideo" class="text-center"> <div v-if="currentVideo" class="text-center">
<UIcon name="i-heroicons-film" class="w-16 h-16 text-gray-400 mx-auto mb-2" /> <UIcon name="i-heroicons-film" class="w-16 h-16 text-gray-400 mx-auto mb-2" />
<p class="text-white text-sm">{{ currentVideo }}</p> <p class="text-white text-sm">
{{ currentVideo }}
</p>
</div> </div>
<div v-else class="text-center"> <div v-else class="text-center">
<UIcon name="i-heroicons-video-camera-slash" class="w-16 h-16 text-gray-600 mx-auto mb-2" /> <UIcon name="i-heroicons-video-camera-slash" class="w-16 h-16 text-gray-600 mx-auto mb-2" />
<p class="text-gray-400 text-sm">暂无视频</p> <p class="text-gray-400 text-sm">
暂无视频
</p>
</div> </div>
</div> </div>
</UCard> </UCard>
@@ -46,20 +54,22 @@
<!-- 播放控制 --> <!-- 播放控制 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">播放控制</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
播放控制
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<!-- 主要控制按钮 --> <!-- 主要控制按钮 -->
<div class="flex justify-center space-x-4"> <div class="flex justify-center space-x-4">
<UButton @click="playVideo" :disabled="!isConnected" color="green" size="lg"> <UButton :disabled="!isConnected" color="green" size="lg" @click="playVideo">
<UIcon name="i-heroicons-play" class="w-5 h-5 mr-2" /> <UIcon name="i-heroicons-play" class="w-5 h-5 mr-2" />
播放 播放
</UButton> </UButton>
<UButton @click="pauseVideo" :disabled="!isConnected" color="orange" size="lg"> <UButton :disabled="!isConnected" color="orange" size="lg" @click="pauseVideo">
<UIcon name="i-heroicons-pause" class="w-5 h-5 mr-2" /> <UIcon name="i-heroicons-pause" class="w-5 h-5 mr-2" />
暂停 暂停
</UButton> </UButton>
<UButton @click="stopVideo" :disabled="!isConnected" color="red" size="lg"> <UButton :disabled="!isConnected" color="red" size="lg" @click="stopVideo">
<UIcon name="i-heroicons-stop" class="w-5 h-5 mr-2" /> <UIcon name="i-heroicons-stop" class="w-5 h-5 mr-2" />
停止 停止
</UButton> </UButton>
@@ -87,15 +97,15 @@
<!-- 额外功能 --> <!-- 额外功能 -->
<div class="flex flex-wrap gap-2 pt-4 border-t border-gray-200 dark:border-gray-700"> <div class="flex flex-wrap gap-2 pt-4 border-t border-gray-200 dark:border-gray-700">
<UButton @click="toggleLoop" :disabled="!isConnected" :variant="isLooping ? 'solid' : 'outline'" size="sm"> <UButton :disabled="!isConnected" :variant="isLooping ? 'solid' : 'outline'" size="sm" @click="toggleLoop">
<UIcon name="i-heroicons-arrow-path" class="w-4 h-4 mr-1" /> <UIcon name="i-heroicons-arrow-path" class="w-4 h-4 mr-1" />
循环播放 循环播放
</UButton> </UButton>
<UButton @click="toggleFullscreen" :disabled="!isConnected" variant="outline" size="sm"> <UButton :disabled="!isConnected" variant="outline" size="sm" @click="toggleFullscreen">
<UIcon name="i-heroicons-arrows-pointing-out" class="w-4 h-4 mr-1" /> <UIcon name="i-heroicons-arrows-pointing-out" class="w-4 h-4 mr-1" />
全屏 全屏
</UButton> </UButton>
<UButton @click="openVideoFile" variant="outline" size="sm"> <UButton variant="outline" size="sm" @click="openVideoFile">
<UIcon name="i-heroicons-folder-open" class="w-4 h-4 mr-1" /> <UIcon name="i-heroicons-folder-open" class="w-4 h-4 mr-1" />
打开文件 打开文件
</UButton> </UButton>
@@ -107,8 +117,10 @@
<UCard> <UCard>
<template #header> <template #header>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">播放列表</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
<UButton @click="clearPlaylist" variant="ghost" size="sm" color="red"> 播放列表
</h2>
<UButton variant="ghost" size="sm" color="red" @click="clearPlaylist">
<UIcon name="i-heroicons-trash" class="w-4 h-4 mr-1" /> <UIcon name="i-heroicons-trash" class="w-4 h-4 mr-1" />
清空 清空
</UButton> </UButton>
@@ -120,8 +132,8 @@
<p>播放列表为空</p> <p>播放列表为空</p>
</div> </div>
<div v-else> <div v-else>
<div <div
v-for="(item, index) in playlist" v-for="(item, index) in playlist"
:key="index" :key="index"
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg" class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
> >
@@ -130,10 +142,10 @@
<span class="text-sm font-medium">{{ item }}</span> <span class="text-sm font-medium">{{ item }}</span>
</div> </div>
<div class="flex space-x-1"> <div class="flex space-x-1">
<UButton @click="playVideoFromPlaylist(index)" size="xs" variant="ghost"> <UButton size="xs" variant="ghost" @click="playVideoFromPlaylist(index)">
<UIcon name="i-heroicons-play" class="w-4 h-4" /> <UIcon name="i-heroicons-play" class="w-4 h-4" />
</UButton> </UButton>
<UButton @click="removeFromPlaylist(index)" size="xs" variant="ghost" color="red"> <UButton size="xs" variant="ghost" color="red" @click="removeFromPlaylist(index)">
<UIcon name="i-heroicons-x-mark" class="w-4 h-4" /> <UIcon name="i-heroicons-x-mark" class="w-4 h-4" />
</UButton> </UButton>
</div> </div>
@@ -145,108 +157,108 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
interface VideoControllerState { interface VideoControllerState {
connectionStatus: 'connected' | 'connecting' | 'disconnected' connectionStatus: "connected" | "connecting" | "disconnected"
currentVideo: string | null currentVideo: string | null
progress: number progress: number
volume: number volume: number
isLooping: boolean isLooping: boolean
playlist: string[] playlist: string[]
playerAddress: string playerAddress: string
}
// 响应式状态
const connectionStatus = ref<VideoControllerState['connectionStatus']>('disconnected')
const currentVideo = ref<string | null>(null)
const progress = ref(0)
const volume = ref(50)
const isLooping = ref(false)
const playlist = ref<string[]>([])
const playerAddress = ref('192.168.1.100:8080')
// 计算属性
const isConnected = computed(() => connectionStatus.value === 'connected')
const connectionStatusText = computed(() => {
switch (connectionStatus.value) {
case 'connected': return '已连接'
case 'connecting': return '连接中...'
default: return '未连接'
} }
})
// 方法 // 响应式状态
const toggleConnection = async () => { const connectionStatus = ref<VideoControllerState["connectionStatus"]>("disconnected");
if (connectionStatus.value === 'connected') { const currentVideo = ref<string | null>(null);
// 断开连接 const progress = ref(0);
connectionStatus.value = 'disconnected' const volume = ref(50);
} else { const isLooping = ref(false);
// 尝试连接 const playlist = ref<string[]>([]);
connectionStatus.value = 'connecting' const playerAddress = ref("192.168.1.100:8080");
// TODO: 在这里调用 Tauri API 进行实际连接
setTimeout(() => {
connectionStatus.value = 'connected' // 模拟连接成功
}, 1000)
}
}
const playVideo = async () => { // 计算属性
// TODO: 调用 Tauri API const isConnected = computed(() => connectionStatus.value === "connected");
console.log('播放视频')
}
const pauseVideo = async () => { const connectionStatusText = computed(() => {
// TODO: 调用 Tauri API switch (connectionStatus.value) {
console.log('暂停视频') case "connected": return "已连接";
} case "connecting": return "连接中...";
default: return "未连接";
}
});
const stopVideo = async () => { // 方法
// TODO: 调用 Tauri API const toggleConnection = async () => {
console.log('停止视频') if (connectionStatus.value === "connected") {
} // 断开连接
connectionStatus.value = "disconnected";
} else {
// 尝试连接
connectionStatus.value = "connecting";
// TODO: 在这里调用 Tauri API 进行实际连接
setTimeout(() => {
connectionStatus.value = "connected"; // 模拟连接成功
}, 1000);
}
};
const toggleLoop = async () => { const playVideo = async () => {
isLooping.value = !isLooping.value // TODO: 调用 Tauri API
// TODO: 调用 Tauri API console.log("播放视频");
console.log('循环播放:', isLooping.value) };
}
const toggleFullscreen = async () => { const pauseVideo = async () => {
// TODO: 调用 Tauri API // TODO: 调用 Tauri API
console.log('切换全屏') console.log("暂停视频");
} };
const openVideoFile = async () => { const stopVideo = async () => {
// TODO: 调用 Tauri API 打开文件选择器 // TODO: 调用 Tauri API
console.log('打开文件') console.log("停止视频");
// 模拟添加到播放列表 };
playlist.value.push(`示例视频${playlist.value.length + 1}.mp4`)
}
const playVideoFromPlaylist = async (index: number) => { const toggleLoop = async () => {
currentVideo.value = playlist.value[index] isLooping.value = !isLooping.value;
// TODO: 调用 Tauri API 播放指定视频 // TODO: 调用 Tauri API
console.log('播放:', currentVideo.value) console.log("循环播放:", isLooping.value);
} };
const removeFromPlaylist = (index: number) => { const toggleFullscreen = async () => {
playlist.value.splice(index, 1) // TODO: 调用 Tauri API
} console.log("切换全屏");
};
const clearPlaylist = () => { const openVideoFile = async () => {
playlist.value = [] // TODO: 调用 Tauri API 打开文件选择器
currentVideo.value = null console.log("打开文件");
} // 模拟添加到播放列表
playlist.value.push(`示例视频${playlist.value.length + 1}.mp4`);
};
// 监听进度变化 const playVideoFromPlaylist = async (index: number) => {
watch(progress, (newProgress) => { currentVideo.value = playlist.value[index];
// TODO: 调用 Tauri API 设置播放进度 // TODO: 调用 Tauri API 播放指定视频
console.log('设置进度:', newProgress) console.log("播放:", currentVideo.value);
}) };
// 监听音量变化 const removeFromPlaylist = (index: number) => {
watch(volume, (newVolume) => { playlist.value.splice(index, 1);
// TODO: 调用 Tauri API 设置音量 };
console.log('设置音量:', newVolume)
}) const clearPlaylist = () => {
playlist.value = [];
currentVideo.value = null;
};
// 监听进度变化
watch(progress, (newProgress) => {
// TODO: 调用 Tauri API 设置播放进度
console.log("设置进度:", newProgress);
});
// 监听音量变化
watch(volume, (newVolume) => {
// TODO: 调用 Tauri API 设置音量
console.log("设置音量:", newVolume);
});
</script> </script>

View File

@@ -3,7 +3,9 @@
<!-- 连接设置 --> <!-- 连接设置 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">连接设置</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
连接设置
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<UFormGroup label="视频播放器地址" description="设置要连接的视频播放器的IP地址和端口"> <UFormGroup label="视频播放器地址" description="设置要连接的视频播放器的IP地址和端口">
@@ -23,12 +25,12 @@
</UFormGroup> </UFormGroup>
<UFormGroup label="重连间隔 (秒)" description="自动重连的间隔时间"> <UFormGroup label="重连间隔 (秒)" description="自动重连的间隔时间">
<UInput <UInput
v-model.number="settings.reconnectInterval" v-model.number="settings.reconnectInterval"
type="number" type="number"
min="1" min="1"
max="300" max="300"
:disabled="!settings.autoReconnect" :disabled="!settings.autoReconnect"
/> />
</UFormGroup> </UFormGroup>
</div> </div>
@@ -37,7 +39,9 @@
<!-- 播放设置 --> <!-- 播放设置 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">播放设置</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
播放设置
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<UFormGroup label="默认音量" description="新视频播放时的默认音量"> <UFormGroup label="默认音量" description="新视频播放时的默认音量">
@@ -56,7 +60,7 @@
</UFormGroup> </UFormGroup>
<UFormGroup label="播放完成后行为"> <UFormGroup label="播放完成后行为">
<USelectMenu <USelectMenu
v-model="settings.playbackEndBehavior" v-model="settings.playbackEndBehavior"
:options="playbackEndOptions" :options="playbackEndOptions"
option-attribute="label" option-attribute="label"
@@ -69,11 +73,13 @@
<!-- 界面设置 --> <!-- 界面设置 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">界面设置</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
界面设置
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<UFormGroup label="主题模式"> <UFormGroup label="主题模式">
<USelectMenu <USelectMenu
v-model="settings.theme" v-model="settings.theme"
:options="themeOptions" :options="themeOptions"
option-attribute="label" option-attribute="label"
@@ -82,7 +88,7 @@
</UFormGroup> </UFormGroup>
<UFormGroup label="语言"> <UFormGroup label="语言">
<USelectMenu <USelectMenu
v-model="settings.language" v-model="settings.language"
:options="languageOptions" :options="languageOptions"
option-attribute="label" option-attribute="label"
@@ -99,7 +105,9 @@
<!-- 高级设置 --> <!-- 高级设置 -->
<UCard> <UCard>
<template #header> <template #header>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">高级设置</h2> <h2 class="text-lg font-semibold text-gray-900 dark:text-white">
高级设置
</h2>
</template> </template>
<div class="space-y-4"> <div class="space-y-4">
<UFormGroup label="调试模式" description="启用详细的日志记录"> <UFormGroup label="调试模式" description="启用详细的日志记录">
@@ -118,20 +126,20 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="flex justify-between"> <div class="flex justify-between">
<UButton @click="resetSettings" variant="outline" color="gray"> <UButton variant="outline" color="gray" @click="resetSettings">
<UIcon name="i-heroicons-arrow-path" class="w-4 h-4 mr-2" /> <UIcon name="i-heroicons-arrow-path" class="w-4 h-4 mr-2" />
重置设置 重置设置
</UButton> </UButton>
<div class="space-x-2"> <div class="space-x-2">
<UButton @click="exportSettings" variant="outline"> <UButton variant="outline" @click="exportSettings">
<UIcon name="i-heroicons-arrow-up-tray" class="w-4 h-4 mr-2" /> <UIcon name="i-heroicons-arrow-up-tray" class="w-4 h-4 mr-2" />
导出配置 导出配置
</UButton> </UButton>
<UButton @click="importSettings" variant="outline"> <UButton variant="outline" @click="importSettings">
<UIcon name="i-heroicons-arrow-down-tray" class="w-4 h-4 mr-2" /> <UIcon name="i-heroicons-arrow-down-tray" class="w-4 h-4 mr-2" />
导入配置 导入配置
</UButton> </UButton>
<UButton @click="saveSettings" color="blue"> <UButton color="blue" @click="saveSettings">
<UIcon name="i-heroicons-check" class="w-4 h-4 mr-2" /> <UIcon name="i-heroicons-check" class="w-4 h-4 mr-2" />
保存设置 保存设置
</UButton> </UButton>
@@ -141,80 +149,27 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
interface AppSettings { interface AppSettings {
playerHost: string playerHost: string
playerPort: number playerPort: number
connectionTimeout: number connectionTimeout: number
autoReconnect: boolean autoReconnect: boolean
reconnectInterval: number reconnectInterval: number
defaultVolume: number defaultVolume: number
defaultLoop: boolean defaultLoop: boolean
autoFullscreen: boolean autoFullscreen: boolean
playbackEndBehavior: string playbackEndBehavior: string
theme: string theme: string
language: string language: string
showNotifications: boolean showNotifications: boolean
debugMode: boolean debugMode: boolean
cacheSize: number cacheSize: number
proxy: string proxy: string
}
// 响应式设置数据
const settings = ref<AppSettings>({
playerHost: '192.168.1.100',
playerPort: 8080,
connectionTimeout: 10,
autoReconnect: true,
reconnectInterval: 5,
defaultVolume: 50,
defaultLoop: false,
autoFullscreen: false,
playbackEndBehavior: 'stop',
theme: 'system',
language: 'zh-CN',
showNotifications: true,
debugMode: false,
cacheSize: 100,
proxy: ''
})
// 选项数据
const playbackEndOptions = [
{ label: '停止播放', value: 'stop' },
{ label: '播放下一个', value: 'next' },
{ label: '重复播放', value: 'repeat' }
]
const themeOptions = [
{ label: '跟随系统', value: 'system' },
{ label: '浅色模式', value: 'light' },
{ label: '深色模式', value: 'dark' }
]
const languageOptions = [
{ label: '简体中文', value: 'zh-CN' },
{ label: 'English', value: 'en' },
{ label: '日本語', value: 'ja' }
]
// 方法
const saveSettings = async () => {
try {
// TODO: 调用 Tauri API 保存设置
console.log('保存设置:', settings.value)
// 显示成功通知
// useToast().add({ title: '设置已保存', color: 'green' })
} catch (error) {
console.error('保存设置失败:', error)
// 显示错误通知
// useToast().add({ title: '保存失败', color: 'red' })
} }
}
const resetSettings = async () => { // 响应式设置数据
// 重置为默认设置 const settings = ref<AppSettings>({
settings.value = { playerHost: "192.168.1.100",
playerHost: '192.168.1.100',
playerPort: 8080, playerPort: 8080,
connectionTimeout: 10, connectionTimeout: 10,
autoReconnect: true, autoReconnect: true,
@@ -222,47 +177,100 @@ const resetSettings = async () => {
defaultVolume: 50, defaultVolume: 50,
defaultLoop: false, defaultLoop: false,
autoFullscreen: false, autoFullscreen: false,
playbackEndBehavior: 'stop', playbackEndBehavior: "stop",
theme: 'system', theme: "system",
language: 'zh-CN', language: "zh-CN",
showNotifications: true, showNotifications: true,
debugMode: false, debugMode: false,
cacheSize: 100, cacheSize: 100,
proxy: '' proxy: ""
} });
}
const exportSettings = async () => { // 选项数据
try { const playbackEndOptions = [
// TODO: 调用 Tauri API 导出设置到文件 { label: "停止播放", value: "stop" },
console.log('导出设置') { label: "播放下一个", value: "next" },
} catch (error) { { label: "重复播放", value: "repeat" }
console.error('导出设置失败:', error) ];
}
}
const importSettings = async () => { const themeOptions = [
try { { label: "跟随系统", value: "system" },
// TODO: 调用 Tauri API 从文件导入设置 { label: "浅色模式", value: "light" },
console.log('导入设置') { label: "深色模式", value: "dark" }
} catch (error) { ];
console.error('导入设置失败:', error)
}
}
// 页面加载时读取设置 const languageOptions = [
onMounted(async () => { { label: "简体中文", value: "zh-CN" },
try { { label: "English", value: "en" },
// TODO: 调用 Tauri API 读取保存的设置 { label: "日本語", value: "ja" }
console.log('加载设置') ];
} catch (error) {
console.error('加载设置失败:', error)
}
})
// 监听设置变化并自动保存关键设置 // 方法
watch(() => [settings.value.playerHost, settings.value.playerPort], async () => { const saveSettings = async () => {
// 自动保存连接相关设置 try {
await saveSettings() // TODO: 调用 Tauri API 保存设置
}, { debounce: 1000 }) console.log("保存设置:", settings.value);
// 显示成功通知
// useToast().add({ title: '设置已保存', color: 'green' })
} catch (error) {
console.error("保存设置失败:", error);
// 显示错误通知
// useToast().add({ title: '保存失败', color: 'red' })
}
};
const resetSettings = async () => {
// 重置为默认设置
settings.value = {
playerHost: "192.168.1.100",
playerPort: 8080,
connectionTimeout: 10,
autoReconnect: true,
reconnectInterval: 5,
defaultVolume: 50,
defaultLoop: false,
autoFullscreen: false,
playbackEndBehavior: "stop",
theme: "system",
language: "zh-CN",
showNotifications: true,
debugMode: false,
cacheSize: 100,
proxy: ""
};
};
const exportSettings = async () => {
try {
// TODO: 调用 Tauri API 导出设置到文件
console.log("导出设置");
} catch (error) {
console.error("导出设置失败:", error);
}
};
const importSettings = async () => {
try {
// TODO: 调用 Tauri API 从文件导入设置
console.log("导入设置");
} catch (error) {
console.error("导入设置失败:", error);
}
};
// 页面加载时读取设置
onMounted(async () => {
try {
// TODO: 调用 Tauri API 读取保存的设置
console.log("加载设置");
} catch (error) {
console.error("加载设置失败:", error);
}
});
// 监听设置变化并自动保存关键设置
watch(() => [settings.value.playerHost, settings.value.playerPort], async () => {
// 自动保存连接相关设置
await saveSettings();
}, { debounce: 1000 });
</script> </script>

View File

@@ -1,10 +1,10 @@
export default defineNuxtConfig({ export default defineNuxtConfig({
modules: [ modules: [
"@vueuse/nuxt", "@vueuse/nuxt",
"@nuxt/ui",
"nuxt-svgo", "nuxt-svgo",
"reka-ui/nuxt", "reka-ui/nuxt",
"@nuxt/eslint" "@nuxt/eslint",
"@nuxt/ui-pro"
], ],
app: { app: {
head: { head: {

View File

@@ -24,7 +24,7 @@
}, },
"dependencies": { "dependencies": {
"@iconify/vue": "^5.0.0", "@iconify/vue": "^5.0.0",
"@nuxt/ui": "^3.2.0", "@nuxt/ui-pro": "^3.3.2",
"@tauri-apps/api": "^2.6.0", "@tauri-apps/api": "^2.6.0",
"@tauri-apps/plugin-fs": "^2.4.0", "@tauri-apps/plugin-fs": "^2.4.0",
"@tauri-apps/plugin-notification": "^2.3.0", "@tauri-apps/plugin-notification": "^2.3.0",

652
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@ tauri-plugin-os = "2.3.0"
tauri-plugin-fs = "2.4.0" tauri-plugin-fs = "2.4.0"
tauri-plugin-store = "2.3.0" tauri-plugin-store = "2.3.0"
serde_json = "1" serde_json = "1"
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = [ "full" ] }
tokio-tungstenite = "0.23" tokio-tungstenite = "0.23"
futures-util = "0.3" futures-util = "0.3"
log = "0.4" log = "0.4"

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"main":{"identifier":"main","description":"Capabilities for the app window","local":true,"windows":["main","secondary"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","shell:allow-open",{"identifier":"shell:allow-execute","allow":[{"args":["-c",{"validator":"\\S+"}],"cmd":"sh","name":"exec-sh","sidecar":false}]},"notification:default","os:allow-platform","os:allow-arch","os:allow-family","os:allow-version","os:allow-locale","fs:allow-document-read","fs:allow-document-write","store:default","core:webview:allow-create-webview","core:webview:allow-create-webview-window"]}} { "main": { "identifier": "main", "description": "Capabilities for the app window", "local": true, "windows": ["main", "secondary"], "permissions": ["core:path:default", "core:event:default", "core:window:default", "core:app:default", "core:resources:default", "core:menu:default", "core:tray:default", "shell:allow-open", { "identifier": "shell:allow-execute", "allow": [{ "args": ["-c", { "validator": "\\S+" }], "cmd": "sh", "name": "exec-sh", "sidecar": false }] }, "notification:default", "os:allow-platform", "os:allow-arch", "os:allow-family", "os:allow-version", "os:allow-locale", "fs:allow-document-read", "fs:allow-document-write", "store:default", "core:webview:allow-create-webview", "core:webview:allow-create-webview-window"] } }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff