From 3431a866ea03411b95af00278a4e5acb043c8810 Mon Sep 17 00:00:00 2001 From: yumi Date: Wed, 21 Oct 2026 15:50:27 +0800 Subject: [PATCH] refactor(web): remove setup wizard and clarify settings categories --- README.md | 2 +- docs/DEMO.md | 6 +- web/src/components/SetupWizard.vue | 131 ------------- web/src/router.ts | 2 +- web/src/style.css | 4 +- web/src/types/settings.ts | 17 -- web/src/views/ChangePasswordView.vue | 3 +- web/src/views/SettingsView.vue | 280 +++++++++++++++------------ 8 files changed, 164 insertions(+), 281 deletions(-) delete mode 100644 web/src/components/SetupWizard.vue diff --git a/README.md b/README.md index 61e89ac..0a9f772 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ docker compose -f docker-compose.prebuilt.yml ps 1. 打开 `http://<服务器地址>:8080`。 2. 在服务器的 `logs/initial-admin-password.txt` 读取初始密码,以 `admin` 登录并修改密码。 3. 在“系统配置”填写 TeamSpeak 地址、频道和音乐源配置,点击“应用配置”,确认机器人进入频道。 - 首次登录改密后也可跟随“部署向导”完成配置,使用“应用并检查”分别检查 Voice、TeamSpeak 和音乐接口。 + 首次登录改密后进入设置,按“TeamSpeak 连接”“音乐账号登录”等分类完成配置;每类均附用途说明。 4. 在 Web 搜索并加入一首歌曲,或在 TeamSpeak 发送 `帮助` 查看聊天指令。 网易云功能需要自行部署 `NeteaseCloudMusicApi`;QQ 音乐和 B 站适配已内建。部分播放和账号功能需要对应平台登录态,实际可播放内容受账号权限及平台限制影响。 diff --git a/docs/DEMO.md b/docs/DEMO.md index e07ddd8..271e1cf 100644 --- a/docs/DEMO.md +++ b/docs/DEMO.md @@ -14,7 +14,7 @@ npm --prefix web run preview:demo 访问终端显示的地址,并保留 `/NeteaseTSBot/` 子目录。演示默认使用哈希路由,例如 `/NeteaseTSBot/#/queue`,直接打开或刷新子页面都不需要服务器路由重写。 -演示支持三平台模拟搜索、添加/移除/重排队列、模拟播放暂停和进度、歌词、示例歌单、历史记录、本地收藏和部署向导。队列与配置只保存在当前页面内存中,刷新即重置;收藏、搜索历史和主题使用浏览器本地存储。 +演示支持三平台模拟搜索、添加/移除/重排队列、模拟播放暂停和进度、歌词、示例歌单、历史记录、本地收藏和分类设置。队列与配置只保存在当前页面内存中,刷新即重置;收藏、搜索历史和主题使用浏览器本地存储。 界面明确标注模拟数据,不播放真实音频,不连接音乐平台或 TeamSpeak。演示构建中的 API 只调用本地适配器;未实现的接口会明确报错,不会回退到真实请求。平台登录界面不展示,也不接收 Cookie、密码或上传文件。 @@ -33,9 +33,9 @@ npm --prefix web run preview:demo `VITE_DEMO_MODE` 是构建时开关,不能用 URL 参数打开或关闭。不要给演示构建配置真实 API Token、Cookie 或平台凭据;公开前端构建不应包含这些信息。 -## 部署向导与连接诊断 +## 分类设置与连接诊断 -真实部署首次登录改密后进入“设置 → 部署向导”(`/settings?group=setup`),已有管理员可以在设置内切换到部署向导。旧 `/setup` 地址自动跳转到该分类。前两步只保存 TeamSpeak 与网易云 API 配置,最后一步应用所有已保存的配置,等待 Voice 确认配置版本后检查连接。 +真实部署首次登录改密后进入设置。常用设置包含 TeamSpeak 连接、音乐账号登录、音乐接口与缓存、界面外观;高级设置包含服务与日志、外部 API、ServerQuery 兼容。每类均显示用途说明,原有 `/setup` 地址兼容跳转到 TeamSpeak 连接。修改后保存配置,再点击“应用配置”使其生效。 `GET /admin/diagnostics` 仅管理员会话可访问,分别检查数据库、待应用配置、Voice gRPC、TeamSpeak 会话和可选的网易云 API。Voice 检查最多等待 3 秒;音乐 API 使用 4 秒 HTTP 超时,两者并发执行。错误响应不会包含上游异常文本、地址中的凭据或 Cookie。 diff --git a/web/src/components/SetupWizard.vue b/web/src/components/SetupWizard.vue deleted file mode 100644 index 504b31e..0000000 --- a/web/src/components/SetupWizard.vue +++ /dev/null @@ -1,131 +0,0 @@ - - - diff --git a/web/src/router.ts b/web/src/router.ts index 693ceeb..76023bc 100644 --- a/web/src/router.ts +++ b/web/src/router.ts @@ -27,7 +27,7 @@ export const router = createRouter({ { path: '/login', name: 'login', component: LoginView, meta: { authPage: true } }, { path: '/change-password', name: 'change-password', component: ChangePasswordView, meta: { authPage: true, requiresAuth: true } }, { path: '/settings', component: SettingsView, meta: { requiresAuth: true } }, - { path: '/setup', redirect: { path: '/settings', query: { group: 'setup' } } }, + { path: '/setup', redirect: { path: '/settings', query: { group: 'teamspeak' } } }, { path: '/cookie', redirect: { path: '/settings', query: { group: 'authorization' } } }, { path: '/lyrics', component: LyricsView }, ], diff --git a/web/src/style.css b/web/src/style.css index 406b8f1..1a4fd04 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -791,7 +791,7 @@ body.theme-dark .demo-banner { } body.theme-dark .demo-banner a, -body.theme-dark .theme-setup-shell .text-blue-600, -body.theme-dark .theme-setup-shell .text-blue-700 { +body.theme-dark .theme-settings-shell .text-blue-600, +body.theme-dark .theme-settings-shell .text-blue-700 { color: #93c5fd !important; } diff --git a/web/src/types/settings.ts b/web/src/types/settings.ts index 687c990..f8729e8 100644 --- a/web/src/types/settings.ts +++ b/web/src/types/settings.ts @@ -1,20 +1,3 @@ -export interface SetupField { - key: string - label: string - type: string - value: unknown - sensitive: boolean - configured: boolean - minimum: number | null - maximum: number | null - help: string -} -export interface SetupSettings { - fields: SetupField[] - apply_pending: boolean - voice_restart_requested?: boolean - voice_config_revision?: string -} export interface DiagnosticCheck { id: string label: string diff --git a/web/src/views/ChangePasswordView.vue b/web/src/views/ChangePasswordView.vue index 321f948..f272be2 100644 --- a/web/src/views/ChangePasswordView.vue +++ b/web/src/views/ChangePasswordView.vue @@ -20,9 +20,8 @@ async function submit() { } submitting.value = true try { - const firstLogin = authState.must_change_password await changePassword(currentPassword.value, newPassword.value) - await router.replace(firstLogin ? '/settings?group=setup' : '/settings') + await router.replace('/settings') } catch (e: any) { error.value = String(e?.message || e) } finally { diff --git a/web/src/views/SettingsView.vue b/web/src/views/SettingsView.vue index d160b64..329b24f 100644 --- a/web/src/views/SettingsView.vue +++ b/web/src/views/SettingsView.vue @@ -7,7 +7,6 @@ import { logout } from '../auth' import { loadAppBranding } from '../appConfig' import FloatingToast from '../components/FloatingToast.vue' import CookieView from './CookieView.vue' -import SetupWizard from '../components/SetupWizard.vue' import { DEMO_MODE } from '../demoMode' interface SettingField { @@ -50,17 +49,54 @@ interface ManagedAsset { } const GROUPS = [ - ['setup', '部署向导'], - ['web', 'Web 配置'], - ['backend', 'Voice 服务'], - ['music', '音乐接口配置'], - ['authorization', '音乐会员登录'], - ['teamspeak', 'TeamSpeak 配置'], - ['serverquery', 'ServerQuery 兼容'], - ['access', '外部 api'], + { id: 'teamspeak', label: 'TeamSpeak 连接', section: 'common', summary: '服务器、频道与机器人资料', description: '设置机器人要加入的语音服务器和频道,以及机器人在 TeamSpeak 中显示的昵称、头像与简介。' }, + { id: 'authorization', label: '音乐账号登录', section: 'common', summary: '网易云、QQ 音乐与 B 站账号', description: '连接你的音乐平台账号,用于读取个人歌单、收藏及账号有权限使用的音乐内容。各平台分别登录。' }, + { id: 'music', label: '音乐接口与缓存', section: 'common', summary: '音乐服务地址与下载限制', description: '配置网易云音乐 API 地址,以及 B 站点播时长和音频缓存大小。QQ 音乐与 B 站使用内建接口,无需填写服务地址。' }, + { id: 'web', label: '界面外观', section: 'common', summary: '网页名称与图标', description: '修改网页显示名称和浏览器标签页图标,用于定制自己的机器人界面。' }, + { id: 'backend', label: '服务与日志', section: 'advanced', summary: '语音服务通信与问题排查', description: '配置后端与 Voice 语音服务的通信地址、各服务日志等级和播放状态文件。连接异常或排查问题时使用。' }, + { id: 'access', label: '外部 API', section: 'advanced', summary: '供脚本与其他系统调用', description: '为外部程序调用机器人接口配置访问 Token,适合接入脚本或其他系统。多个 Token 使用逗号分隔。' }, + { id: 'serverquery', label: 'ServerQuery 兼容', section: 'advanced', summary: 'TeamSpeak 管理接口', description: '配置 TeamSpeak 的 ServerQuery 管理接口,供机器人更新头像、简介等资料时使用。通常只在需要此兼容方式时配置。' }, ] as const +const NAV_SECTIONS = [ + { id: 'common', label: '常用设置', groups: GROUPS.filter(group => group.section === 'common') }, + { id: 'advanced', label: '高级设置', groups: GROUPS.filter(group => group.section === 'advanced') }, +] + +const FIELD_HELP: Record = { + 'voice.ts3_host': '填写 TeamSpeak 服务器域名或 IP,不要包含 http:// 或 https://。', + 'voice.ts3_port': 'TeamSpeak 语音连接使用的 UDP 端口,通常为 9987。', + 'voice.ts3_nickname': '机器人加入频道后显示的名称。', + 'voice.ts3_server_password': '服务器设置了加入密码时填写;已有密码留空不修改。', + 'voice.ts3_channel_id': '机器人要加入的频道 ID。与频道路径二选一;均留空时使用默认频道。', + 'voice.ts3_channel_path': '用频道名称路径指定目标频道;已填写频道 ID 时通常无需再填。', + 'voice.ts3_channel_password': '目标频道设有加入密码时填写。', + 'voice.ts3_identity_file': 'Voice 服务读取的身份文件路径。保留原文件可让机器人延续已有身份与权限。', + 'voice.ts3_identity': '直接指定 TeamSpeak 身份内容;通常保留现有身份文件即可,无需填写。', + 'voice.allow_direct_description': '允许通过 TeamSpeak 客户端连接直接更新机器人简介。', + 'voice.description_title': '显示在机器人 TeamSpeak 客户端简介中的标题。', + 'voice.description_intro': '显示在机器人 TeamSpeak 客户端简介中的说明文字。', + 'backend.netease_api_base': '填写单独部署的 NeteaseCloudMusicApi 服务地址,用于网易云搜索、歌单及播放。', + 'backend.bilibili_max_duration_minutes': '限制单个 B 站点播视频的长度,避免下载过长内容。', + 'backend.bilibili_audio_cache_ttl_hours': '缓存音频保留多久,过期内容会被清理。', + 'backend.bilibili_audio_cache_max_mb': '限制 B 站音频缓存占用的磁盘空间。', + 'backend.bilibili_audio_partial_ttl_minutes': '未完成的下载保留多久,避免临时文件长期占用空间。', + 'backend.voice_grpc_addr': '后端连接 Voice 的地址。源码部署通常为 127.0.0.1:50051,Docker 部署通常为 voice-service:50051。', + 'backend.log_level': '控制后端日志详细程度。排查问题时可临时使用 DEBUG。', + 'backend.log_file': '后端日志写入的位置,用于查看请求和运行错误。', + 'web.log_level': '控制浏览器控制台中前端日志的详细程度。', + 'voice.log_level': '控制 Voice 语音服务日志的详细程度。', + 'voice.state_file': '保存播放状态的文件路径,供 Voice 恢复运行状态使用。', + 'voice.serverquery_user': 'TeamSpeak ServerQuery 管理账号,与音乐账号无关。', + 'voice.serverquery_password': '上述 ServerQuery 管理账号的密码。', + 'voice.serverquery_host': 'ServerQuery 管理接口所在服务器的域名或 IP。', + 'voice.serverquery_port': 'ServerQuery 管理端口,通常为 TCP 10011,不是语音端口 9987。', + 'voice.serverquery_sid': '需要管理的 TeamSpeak 虚拟服务器 ID。', + 'voice.serverquery_use_port': '通过语音端口定位需要管理的 TeamSpeak 虚拟服务器。', +} + const GROUP_ALIASES: Record = { + setup: 'teamspeak', description: 'teamspeak', voice: 'backend', } @@ -70,7 +106,7 @@ const route = useRoute() const payload = ref(null) const rawRequestedGroup = String(route.query.group || '') const requestedGroup = GROUP_ALIASES[rawRequestedGroup] || rawRequestedGroup -const activeGroup = ref(GROUPS.some((group) => group[0] === requestedGroup) ? requestedGroup : 'web') +const activeGroup = ref(GROUPS.some((group) => group.id === requestedGroup) ? requestedGroup : 'teamspeak') const draft = ref>({}) const revealed = ref>({}) const assetBusy = ref>({}) @@ -83,7 +119,8 @@ const toastType = ref<'success' | 'error' | 'info'>('info') let toastTimer: ReturnType | null = null let waitGeneration = 0 -const activeFields = computed(() => payload.value?.fields.filter((field) => field.group === activeGroup.value) || []) +const activeCategory = computed(() => GROUPS.find(group => group.id === activeGroup.value)!) +const activeFields = computed(() => payload.value?.fields.filter((field) => (field.key === 'web.log_level' ? 'backend' : field.group) === activeGroup.value) || []) const activeAssets = computed(() => payload.value?.assets.filter((asset) => asset.group === activeGroup.value) || []) const activeNeedsVoiceRestart = computed(() => ( activeFields.value.some((field) => field.restart === 'voice') @@ -247,14 +284,6 @@ function clearSecret(field: SettingField) { field.configured = false } -async function refreshAfterSetup() { - try { - hydrate(await apiGet('/admin/settings')) - } catch (e: any) { - showToast(String(e?.message || e), 'error', 8000) - } -} - function selectGroup(group: string) { activeGroup.value = group error.value = '' @@ -278,12 +307,12 @@ onBeforeUnmount(() => {

- 系统配置 + 设置

-

运行配置与服务连接

+

按用途管理机器人、音乐账号和运行参数

-