feat(settings): 新增设置页面和相关功能
- 添加设置页面组件和路由 - 实现设置数据的获取和更新逻辑 - 新增多个设置选项,包括功能设置、通知设置、更新设置等 - 优化设置页面样式和交互
This commit is contained in:
100
frontend/src/composables/useSettingsApi.ts
Normal file
100
frontend/src/composables/useSettingsApi.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { SettingsData, GetSettingsResponse, UpdateSettingsResponse } from '../types/settings.ts'
|
||||
|
||||
const API_BASE_URL = 'http://localhost:8000/api'
|
||||
|
||||
export function useSettingsApi() {
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// 获取设置
|
||||
const getSettings = async (): Promise<SettingsData | null> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/setting/get`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({}), // 空请求体
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const apiResponse: GetSettingsResponse = await response.json()
|
||||
|
||||
// 根据code判断是否成功(非200就是不成功)
|
||||
if (apiResponse.code !== 200) {
|
||||
const errorMsg = apiResponse.message || '获取设置失败'
|
||||
message.error(errorMsg)
|
||||
throw new Error(errorMsg)
|
||||
}
|
||||
|
||||
return apiResponse.data
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : '获取设置失败'
|
||||
error.value = errorMsg
|
||||
if (!err.message?.includes('HTTP error')) {
|
||||
message.error(errorMsg)
|
||||
}
|
||||
return null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设置
|
||||
const updateSettings = async (settings: Partial<SettingsData>): Promise<boolean> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/setting/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
data: settings,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const apiResponse: UpdateSettingsResponse = await response.json()
|
||||
|
||||
// 根据code判断是否成功(非200就是不成功)
|
||||
if (apiResponse.code !== 200) {
|
||||
const errorMsg = apiResponse.message || '设置修改失败'
|
||||
message.error(errorMsg)
|
||||
throw new Error(errorMsg)
|
||||
}
|
||||
|
||||
// message.success(apiResponse.message || '设置修改成功')
|
||||
return true
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : '设置修改失败'
|
||||
error.value = errorMsg
|
||||
if (!err.message?.includes('HTTP error')) {
|
||||
message.error(errorMsg)
|
||||
}
|
||||
return false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
error,
|
||||
getSettings,
|
||||
updateSettings,
|
||||
}
|
||||
}
|
||||
@@ -182,4 +182,4 @@ export interface UpdateScriptResponse {
|
||||
code: number
|
||||
status: string
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
69
frontend/src/types/settings.ts
Normal file
69
frontend/src/types/settings.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
// 设置相关类型定义
|
||||
export interface SettingsData {
|
||||
Function: {
|
||||
BossKey: string
|
||||
HistoryRetentionTime: number
|
||||
HomeImageMode: string
|
||||
IfAgreeBilibili: boolean
|
||||
IfAllowSleep: boolean
|
||||
IfSilence: boolean
|
||||
IfSkipMumuSplashAds: boolean
|
||||
UnattendedMode: boolean
|
||||
}
|
||||
Notify: {
|
||||
AuthorizationCode: string
|
||||
CompanyWebHookBotUrl: string
|
||||
FromAddress: string
|
||||
IfCompanyWebHookBot: boolean
|
||||
IfPushPlyer: boolean
|
||||
IfSendMail: boolean
|
||||
IfSendSixStar: boolean
|
||||
IfSendStatistic: boolean
|
||||
IfServerChan: boolean
|
||||
SMTPServerAddress: string
|
||||
SendTaskResultTime: string
|
||||
ServerChanChannel: string
|
||||
ServerChanKey: string
|
||||
ServerChanTag: string
|
||||
ToAddress: string
|
||||
}
|
||||
Update: {
|
||||
IfAutoUpdate: boolean
|
||||
MirrorChyanCDK: string
|
||||
ProxyAddress: string
|
||||
ProxyUrlList: string[]
|
||||
ThreadNumb: number
|
||||
UpdateType: string
|
||||
}
|
||||
Start: {
|
||||
IfMinimizeDirectly: boolean
|
||||
IfSelfStart: boolean
|
||||
}
|
||||
UI: {
|
||||
IfShowTray: boolean
|
||||
IfToTray: boolean
|
||||
location: string
|
||||
maximized: boolean
|
||||
size: string
|
||||
}
|
||||
Voice: {
|
||||
Enabled: boolean
|
||||
Type: string
|
||||
}
|
||||
}
|
||||
|
||||
// 获取设置API响应
|
||||
export interface GetSettingsResponse {
|
||||
code: number
|
||||
status: string
|
||||
message: string
|
||||
data: SettingsData
|
||||
}
|
||||
|
||||
// 更新设置API响应
|
||||
export interface UpdateSettingsResponse {
|
||||
code: number
|
||||
status: string
|
||||
message: string
|
||||
}
|
||||
@@ -1,33 +1,129 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { Button, Card, Divider, Radio, Select, Space, Switch, Tabs } from 'ant-design-vue'
|
||||
import { ref, computed, onMounted, reactive } from 'vue'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Radio,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Tabs,
|
||||
InputNumber,
|
||||
Input,
|
||||
message,
|
||||
} from 'ant-design-vue'
|
||||
import type { ThemeColor, ThemeMode } from '../composables/useTheme'
|
||||
import { useTheme } from '../composables/useTheme.ts'
|
||||
import { useSettingsApi } from '../composables/useSettingsApi'
|
||||
import type { SelectValue } from 'ant-design-vue/es/select'
|
||||
import type { SettingsData } from '../types/settings'
|
||||
|
||||
const { themeMode, themeColor, themeColors, setThemeMode, setThemeColor, isDark } = useTheme()
|
||||
const { loading, getSettings, updateSettings } = useSettingsApi()
|
||||
|
||||
// 主题感知的颜色
|
||||
const textColor = computed(() =>
|
||||
isDark.value ? 'rgba(255, 255, 255, 0.88)' : 'rgba(0, 0, 0, 0.88)'
|
||||
)
|
||||
const textSecondaryColor = computed(() =>
|
||||
isDark.value ? 'rgba(255, 255, 255, 0.65)' : 'rgba(0, 0, 0, 0.65)'
|
||||
)
|
||||
const textTertiaryColor = computed(() =>
|
||||
isDark.value ? 'rgba(255, 255, 255, 0.45)' : 'rgba(0, 0, 0, 0.45)'
|
||||
)
|
||||
|
||||
const activeKey = ref('basic')
|
||||
|
||||
// 主题模式选项
|
||||
const settings = reactive<SettingsData>({
|
||||
Function: {
|
||||
BossKey: '',
|
||||
HistoryRetentionTime: 0,
|
||||
HomeImageMode: '默认',
|
||||
IfAgreeBilibili: false,
|
||||
IfAllowSleep: false,
|
||||
IfSilence: false,
|
||||
IfSkipMumuSplashAds: false,
|
||||
UnattendedMode: false,
|
||||
},
|
||||
Notify: {
|
||||
AuthorizationCode: '',
|
||||
CompanyWebHookBotUrl: '',
|
||||
FromAddress: '',
|
||||
IfCompanyWebHookBot: false,
|
||||
IfPushPlyer: false,
|
||||
IfSendMail: false,
|
||||
IfSendSixStar: false,
|
||||
IfSendStatistic: false,
|
||||
IfServerChan: false,
|
||||
SMTPServerAddress: '',
|
||||
SendTaskResultTime: '不推送',
|
||||
ServerChanChannel: '',
|
||||
ServerChanKey: '',
|
||||
ServerChanTag: '',
|
||||
ToAddress: '',
|
||||
},
|
||||
Update: {
|
||||
IfAutoUpdate: false,
|
||||
MirrorChyanCDK: '',
|
||||
ProxyAddress: '',
|
||||
ProxyUrlList: [],
|
||||
ThreadNumb: 8,
|
||||
UpdateType: 'stable',
|
||||
},
|
||||
Start: {
|
||||
IfMinimizeDirectly: false,
|
||||
IfSelfStart: false,
|
||||
},
|
||||
UI: {
|
||||
IfShowTray: false,
|
||||
IfToTray: false,
|
||||
location: '100x100',
|
||||
maximized: false,
|
||||
size: '1200x700',
|
||||
},
|
||||
Voice: {
|
||||
Enabled: false,
|
||||
Type: 'simple',
|
||||
},
|
||||
})
|
||||
|
||||
// 选项配置
|
||||
const homeImageModeOptions = [
|
||||
{ label: '默认', value: '默认' },
|
||||
{ label: '自定义', value: '自定义' },
|
||||
{ label: '主题图像', value: '主题图像' },
|
||||
]
|
||||
|
||||
const historyRetentionOptions = [
|
||||
{ label: '7天', value: 7 },
|
||||
{ label: '15天', value: 15 },
|
||||
{ label: '30天', value: 30 },
|
||||
{ label: '60天', value: 60 },
|
||||
{ label: '90天', value: 90 },
|
||||
{ label: '180天', value: 180 },
|
||||
{ label: '365天', value: 365 },
|
||||
{ label: '永久保留', value: 0 },
|
||||
]
|
||||
|
||||
const sendTaskResultTimeOptions = [
|
||||
{ label: '不推送', value: '不推送' },
|
||||
{ label: '任何时刻', value: '任何时刻' },
|
||||
{ label: '仅失败时', value: '仅失败时' },
|
||||
]
|
||||
|
||||
const updateTypeOptions = [
|
||||
{ label: '稳定版', value: 'stable' },
|
||||
{ label: '测试版', value: 'beta' },
|
||||
]
|
||||
|
||||
const voiceTypeOptions = [
|
||||
{ label: '简单', value: 'simple' },
|
||||
{ label: '详细', value: 'noisy' },
|
||||
]
|
||||
|
||||
const themeModeOptions = [
|
||||
{ label: '跟随系统', value: 'system' },
|
||||
{ label: '浅色模式', value: 'light' },
|
||||
{ label: '深色模式', value: 'dark' },
|
||||
]
|
||||
|
||||
// 主题色中文映射
|
||||
const themeColorLabels: Record<ThemeColor, string> = {
|
||||
blue: '蓝色',
|
||||
purple: '紫色',
|
||||
@@ -41,16 +137,41 @@ const themeColorLabels: Record<ThemeColor, string> = {
|
||||
volcano: '火山红',
|
||||
geekblue: '极客蓝',
|
||||
lime: '青柠',
|
||||
gold: '金色'
|
||||
gold: '金色',
|
||||
}
|
||||
|
||||
// 主题色选项
|
||||
const themeColorOptions = Object.entries(themeColors).map(([key, color]) => ({
|
||||
label: themeColorLabels[key as ThemeColor],
|
||||
value: key,
|
||||
color,
|
||||
}))
|
||||
|
||||
const loadSettings = async () => {
|
||||
const data = await getSettings()
|
||||
if (data) {
|
||||
Object.assign(settings, data)
|
||||
}
|
||||
}
|
||||
|
||||
const saveSettings = async (category: keyof SettingsData, changes: any) => {
|
||||
try {
|
||||
const updateData = { [category]: changes }
|
||||
const result = await updateSettings(updateData)
|
||||
if (result) {
|
||||
// message.success('设置保存成功')
|
||||
} else {
|
||||
message.error('设置保存失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('设置保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleSettingChange = async (category: keyof SettingsData, key: string, value: any) => {
|
||||
const changes = { [key]: value }
|
||||
await saveSettings(category, changes)
|
||||
}
|
||||
|
||||
const handleThemeModeChange = (e: any) => {
|
||||
setThemeMode(e.target.value as ThemeMode)
|
||||
}
|
||||
@@ -62,120 +183,239 @@ const handleThemeColorChange = (value: SelectValue) => {
|
||||
}
|
||||
|
||||
const openDevTools = () => {
|
||||
// 通过 Electron 的 preload 脚本调用开发者工具
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI.openDevTools()
|
||||
if ((window as any).electronAPI) {
|
||||
; (window as any).electronAPI.openDevTools()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSettings()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="settings-container">
|
||||
<h1>设置</h1>
|
||||
|
||||
<Tabs v-model:activeKey="activeKey" type="card">
|
||||
<Tabs v-model:activeKey="activeKey" type="card" :loading="loading">
|
||||
<!-- 基础设置 -->
|
||||
<Tabs.TabPane key="basic" tab="基础设置">
|
||||
<Card title="外观设置" :bordered="false">
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<!-- 主题模式 -->
|
||||
<div class="setting-item">
|
||||
<h4>主题模式</h4>
|
||||
<p class="setting-description">选择应用程序的外观主题</p>
|
||||
<Radio.Group
|
||||
:value="themeMode"
|
||||
@change="handleThemeModeChange"
|
||||
:options="themeModeOptions"
|
||||
/>
|
||||
<Radio.Group :value="themeMode" @change="handleThemeModeChange" :options="themeModeOptions" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 主题色 -->
|
||||
<div class="setting-item">
|
||||
<h4>主题色</h4>
|
||||
<p class="setting-description">选择应用程序的主色调</p>
|
||||
<Select :value="themeColor" @change="handleThemeColorChange" style="width: 200px">
|
||||
<Select.Option
|
||||
v-for="option in themeColorOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
<Select.Option v-for="option in themeColorOptions" :key="option.value" :value="option.value">
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<div
|
||||
:style="{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: option.color,
|
||||
}"
|
||||
/>
|
||||
<div :style="{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: option.color,
|
||||
}" />
|
||||
{{ option.label }}
|
||||
</div>
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 功能设置 -->
|
||||
<Tabs.TabPane key="function" tab="功能设置">
|
||||
<Card title="功能配置" :bordered="false">
|
||||
<Space direction="vertical" size="large" style="width: 100%">
|
||||
<div class="setting-item">
|
||||
<h4>Boss键</h4>
|
||||
<p class="setting-description">设置快速隐藏窗口的快捷键</p>
|
||||
<Input v-model:value="settings.Function.BossKey"
|
||||
@blur="handleSettingChange('Function', 'BossKey', settings.Function.BossKey)" placeholder="例如: Ctrl+H"
|
||||
style="width: 300px" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 语言设置 -->
|
||||
<div class="setting-item">
|
||||
<h4>语言</h4>
|
||||
<p class="setting-description">选择应用程序界面语言</p>
|
||||
<Select value="zh-CN" style="width: 200px" disabled>
|
||||
<Select.Option value="zh-CN">简体中文</Select.Option>
|
||||
<Select.Option value="en-US">English</Select.Option>
|
||||
</Select>
|
||||
<p class="setting-note">多语言支持即将推出</p>
|
||||
<h4>历史记录保留时间</h4>
|
||||
<p class="setting-description">设置历史记录的保留时间</p>
|
||||
<Select v-model:value="settings.Function.HistoryRetentionTime"
|
||||
@change="(value) => handleSettingChange('Function', 'HistoryRetentionTime', value)"
|
||||
:options="historyRetentionOptions" style="width: 200px" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>主页图像模式</h4>
|
||||
<p class="setting-description">选择主页显示的图像模式</p>
|
||||
<Select v-model:value="settings.Function.HomeImageMode"
|
||||
@change="(value) => handleSettingChange('Function', 'HomeImageMode', value)"
|
||||
:options="homeImageModeOptions" style="width: 200px" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>功能开关</h4>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfAllowSleep"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfAllowSleep', checked)" />
|
||||
<span class="switch-label">启动时阻止系统休眠</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfSilence"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfSilence', checked)" />
|
||||
<span class="switch-label">静默模式</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.UnattendedMode"
|
||||
@change="(checked) => handleSettingChange('Function', 'UnattendedMode', checked)" />
|
||||
<span class="switch-label">无人值守模式</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfAgreeBilibili"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfAgreeBilibili', checked)" />
|
||||
<span class="switch-label">托管Bilibili游戏隐私政策</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfSkipMumuSplashAds"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfSkipMumuSplashAds', checked)" />
|
||||
<span class="switch-label">跳过MuMu模拟器启动广告</span>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 通知设置 -->
|
||||
<Tabs.TabPane key="notification" tab="通知设置">
|
||||
<Tabs.TabPane key="notify" tab="通知设置">
|
||||
<Card title="通知配置" :bordered="false">
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<!-- 桌面通知 -->
|
||||
<Space direction="vertical" size="large" style="width: 100%">
|
||||
<div class="setting-item">
|
||||
<h4>桌面通知</h4>
|
||||
<p class="setting-description">启用系统桌面通知</p>
|
||||
<Switch :checked="true" />
|
||||
<h4>任务结果推送时间</h4>
|
||||
<p class="setting-description">设置何时推送任务执行结果</p>
|
||||
<Select v-model:value="settings.Notify.SendTaskResultTime"
|
||||
@change="(value) => handleSettingChange('Notify', 'SendTaskResultTime', value)"
|
||||
:options="sendTaskResultTimeOptions" style="width: 200px" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 声音提醒 -->
|
||||
<div class="setting-item">
|
||||
<h4>声音提醒</h4>
|
||||
<p class="setting-description">任务完成时播放提示音</p>
|
||||
<Switch :checked="false" />
|
||||
<h4>通知开关</h4>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfSendStatistic"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfSendStatistic', checked)" />
|
||||
<span class="switch-label">发送统计信息</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfSendSixStar"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfSendSixStar', checked)" />
|
||||
<span class="switch-label">发送六星通知</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfPushPlyer"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfPushPlyer', checked)" />
|
||||
<span class="switch-label">启用PushPlus推送</span>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 通知类型 -->
|
||||
<div class="setting-item">
|
||||
<h4>通知类型</h4>
|
||||
<p class="setting-description">选择需要接收通知的事件类型</p>
|
||||
<div style="margin-top: 12px">
|
||||
<div style="margin-bottom: 8px">
|
||||
<Switch :checked="true" style="margin-right: 8px" />
|
||||
任务执行完成
|
||||
<h4>邮件通知</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfSendMail"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfSendMail', checked)" />
|
||||
<span class="switch-label">启用邮件通知</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 8px">
|
||||
<Switch :checked="true" style="margin-right: 8px" />
|
||||
任务执行失败
|
||||
<div class="input-group">
|
||||
<label>SMTP服务器地址</label>
|
||||
<Input v-model:value="settings.Notify.SMTPServerAddress"
|
||||
@blur="handleSettingChange('Notify', 'SMTPServerAddress', settings.Notify.SMTPServerAddress)"
|
||||
placeholder="例如: smtp.gmail.com" style="width: 300px" />
|
||||
</div>
|
||||
<div style="margin-bottom: 8px">
|
||||
<Switch :checked="false" style="margin-right: 8px" />
|
||||
计划任务开始
|
||||
<div class="input-group">
|
||||
<label>授权码</label>
|
||||
<Input.Password v-model:value="settings.Notify.AuthorizationCode"
|
||||
@blur="handleSettingChange('Notify', 'AuthorizationCode', settings.Notify.AuthorizationCode)"
|
||||
placeholder="邮箱授权码" style="width: 300px" />
|
||||
</div>
|
||||
<div>
|
||||
<Switch :checked="false" style="margin-right: 8px" />
|
||||
系统状态变更
|
||||
<div class="input-group">
|
||||
<label>发件人地址</label>
|
||||
<Input v-model:value="settings.Notify.FromAddress"
|
||||
@blur="handleSettingChange('Notify', 'FromAddress', settings.Notify.FromAddress)"
|
||||
placeholder="发件人邮箱地址" style="width: 300px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>收件人地址</label>
|
||||
<Input v-model:value="settings.Notify.ToAddress"
|
||||
@blur="handleSettingChange('Notify', 'ToAddress', settings.Notify.ToAddress)" placeholder="收件人邮箱地址"
|
||||
style="width: 300px" />
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>Server酱通知</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfServerChan"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfServerChan', checked)" />
|
||||
<span class="switch-label">启用Server酱通知</span>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>Server酱Key</label>
|
||||
<Input v-model:value="settings.Notify.ServerChanKey"
|
||||
@blur="handleSettingChange('Notify', 'ServerChanKey', settings.Notify.ServerChanKey)"
|
||||
placeholder="Server酱推送Key" style="width: 300px" />
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>推送频道</label>
|
||||
<Input v-model:value="settings.Notify.ServerChanChannel"
|
||||
@blur="handleSettingChange('Notify', 'ServerChanChannel', settings.Notify.ServerChanChannel)"
|
||||
placeholder="推送频道" style="width: 300px" />
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>推送标签</label>
|
||||
<Input v-model:value="settings.Notify.ServerChanTag"
|
||||
@blur="handleSettingChange('Notify', 'ServerChanTag', settings.Notify.ServerChanTag)"
|
||||
placeholder="推送标签" style="width: 300px" />
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>企业微信机器人</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfCompanyWebHookBot"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfCompanyWebHookBot', checked)" />
|
||||
<span class="switch-label">启用企业微信机器人</span>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>Webhook URL</label>
|
||||
<Input v-model:value="settings.Notify.CompanyWebHookBotUrl"
|
||||
@blur="handleSettingChange('Notify', 'CompanyWebHookBotUrl', settings.Notify.CompanyWebHookBotUrl)"
|
||||
placeholder="企业微信机器人Webhook地址" style="width: 400px" />
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -183,50 +423,151 @@ const openDevTools = () => {
|
||||
|
||||
<!-- 更新设置 -->
|
||||
<Tabs.TabPane key="update" tab="更新设置">
|
||||
<Card title="自动更新" :bordered="false">
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<!-- 自动检查更新 -->
|
||||
<Card title="更新配置" :bordered="false">
|
||||
<Space direction="vertical" size="large" style="width: 100%">
|
||||
<div class="setting-item">
|
||||
<h4>自动检查更新</h4>
|
||||
<p class="setting-description">启动时自动检查应用程序更新</p>
|
||||
<Switch :checked="true" />
|
||||
<h4>自动更新</h4>
|
||||
<p class="setting-description">是否启用自动更新功能</p>
|
||||
<Switch v-model:checked="settings.Update.IfAutoUpdate"
|
||||
@change="(checked) => handleSettingChange('Update', 'IfAutoUpdate', checked)" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 更新频率 -->
|
||||
<div class="setting-item">
|
||||
<h4>检查频率</h4>
|
||||
<p class="setting-description">设置检查更新的频率</p>
|
||||
<Select value="daily" style="width: 200px">
|
||||
<Select.Option value="startup">每次启动</Select.Option>
|
||||
<Select.Option value="daily">每天</Select.Option>
|
||||
<Select.Option value="weekly">每周</Select.Option>
|
||||
<Select.Option value="manual">手动检查</Select.Option>
|
||||
</Select>
|
||||
<h4>更新类型</h4>
|
||||
<p class="setting-description">选择更新版本类型</p>
|
||||
<Select v-model:value="settings.Update.UpdateType"
|
||||
@change="(value) => handleSettingChange('Update', 'UpdateType', value)" :options="updateTypeOptions"
|
||||
style="width: 200px" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 预发布版本 -->
|
||||
<div class="setting-item">
|
||||
<h4>预发布版本</h4>
|
||||
<p class="setting-description">接收 Beta 版本和预发布版本的更新</p>
|
||||
<Switch :checked="false" />
|
||||
<p class="setting-note">预发布版本可能包含未完全测试的功能</p>
|
||||
<h4>下载线程数</h4>
|
||||
<p class="setting-description">设置下载时使用的线程数量 (1-32)</p>
|
||||
<InputNumber v-model:value="settings.Update.ThreadNumb"
|
||||
@change="(value) => handleSettingChange('Update', 'ThreadNumb', value)" :min="1" :max="32"
|
||||
style="width: 120px" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 当前版本信息 -->
|
||||
<div class="setting-item">
|
||||
<h4>版本信息</h4>
|
||||
<p class="setting-description">当前应用程序版本</p>
|
||||
<div style="margin-top: 12px">
|
||||
<p><strong>版本:</strong> v1.0.0</p>
|
||||
<p><strong>构建时间:</strong> 2024-02-08</p>
|
||||
<Button type="primary" style="margin-top: 8px">检查更新</Button>
|
||||
</div>
|
||||
<h4>代理设置</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="input-group">
|
||||
<label>代理地址</label>
|
||||
<Input v-model:value="settings.Update.ProxyAddress"
|
||||
@blur="handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)"
|
||||
placeholder="例如: http://127.0.0.1:7890" style="width: 300px" />
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>Mirror酱 CDK</h4>
|
||||
<p class="setting-description">设置Mirror酱CDK</p>
|
||||
<Input v-model:value="settings.Update.MirrorChyanCDK"
|
||||
@blur="handleSettingChange('Update', 'MirrorChyanCDK', settings.Update.MirrorChyanCDK)"
|
||||
placeholder="镜像CDK" style="width: 300px" />
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 启动设置 -->
|
||||
<Tabs.TabPane key="start" tab="启动设置">
|
||||
<Card title="启动配置" :bordered="false">
|
||||
<Space direction="vertical" size="large" style="width: 100%">
|
||||
<div class="setting-item">
|
||||
<h4>开机自启</h4>
|
||||
<p class="setting-description">是否在系统启动时自动启动应用</p>
|
||||
<Switch v-model:checked="settings.Start.IfSelfStart"
|
||||
@change="(checked) => handleSettingChange('Start', 'IfSelfStart', checked)" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>启动后直接最小化</h4>
|
||||
<p class="setting-description">启动后是否直接最小化到系统托盘</p>
|
||||
<Switch v-model:checked="settings.Start.IfMinimizeDirectly"
|
||||
@change="(checked) => handleSettingChange('Start', 'IfMinimizeDirectly', checked)" />
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 界面设置 -->
|
||||
<Tabs.TabPane key="ui" tab="界面设置">
|
||||
<Card title="界面配置" :bordered="false">
|
||||
<Space direction="vertical" size="large" style="width: 100%">
|
||||
<div class="setting-item">
|
||||
<h4>系统托盘</h4>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.UI.IfShowTray"
|
||||
@change="(checked) => handleSettingChange('UI', 'IfShowTray', checked)" />
|
||||
<span class="switch-label">显示系统托盘图标</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.UI.IfToTray"
|
||||
@change="(checked) => handleSettingChange('UI', 'IfToTray', checked)" />
|
||||
<span class="switch-label">关闭时最小化到托盘</span>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- <div class="setting-item">-->
|
||||
<!-- <h4>窗口设置</h4>-->
|
||||
<!-- <Space direction="vertical" size="middle" style="width: 100%">-->
|
||||
<!-- <div class="input-group">-->
|
||||
<!-- <label>窗口大小</label>-->
|
||||
<!-- <Input v-model:value="settings.UI.size" @blur="handleSettingChange('UI', 'size', settings.UI.size)"-->
|
||||
<!-- placeholder="例如: 1200x700" style="width: 200px" />-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="input-group">-->
|
||||
<!-- <label>窗口位置</label>-->
|
||||
<!-- <Input v-model:value="settings.UI.location"-->
|
||||
<!-- @blur="handleSettingChange('UI', 'location', settings.UI.location)" placeholder="例如: 100x100"-->
|
||||
<!-- style="width: 200px" />-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="switch-item">-->
|
||||
<!-- <Switch v-model:checked="settings.UI.maximized"-->
|
||||
<!-- @change="(checked) => handleSettingChange('UI', 'maximized', checked)" />-->
|
||||
<!-- <span class="switch-label">启动时最大化窗口</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </Space>-->
|
||||
<!-- </div>-->
|
||||
</Space>
|
||||
</Card>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- 语音设置 -->
|
||||
<Tabs.TabPane key="voice" tab="语音设置">
|
||||
<Card title="语音配置" :bordered="false">
|
||||
<Space direction="vertical" size="large" style="width: 100%">
|
||||
<div class="setting-item">
|
||||
<h4>语音提示</h4>
|
||||
<p class="setting-description">是否启用语音提示功能</p>
|
||||
<Switch v-model:checked="settings.Voice.Enabled"
|
||||
@change="(checked) => handleSettingChange('Voice', 'Enabled', checked)" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="setting-item">
|
||||
<h4>语音类型</h4>
|
||||
<p class="setting-description">选择语音提示的详细程度</p>
|
||||
<Select v-model:value="settings.Voice.Type"
|
||||
@change="(value) => handleSettingChange('Voice', 'Type', value)" :options="voiceTypeOptions"
|
||||
style="width: 200px" :disabled="!settings.Voice.Enabled" />
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -236,47 +577,10 @@ const openDevTools = () => {
|
||||
<Tabs.TabPane key="advanced" tab="高级设置">
|
||||
<Card title="开发者选项" :bordered="false">
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<!-- 开发者工具 -->
|
||||
<div class="setting-item">
|
||||
<h4>开发者工具</h4>
|
||||
<p class="setting-description">打开浏览器开发者工具进行调试</p>
|
||||
<Button type="primary" @click="openDevTools"> 打开 F12 开发者工具 </Button>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 日志级别 -->
|
||||
<div class="setting-item">
|
||||
<h4>日志级别</h4>
|
||||
<p class="setting-description">设置应用程序日志记录级别</p>
|
||||
<Select value="info" style="width: 200px">
|
||||
<Select.Option value="error">错误</Select.Option>
|
||||
<Select.Option value="warn">警告</Select.Option>
|
||||
<Select.Option value="info">信息</Select.Option>
|
||||
<Select.Option value="debug">调试</Select.Option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 性能监控 -->
|
||||
<div class="setting-item">
|
||||
<h4>性能监控</h4>
|
||||
<p class="setting-description">启用性能监控和统计信息收集</p>
|
||||
<Switch :checked="false" />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- 数据重置 -->
|
||||
<div class="setting-item">
|
||||
<h4>数据管理</h4>
|
||||
<p class="setting-description">重置应用程序数据和设置</p>
|
||||
<div style="margin-top: 12px">
|
||||
<Button danger style="margin-right: 8px">清除缓存</Button>
|
||||
<Button danger type="primary">重置所有设置</Button>
|
||||
</div>
|
||||
<p class="setting-note">重置操作不可撤销,请谨慎操作</p>
|
||||
<Button type="primary" @click="openDevTools">打开 F12 开发者工具</Button>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -289,10 +593,14 @@ const openDevTools = () => {
|
||||
.settings-container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
margin-bottom: 0px;
|
||||
.settings-container h1 {
|
||||
margin: 0 0 24px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: v-bind(textColor);
|
||||
}
|
||||
|
||||
.setting-item h4 {
|
||||
@@ -309,56 +617,38 @@ const openDevTools = () => {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.setting-note {
|
||||
margin: 8px 0 0 0;
|
||||
color: v-bind(textTertiaryColor);
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
.switch-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-card) {
|
||||
box-shadow:
|
||||
0 1px 2px 0 rgba(0, 0, 0, 0.03),
|
||||
0 1px 6px -1px rgba(0, 0, 0, 0.02),
|
||||
0 2px 4px 0 rgba(0, 0, 0, 0.02);
|
||||
.switch-label {
|
||||
color: v-bind(textColor);
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
color: v-bind(textColor);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-card-head-title) {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-card .ant-tabs-tab) {
|
||||
padding: 8px 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-card .ant-tabs-tab-active) {
|
||||
color: v-bind(textColor);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-content-holder) {
|
||||
padding-top: 0px;
|
||||
:deep(.ant-tabs-tab) {
|
||||
color: v-bind(textSecondaryColor);
|
||||
}
|
||||
|
||||
:deep(.ant-divider) {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
:deep(.ant-switch) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.settings-container {
|
||||
max-width: 100%;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-card .ant-tabs-tab) {
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user