mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
support multi column
This commit is contained in:
parent
c72618570a
commit
d4698583ba
@ -1,6 +1,7 @@
|
|||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- Linux支持手动授权内核
|
- Linux支持手动授权内核
|
||||||
|
- 代理节点支持多列展示
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
@ -157,9 +157,6 @@ export function createWindow(show = false): void {
|
|||||||
mainWindow.webContents.on('did-fail-load', () => {
|
mainWindow.webContents.on('did-fail-load', () => {
|
||||||
mainWindow?.webContents.reload()
|
mainWindow?.webContents.reload()
|
||||||
})
|
})
|
||||||
mainWindow.on('resize', () => {
|
|
||||||
mainWindow?.webContents.send('resize')
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.on('show', () => {
|
mainWindow.on('show', () => {
|
||||||
startMihomoMemory()
|
startMihomoMemory()
|
||||||
|
|||||||
@ -58,12 +58,11 @@ export const BaseEditor: React.FC<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.electron.ipcRenderer.on('resize', () => {
|
window.onresize = (): void => {
|
||||||
editorRef.current?.layout()
|
editorRef.current?.layout()
|
||||||
})
|
}
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
window.electron.ipcRenderer.removeAllListeners('resize')
|
window.onresize = null
|
||||||
editorRef.current?.dispose()
|
editorRef.current?.dispose()
|
||||||
editorRef.current = undefined
|
editorRef.current = undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import SettingCard from '../base/base-setting-card'
|
import SettingCard from '../base/base-setting-card'
|
||||||
import SettingItem from '../base/base-setting-item'
|
import SettingItem from '../base/base-setting-item'
|
||||||
import { Input, Switch } from '@nextui-org/react'
|
import { Input, Select, SelectItem, Switch } from '@nextui-org/react'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
import debounce from '@renderer/utils/debounce'
|
import debounce from '@renderer/utils/debounce'
|
||||||
import { patchControledMihomoConfig } from '@renderer/utils/ipc'
|
import { patchControledMihomoConfig } from '@renderer/utils/ipc'
|
||||||
@ -14,7 +14,8 @@ const MihomoConfig: React.FC = () => {
|
|||||||
delayTestTimeout,
|
delayTestTimeout,
|
||||||
autoCloseConnection = true,
|
autoCloseConnection = true,
|
||||||
delayTestUrl,
|
delayTestUrl,
|
||||||
userAgent
|
userAgent,
|
||||||
|
proxyCols = 'auto'
|
||||||
} = appConfig || {}
|
} = appConfig || {}
|
||||||
const [url, setUrl] = useState(delayTestUrl)
|
const [url, setUrl] = useState(delayTestUrl)
|
||||||
const setUrlDebounce = debounce((v: string) => {
|
const setUrlDebounce = debounce((v: string) => {
|
||||||
@ -62,6 +63,22 @@ const MihomoConfig: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
<SettingItem title="代理节点展示列数" divider>
|
||||||
|
<Select
|
||||||
|
className="w-[150px]"
|
||||||
|
size="sm"
|
||||||
|
selectedKeys={new Set([proxyCols])}
|
||||||
|
onSelectionChange={async (v) => {
|
||||||
|
await patchAppConfig({ proxyCols: v.currentKey as 'auto' | '1' | '2' | '3' | '4' })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectItem key="auto">自动</SelectItem>
|
||||||
|
<SelectItem key="1">一列</SelectItem>
|
||||||
|
<SelectItem key="2">两列</SelectItem>
|
||||||
|
<SelectItem key="3">三列</SelectItem>
|
||||||
|
<SelectItem key="4">四列</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</SettingItem>
|
||||||
<SettingItem title="接管DNS设置" divider>
|
<SettingItem title="接管DNS设置" divider>
|
||||||
<Switch
|
<Switch
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { FaBoltLightning } from 'react-icons/fa6'
|
|||||||
import { TbCircleLetterD } from 'react-icons/tb'
|
import { TbCircleLetterD } from 'react-icons/tb'
|
||||||
import { FaLocationCrosshairs } from 'react-icons/fa6'
|
import { FaLocationCrosshairs } from 'react-icons/fa6'
|
||||||
import { RxLetterCaseCapitalize } from 'react-icons/rx'
|
import { RxLetterCaseCapitalize } from 'react-icons/rx'
|
||||||
import { useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso'
|
import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso'
|
||||||
import ProxyItem from '@renderer/components/proxies/proxy-item'
|
import ProxyItem from '@renderer/components/proxies/proxy-item'
|
||||||
@ -26,15 +26,19 @@ const Proxies: React.FC = () => {
|
|||||||
const {
|
const {
|
||||||
proxyDisplayMode = 'simple',
|
proxyDisplayMode = 'simple',
|
||||||
proxyDisplayOrder = 'default',
|
proxyDisplayOrder = 'default',
|
||||||
autoCloseConnection = true
|
autoCloseConnection = true,
|
||||||
|
proxyCols = 'auto'
|
||||||
} = appConfig || {}
|
} = appConfig || {}
|
||||||
|
const [cols, setCols] = useState(1)
|
||||||
const [isOpen, setIsOpen] = useState(Array(groups.length).fill(false))
|
const [isOpen, setIsOpen] = useState(Array(groups.length).fill(false))
|
||||||
const virtuosoRef = useRef<GroupedVirtuosoHandle>(null)
|
const virtuosoRef = useRef<GroupedVirtuosoHandle>(null)
|
||||||
const { groupCounts, allProxies } = useMemo(() => {
|
const { groupCounts, allProxies } = useMemo(() => {
|
||||||
const groupCounts = groups.map((group, index) => {
|
const groupCounts = groups.map((group, index) => {
|
||||||
return isOpen[index] ? group.all.length : 0
|
if (!isOpen[index]) return 0
|
||||||
|
const count = Math.floor(group.all.length / cols)
|
||||||
|
return group.all.length % cols === 0 ? count : count + 1
|
||||||
})
|
})
|
||||||
const allProxies: (IMihomoProxy | IMihomoGroup)[] = []
|
const allProxies: (IMihomoProxy | IMihomoGroup)[][] = []
|
||||||
groups.forEach((group, index) => {
|
groups.forEach((group, index) => {
|
||||||
if (isOpen[index]) {
|
if (isOpen[index]) {
|
||||||
let groupProxies = [...group.all]
|
let groupProxies = [...group.all]
|
||||||
@ -50,7 +54,9 @@ const Proxies: React.FC = () => {
|
|||||||
if (proxyDisplayOrder === 'name') {
|
if (proxyDisplayOrder === 'name') {
|
||||||
groupProxies = groupProxies.sort((a, b) => a.name.localeCompare(b.name))
|
groupProxies = groupProxies.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
}
|
}
|
||||||
allProxies.push(...groupProxies)
|
allProxies.push(groupProxies)
|
||||||
|
} else {
|
||||||
|
allProxies.push([])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -74,6 +80,33 @@ const Proxies: React.FC = () => {
|
|||||||
await mihomoGroupDelay(group, url)
|
await mihomoGroupDelay(group, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const calcCols = (): void => {
|
||||||
|
if (window.innerWidth >= 1280) {
|
||||||
|
setCols(4)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (window.innerWidth >= 1024) {
|
||||||
|
setCols(3)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (window.innerWidth >= 768) {
|
||||||
|
setCols(2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (proxyCols !== 'auto') {
|
||||||
|
setCols(parseInt(proxyCols))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
calcCols()
|
||||||
|
window.onresize = calcCols
|
||||||
|
return (): void => {
|
||||||
|
window.onresize = null
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage
|
<BasePage
|
||||||
title="代理组"
|
title="代理组"
|
||||||
@ -155,7 +188,6 @@ const Proxies: React.FC = () => {
|
|||||||
<div className="inline flag-emoji h-[32px] text-md leading-[32px]">
|
<div className="inline flag-emoji h-[32px] text-md leading-[32px]">
|
||||||
{groups[index].name}
|
{groups[index].name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{proxyDisplayMode === 'full' && (
|
{proxyDisplayMode === 'full' && (
|
||||||
<div className="inline ml-2 text-sm text-default-500">
|
<div className="inline ml-2 text-sm text-default-500">
|
||||||
{groups[index].type}
|
{groups[index].type}
|
||||||
@ -185,13 +217,15 @@ const Proxies: React.FC = () => {
|
|||||||
for (let j = 0; j < index; j++) {
|
for (let j = 0; j < index; j++) {
|
||||||
i += groupCounts[j]
|
i += groupCounts[j]
|
||||||
}
|
}
|
||||||
for (let j = 0; j < groupCounts[index]; j++) {
|
i += Math.floor(
|
||||||
if (allProxies[i + j].name === groups[index].now) {
|
allProxies[index].findIndex(
|
||||||
i += j
|
(proxy) => proxy.name === groups[index].now
|
||||||
break
|
) / cols
|
||||||
}
|
)
|
||||||
}
|
virtuosoRef.current?.scrollToIndex({
|
||||||
virtuosoRef.current?.scrollToIndex({ index: i, align: 'start' })
|
index: Math.floor(i),
|
||||||
|
align: 'start'
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FaLocationCrosshairs className="text-lg text-default-500" />
|
<FaLocationCrosshairs className="text-lg text-default-500" />
|
||||||
@ -220,17 +254,29 @@ const Proxies: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
itemContent={(index, groupIndex) => {
|
itemContent={(index, groupIndex) => {
|
||||||
return allProxies[index] ? (
|
let innerIndex = index
|
||||||
<div className="pt-2 mx-2">
|
groupCounts.slice(0, groupIndex).forEach((count) => {
|
||||||
|
innerIndex -= count
|
||||||
|
})
|
||||||
|
return allProxies[groupIndex] ? (
|
||||||
|
<div className={`grid grid-cols-${cols} gap-2 pt-2 mx-2`}>
|
||||||
|
{Array.from({ length: cols }).map((_, i) => {
|
||||||
|
if (!allProxies[groupIndex][innerIndex * cols + i]) return null
|
||||||
|
return (
|
||||||
<ProxyItem
|
<ProxyItem
|
||||||
|
key={allProxies[groupIndex][innerIndex * cols + i].name}
|
||||||
mutateProxies={mutate}
|
mutateProxies={mutate}
|
||||||
onProxyDelay={onProxyDelay}
|
onProxyDelay={onProxyDelay}
|
||||||
onSelect={onChangeProxy}
|
onSelect={onChangeProxy}
|
||||||
proxy={allProxies[index]}
|
proxy={allProxies[groupIndex][innerIndex * cols + i]}
|
||||||
group={groups[groupIndex]}
|
group={groups[groupIndex]}
|
||||||
proxyDisplayMode={proxyDisplayMode}
|
proxyDisplayMode={proxyDisplayMode}
|
||||||
selected={allProxies[index]?.name === groups[groupIndex].now}
|
selected={
|
||||||
|
allProxies[groupIndex][innerIndex * cols + i]?.name === groups[groupIndex].now
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>Never See This</div>
|
<div>Never See This</div>
|
||||||
|
|||||||
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
@ -215,6 +215,7 @@ interface IAppConfig {
|
|||||||
proxyDisplayMode: 'simple' | 'full'
|
proxyDisplayMode: 'simple' | 'full'
|
||||||
proxyDisplayOrder: 'default' | 'delay' | 'name'
|
proxyDisplayOrder: 'default' | 'delay' | 'name'
|
||||||
envType?: 'bash' | 'cmd' | 'powershell'
|
envType?: 'bash' | 'cmd' | 'powershell'
|
||||||
|
proxyCols: 'auto' | '1' | '2' | '3' | '4'
|
||||||
proxyInTray: boolean
|
proxyInTray: boolean
|
||||||
siderOrder: string[]
|
siderOrder: string[]
|
||||||
appTheme: AppTheme
|
appTheme: AppTheme
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user