feat: 配置Dialog

This commit is contained in:
lanyeeee
2025-08-07 05:44:25 +08:00
parent 04ed15a196
commit f137b2b8a6
8 changed files with 709 additions and 6 deletions
+18 -6
View File
@@ -2,11 +2,11 @@
import { onMounted, ref } from 'vue'
import { useStore } from './store.ts'
import LogDialog from './dialogs/LogDialog.vue'
import { PhClockCounterClockwise, PhInfo } from '@phosphor-icons/vue'
import { commands } from './bindings.ts'
import { PhClockCounterClockwise, PhInfo, PhGearSix } from '@phosphor-icons/vue'
import AboutDialog from './dialogs/AboutDialog.vue'
import { platform } from '@tauri-apps/plugin-os'
import TitleBar from './components/TitleBar.vue'
import SettingsDialog from './dialogs/SettingsDialog/SettingsDialog.vue'
const currentPlatform = platform()
@@ -14,28 +14,39 @@ const store = useStore()
const logDialogShowing = ref<boolean>(false)
const aboutDialogShowing = ref<boolean>(false)
const settingsDialogShowing = ref<boolean>(false)
onMounted(async () => {
onMounted(() => {
// 屏蔽浏览器右键菜单
document.oncontextmenu = (event) => {
event.preventDefault()
}
store.config = await commands.getConfig()
})
</script>
<template>
<div
:class="[
:class="[
'h-screen flex flex-col',
{
'box-border border border-solid border-gray-3': currentPlatform === 'linux',
},
]">
<TitleBar />
<div v-if="store.config !== undefined" class="h-full w-full flex overflow-hidden select-none">
<div class="flex flex-col box-border p-1 border-r-solid border-r-1 border-r-[#DADADA] bg-[#F9F9F9] flex-shrink-0">
<n-tooltip placement="right" trigger="hover" :show-arrow="false">
配置
<template #trigger>
<n-button text class="mt-auto py-1 px-2" @click="settingsDialogShowing = true">
<n-icon size="28">
<PhGearSix />
</n-icon>
</n-button>
</template>
</n-tooltip>
<n-tooltip placement="right" trigger="hover" :show-arrow="false">
日志
<template #trigger>
@@ -60,6 +71,7 @@ onMounted(async () => {
</div>
</div>
<SettingsDialog v-model:showing="settingsDialogShowing" />
<LogDialog v-model:showing="logDialogShowing" />
<AboutDialog v-model:showing="aboutDialogShowing" />
</div>
@@ -0,0 +1,79 @@
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { commands } from '../../bindings.ts'
import { path } from '@tauri-apps/api'
import { appDataDir } from '@tauri-apps/api/path'
import { useStore } from '../../store.ts'
import DownloadSettings from './components/DownloadSettings.vue'
import ProxySettings from './components/ProxySettings.vue'
import FmtSettings from './components/FmtSettings.vue'
import DownloadSpeedSettings from './components/DownloadSpeedSettings.vue'
import AssDanmakuSettings from './components/AssDanmakuSettings.vue'
import { useMessage } from 'naive-ui'
const store = useStore()
const message = useMessage()
const showing = defineModel<boolean>('showing', { required: true })
onMounted(async () => {
store.config = await commands.getConfig()
})
watch(
() => store.config,
async () => {
if (store.config === undefined) {
return
}
const result = await commands.saveConfig(store.config)
if (result.status === 'error') {
console.error(result.error)
return
}
message.success('保存配置成功')
},
{ deep: true },
)
const currentTabName = ref<string>('download_settings')
async function showConfigInFileManager() {
const configName = 'config.json'
const configPath = await path.join(await appDataDir(), configName)
const result = await commands.showPathInFileManager(configPath)
if (result.status === 'error') {
console.error(result.error)
}
}
</script>
<template>
<n-modal v-if="store.config !== undefined" v-model:show="showing">
<n-dialog :showIcon="false" title="配置" content-style="" @close="showing = false">
<div class="flex flex-col gap-row-2">
<n-tabs class="h-full" v-model:value="currentTabName" type="line" size="small" animated>
<n-tab-pane name="download_settings" tab="下载内容">
<DownloadSettings />
</n-tab-pane>
<n-tab-pane name="fmt_settings" tab="命名格式">
<FmtSettings />
</n-tab-pane>
<n-tab-pane name="ass_danmaku_settings" tab="ass弹幕">
<AssDanmakuSettings />
</n-tab-pane>
<n-tab-pane name="download_speed_settings" tab="下载速度">
<DownloadSpeedSettings />
</n-tab-pane>
<n-tab-pane name="proxy_settings" tab="代理">
<ProxySettings />
</n-tab-pane>
</n-tabs>
<n-button class="ml-auto mt-2" size="small" @click="showConfigInFileManager">打开配置目录</n-button>
</div>
</n-dialog>
</n-modal>
</template>
@@ -0,0 +1,204 @@
<script setup lang="ts">
import { useStore } from '../../../store.ts'
const store = useStore()
</script>
<template>
<div v-if="store.config !== undefined" class="flex flex-col gap-row-2">
<n-tooltip placement="left" trigger="hover">
弹幕使用的字体名称
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">字体</n-input-group-label>
<n-input class="w-full" v-model:value="store.config.danmaku_config.font" size="small" />
</n-input-group>
</template>
</n-tooltip>
<div class="flex items-center gap-2 whitespace-nowrap">
<n-tooltip placement="left" trigger="hover">
弹幕字体大小
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">字体大小</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.font_size"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-checkbox v-model:checked="store.config.danmaku_config.bold">加粗</n-checkbox>
</div>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
弹幕的描边宽度单位为像素
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">描边宽度</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.outline"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
弹幕的不透明度越小越透明越大越不透明
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">不透明度</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.alpha"
size="small"
:min="0"
:max="1"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
弹幕在屏幕上的持续时间单位为秒可以有小数
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">在屏幕上的持续时间</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.duration"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
正常弹幕的屏幕填充占比默认为 0.5即半个屏幕
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">显示区域</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.float_percentage"
size="small"
:min="0"
:max="1"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
<div>是一个比例数用来计算平衡不同字体的宽度</div>
<div>有的字体比较粗比较宽可以适当调大 1.41.6</div>
<div>有的字体比较细比较窄可以适当调小 1.01.2</div>
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">宽度乘子</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.width_ratio"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
用来调整弹幕时间的水平距离单位是像素如果想拉开弹幕之间的距离可以调大
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">最小水平间距</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.horizontal_gap"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
计算弹幕高度的数值行高度/行间距数值越大弹幕的垂直距离越大
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">行高</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.lane_size"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
弹幕时间轴偏移大于0 会让弹幕延后小于0 会让弹幕提前单位为秒
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">延后</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.time_offset"
size="small"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
渲染的屏幕分辨率这个并不会影响渲染区域的大小只影响字体的相对大小
<span class="text-blue">没有特殊原因不用改</span>
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">屏幕宽度</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.width"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
渲染的屏幕分辨率这个并不会影响渲染区域的大小只影响字体的相对大小
<span class="text-blue">没有特殊原因不用改</span>
<template #trigger>
<n-input-group class="box-border">
<n-input-group-label size="small">屏幕高度</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.danmaku_config.height"
size="small"
:min="0"
:parse="(x: string) => Number(x)"
:show-button="false" />
</n-input-group>
</template>
</n-tooltip>
</div>
</template>
@@ -0,0 +1,112 @@
<script setup lang="ts">
import { SelectBaseOption } from 'naive-ui/es/select/src/interface'
import { PreferAudioQuality, PreferVideoQuality } from '../../../bindings.ts'
import { useStore } from '../../../store.ts'
const store = useStore()
const videoQualitySelectOptions: SelectBaseOption<PreferVideoQuality>[] = [
{ label: '最高', value: 'Best' },
{ label: '240P', value: '240P' },
{ label: '360P', value: '360P' },
{ label: '480P', value: '480P' },
{ label: '720P', value: '720P' },
{ label: '720P 60帧', value: '720P60' },
{ label: '1080P', value: '1080P' },
{ label: '智能修复', value: 'AiRepair' },
{ label: '1080P 高码率', value: '1080P+' },
{ label: '1080P 60帧', value: '1080P60' },
{ label: '4K', value: '4K' },
{ label: 'HDR', value: 'HDR' },
{ label: '杜比视界', value: 'Dolby' },
{ label: '8K', value: '8K' },
]
const audioQualitySelectOptions: SelectBaseOption<PreferAudioQuality>[] = [
{ label: '最高', value: 'Best' },
{ label: '64K', value: '64K' },
{ label: '132K', value: '132K' },
{ label: '192K', value: '192K' },
{ label: '杜比全景声', value: 'Dolby' },
{ label: 'Hi-Res', value: 'HiRes' },
]
</script>
<template>
<div v-if="store.config !== undefined" class="flex flex-col gap-row-2">
<div class="flex gap-2">
<span class="font-bold">主要内容</span>
<n-checkbox v-model:checked="store.config.download_video">下载视频</n-checkbox>
<n-checkbox v-model:checked="store.config.download_audio">下载音频</n-checkbox>
<n-tooltip placement="top" trigger="hover">
<div>自动合并音频和视频</div>
<template #trigger>
<n-checkbox v-model:checked="store.config.auto_merge">自动合并</n-checkbox>
</template>
</n-tooltip>
</div>
<div class="flex gap-2">
<span class="font-bold">附加内容</span>
<n-checkbox v-model:checked="store.config.download_subtitle">下载字幕</n-checkbox>
<n-checkbox v-model:checked="store.config.download_cover">下载封面</n-checkbox>
</div>
<div class="flex gap-2">
<span class="font-bold">下载弹幕</span>
<n-checkbox class="w-22" v-model:checked="store.config.download_xml_danmaku">xml弹幕</n-checkbox>
<n-checkbox class="w-22" v-model:checked="store.config.download_ass_danmaku">ass弹幕</n-checkbox>
<n-checkbox class="w-22" v-model:checked="store.config.download_json_danmaku">json弹幕</n-checkbox>
</div>
<div class="flex gap-2">
<span class="w-14 font-bold">元数据</span>
<n-tooltip placement="top" trigger="hover">
<div>还会顺便下载poster和fanart(如果有的话)</div>
<template #trigger>
<n-checkbox class="w-22" v-model:checked="store.config.download_nfo">nfo文件</n-checkbox>
</template>
</n-tooltip>
<n-checkbox class="w-22" v-model:checked="store.config.download_json">json文件</n-checkbox>
</div>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
<div>如果视频有对应的画质则使用对应的画质否则选择最高画质</div>
<template #trigger>
<div class="flex items-center">
<span class="mr-2 whitespace-nowrap font-bold">优先画质</span>
<n-select
size="small"
v-model:value="store.config.prefer_video_quality"
:options="videoQualitySelectOptions" />
</div>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
<div>如果视频有对应的音质则使用对应的音质否则选择最高音质</div>
<template #trigger>
<div class="flex items-center">
<span class="mr-2 whitespace-nowrap font-bold">优先音质</span>
<n-select
size="small"
v-model:value="store.config.prefer_audio_quality"
:options="audioQualitySelectOptions" />
</div>
</template>
</n-tooltip>
<n-tooltip placement="left" trigger="hover" class="w-20vw">
<div>如果视频有对应的编码则使用对应的编码否则按照 AVC > HEVC > AV1 的顺序选择编码</div>
<template #trigger>
<div>
<span class="mr-2 font-bold">优先编码</span>
<n-radio-group v-model:value="store.config.prefer_codec_type" size="small">
<n-radio-button value="AVC">AVC</n-radio-button>
<n-radio-button value="HEVC">HEVC</n-radio-button>
<n-radio-button value="AV1">AV1</n-radio-button>
</n-radio-group>
</div>
</template>
</n-tooltip>
</div>
</template>
@@ -0,0 +1,77 @@
<script setup lang="ts">
import { useStore } from '../../../store.ts'
import { useMessage } from 'naive-ui'
const message = useMessage()
const store = useStore()
</script>
<template>
<div v-if="store.config !== undefined" class="flex flex-col gap-row-2">
<div class="flex gap-1">
<n-tooltip placement="top" trigger="hover">
<div>最多有多少个任务同时下载</div>
<template #trigger>
<n-input-group class="w-40%">
<n-input-group-label size="small">任务并发</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.task_concurrency"
size="small"
@update:value="message.warning('对任务并发的修改需要重启才能生效')"
:min="1"
:parse="(x: string) => Number(x)" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="top" trigger="hover">
<div>每个任务下载完成后休息多久</div>
<template #trigger>
<n-input-group class="w-60%">
<n-input-group-label size="small">任务下载间隔</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.task_download_interval_sec"
size="small"
:min="0"
:parse="(x: string) => Number(x)" />
<n-input-group-label size="small"></n-input-group-label>
</n-input-group>
</template>
</n-tooltip>
</div>
<div class="flex gap-1">
<n-tooltip placement="top" trigger="hover">
<div>最多有多少个分片同时下载</div>
<template #trigger>
<n-input-group class="w-40%">
<n-input-group-label size="small">分片并发</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.chunk_concurrency"
size="small"
@update-value="message.warning('对分片并发的修改需要重启才能生效')"
:min="1"
:parse="(x: string) => Number(x)" />
</n-input-group>
</template>
</n-tooltip>
<n-tooltip placement="top" trigger="hover">
<div>每个分片下载完成后休息多久</div>
<template #trigger>
<n-input-group class="w-60%">
<n-input-group-label size="small">分片下载间隔</n-input-group-label>
<n-input-number
class="w-full"
v-model:value="store.config.chunk_download_interval_sec"
size="small"
:min="0"
:parse="(x: string) => Number(x)" />
<n-input-group-label size="small"></n-input-group-label>
</n-input-group>
</template>
</n-tooltip>
</div>
</div>
</template>
@@ -0,0 +1,177 @@
<script setup lang="tsx">
import { ref } from 'vue'
import { useStore } from '../../../store.ts'
const store = useStore()
const dirFmt = ref<string>(store.config?.dir_fmt ?? '')
const dirFmtForPart = ref<string>(store.config?.dir_fmt_for_part ?? '')
const timeFmt = ref<string>(store.config?.time_fmt ?? '')
function AvailableFmtFields() {
return (
<>
<div>
可以用斜杠
<span class="rounded bg-gray-500 px-1 select-all text-white">/</span>
来分隔目录层级
</div>
<div class="text-blue">注意最后一个层级是下载内容的文件名</div>
<div class="font-semibold mt-2">
<span>可用字段</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">task_id</span>
<span class="ml-2">下载任务ID</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">episode_type</span>
<span class="ml-2">视频类型(Normal / Bangumi / Cheese)</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">aid</span>
<span class="ml-2">av号</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">bvid</span>
<span class="ml-2">bv号</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">cid</span>
<span class="ml-2">cid</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">ep_id</span>
<span class="ml-2">番剧/课程的章节号</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">duration</span>
<span class="ml-2">视频时长单位为秒</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">pub_ts</span>
<span class="ml-2">视频发布的时间</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">collection_title</span>
<span class="ml-2">
合集名称
<span class="text-blue">对于单独(没有合集)的视频合集名称就是视频名称</span>
</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">episode_title</span>
<span class="ml-2">视频名称</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">episode_order</span>
<span class="ml-2">在合集里的序号(从1起)</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">part_title</span>
<span class="ml-2">分P名称</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">part_order</span>
<span class="ml-2">分P序号</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">up_name</span>
<span class="ml-2">up昵称</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">up_uid</span>
<span class="ml-2">up的uid</span>
</div>
<div>
<span class="rounded bg-gray-500 px-1 select-all">create_ts</span>
<span class="ml-2">下载任务创建的时间</span>
</div>
</>
)
}
</script>
<template>
<div v-if="store.config !== undefined" class="flex flex-col">
<n-config-provider
:theme-overrides="{ Scrollbar: { color: 'rgba(255, 255, 255, 0.25)', colorHover: 'rgba(255, 255, 255, 0.3)' } }">
<div class="font-bold">目录格式</div>
<n-tooltip placement="top" trigger="hover" class="w-68vw max-h-35vh" :scrollable="true">
<div>目录的命名格式只作用于没分P的视频</div>
<available-fmt-fields />
<div class="font-semibold mt-2">例如格式</div>
<div class="bg-gray-200 rounded-md p-1 text-black w-fit">{collection_title}/{episode_title}</div>
<div class="font-semibold">
<span>下载</span>
<span class="text-blue mx-0.5">BV1eg411A7qd</span>
<span>会产生一个文件夹</span>
</div>
<div class="bg-gray-200 rounded-md px-1 w-fit text-black">galgame纯音乐</div>
<div class="font-semibold">文件夹内下载内容的文件名为</div>
<div class="bg-gray-200 rounded-md px-1 w-fit text-black">galgame纯音乐 夜の向日葵</div>
<template #trigger>
<n-input
v-model:value="dirFmt"
size="small"
@blur="store.config.dir_fmt = dirFmt"
@keydown.enter="store.config.dir_fmt = dirFmt" />
</template>
</n-tooltip>
<div class="mt-2 font-bold">目录格式(分P)</div>
<n-tooltip placement="top" trigger="hover" class="w-68vw max-h-45vh" :scrollable="true">
<div>有分P的视频用这个格式而不是上面那个</div>
<available-fmt-fields />
<div class="font-semibold mt-2">例如格式</div>
<div class="bg-gray-200 rounded-md p-1 text-black w-fit">
{collection_title}/{episode_title}/{episode_title}-P{part_order} {part_title}
</div>
<div class="font-semibold">
<span>下载</span>
<span class="text-blue mx-0.5">BV1NMaXecEsv</span>
<span></span>
<span class="text-blue mx-0.5">P30</span>
<span>会创建</span>
</div>
<div class="flex flex-col gap-1 text-black">
<div class="bg-gray-200 rounded-md px-1 w-fit">动画游戏专辑</div>
<div class="bg-gray-200 rounded-md px-1 w-fit">高音质樱之诗OST</div>
</div>
<div class="font-semibold">两层文件夹里面的下载内容的文件名为</div>
<div class="bg-gray-200 rounded-md px-1 w-fit text-black">高音质樱之诗OST-P30 30.夢の歩みを見上げて</div>
<template #trigger>
<n-input
v-model:value="dirFmtForPart"
size="small"
@blur="store.config.dir_fmt_for_part = dirFmtForPart"
@keydown.enter="store.config.dir_fmt_for_part = dirFmtForPart" />
</template>
</n-tooltip>
<div class="mt-2 font-bold">时间格式</div>
<n-tooltip placement="top" trigger="hover">
<div>
影响字段
<span class="rounded bg-gray-500 px-1 select-all">pub_ts</span>
<span class="rounded bg-gray-500 px-1 select-all">created_ts</span>
的格式
</div>
<div>
详细用法请看
<n-a href="https://docs.rs/chrono/latest/chrono/format/strftime/index.html" target="_blank">
https://docs.rs/chrono/latest/chrono/format/strftime/index.html
</n-a>
</div>
<template #trigger>
<n-input
v-model:value="timeFmt"
size="small"
@blur="store.config.time_fmt = timeFmt"
@keydown.enter="store.config.time_fmt = timeFmt" />
</template>
</n-tooltip>
</n-config-provider>
</div>
</template>
@@ -0,0 +1,38 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useStore } from '../../../store.ts'
const store = useStore()
const proxyHost = ref<string>(store.config?.proxy_host ?? '')
const disableProxyHostAndPort = computed(() => store.config?.proxy_mode !== 'Custom')
</script>
<template>
<div v-if="store.config !== undefined" class="flex flex-col gap-row-2">
<n-radio-group v-model:value="store.config.proxy_mode" size="small">
<n-radio-button value="NoProxy">直连</n-radio-button>
<n-radio-button value="System">系统代理</n-radio-button>
<n-radio-button value="Custom">自定义</n-radio-button>
</n-radio-group>
<n-input-group>
<n-input-group-label size="small">http://</n-input-group-label>
<n-input
:disabled="disableProxyHostAndPort"
v-model:value="proxyHost"
size="small"
placeholder=""
@blur="store.config.proxy_host = proxyHost"
@keydown.enter="store.config.proxy_host = proxyHost" />
<n-input-group-label size="small">:</n-input-group-label>
<n-input-number
:disabled="disableProxyHostAndPort"
v-model:value="store.config.proxy_port"
size="small"
placeholder=""
:parse="(x: string) => parseInt(x)" />
</n-input-group>
</div>
</template>