dark
5 months ago
11 changed files with 364 additions and 27 deletions
-
53src/App.css
-
96src/pages/websites/domain/components/NameServer.tsx
-
15src/pages/websites/domain/components/style.ts
-
8src/service/website/domain_group.ts
-
23src/service/websites.ts
-
2src/store/websites/dns_account.ts
-
82src/store/websites/domain.ts
-
90src/store/websites/domain_groups.ts
-
6src/theme/index.ts
-
7src/types/website/domain.d.ts
-
9src/types/website/domain_group.d.ts
@ -0,0 +1,96 @@ |
|||
import { INameServer, IWebsiteDomain } from '@/types/website/domain' |
|||
import { Typography, Popover, Table } from 'antd' |
|||
import { memo, useCallback, useMemo } from 'react' |
|||
import { useAtomValue } from 'jotai' |
|||
import { describeDomainNSAtom } from '@/store/websites/domain.ts' |
|||
import { |
|||
CheckCircleFilled, |
|||
CheckCircleOutlined, |
|||
ExclamationCircleFilled, |
|||
ExclamationCircleOutlined, |
|||
LoadingOutlined |
|||
} from '@ant-design/icons' |
|||
|
|||
export interface NameServerProps { |
|||
data: IWebsiteDomain |
|||
} |
|||
|
|||
interface FormattedDnsData { |
|||
expectDnsServers: string; |
|||
dnsServers: string; |
|||
} |
|||
|
|||
function formatNameServerData(data?: INameServer) { |
|||
|
|||
if (!data) { |
|||
return [] |
|||
} |
|||
|
|||
const formattedData: FormattedDnsData[] = [] |
|||
const maxLength = Math.max(data.expectDnsServers.length, data.dnsServers.length) |
|||
|
|||
for (let i = 0; i < maxLength; i++) { |
|||
formattedData.push({ |
|||
expectDnsServers: data.expectDnsServers[i] || '', |
|||
dnsServers: data.dnsServers[i] || '', |
|||
}) |
|||
} |
|||
|
|||
return formattedData |
|||
} |
|||
|
|||
const NameServer = memo(({ data }: NameServerProps) => { |
|||
|
|||
const { data: result, isFetching } = useAtomValue(useMemo(() => describeDomainNSAtom(data?.id), [])) |
|||
|
|||
const columns = [ |
|||
{ |
|||
title: '当前DNS', |
|||
dataIndex: 'dnsServers' |
|||
}, |
|||
{ |
|||
title: '系统分配DNS', |
|||
dataIndex: 'expectDnsServers', |
|||
render: (text: string) => { |
|||
return <Typography.Text copyable={{ text }}>{text}</Typography.Text> |
|||
} |
|||
}, |
|||
] |
|||
|
|||
const content = useCallback(() => { |
|||
return ( |
|||
<Table columns={columns} |
|||
style={{ width: 300}} |
|||
dataSource={formatNameServerData(result)} |
|||
pagination={false} |
|||
bordered={true} |
|||
size={'small'} |
|||
/> |
|||
) |
|||
}, [ result ]) |
|||
|
|||
const title = useCallback(() => { |
|||
if (result?.success) |
|||
return <span><CheckCircleFilled className={'color-green'} style={{ paddingInlineEnd: 5 }}/>域名的DNS信息配置正确。</span> |
|||
|
|||
return <span><ExclamationCircleFilled className={'color-yellow'} style={{ paddingInlineEnd: 5 }}/>探测超时,请稍后查看。</span> |
|||
}, [ result ]) |
|||
|
|||
if (data?.nameservers?.length === 0) { |
|||
return null |
|||
} |
|||
|
|||
return ( |
|||
<Popover content={content()} title={title()} trigger="hover"> |
|||
{isFetching ? <LoadingOutlined/> : null} |
|||
{ |
|||
result?.success ? |
|||
<span className={'color-green'}><CheckCircleOutlined style={{ paddingInlineEnd: 5 }}/>正常</span> |
|||
: <span className={'color-yellow'}><ExclamationCircleOutlined |
|||
style={{ paddingInlineEnd: 5 }}/>{result?.message || '探测超时'}</span> |
|||
} |
|||
</Popover> |
|||
) |
|||
}) |
|||
|
|||
export default NameServer |
@ -0,0 +1,15 @@ |
|||
import { createStyles } from '@/theme' |
|||
|
|||
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
|||
const prefix = `${prefixCls}-${token?.proPrefix}-name-server-component` |
|||
|
|||
const container = css`
|
|||
.green { |
|||
color: green; |
|||
} |
|||
`
|
|||
|
|||
return { |
|||
container: cx(prefix, props?.className, container), |
|||
} |
|||
}) |
@ -0,0 +1,8 @@ |
|||
import { createCURD } from '@/service/base.ts' |
|||
import { WebSite } from '@/types/website/domain_group' |
|||
|
|||
const domain_group = { |
|||
...createCURD<any, WebSite.IDomainGroup >('/website/group'), |
|||
} |
|||
|
|||
export default domain_group |
@ -0,0 +1,90 @@ |
|||
import { atom } from 'jotai' |
|||
import { IApiResult, IPage } from '@/global' |
|||
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' |
|||
import { message } from 'antd' |
|||
import { t } from 'i18next' |
|||
import { WebSite } from '@/types/website/domain_group' |
|||
import webSiteServ from '@/service/website/domain_group' |
|||
|
|||
type SearchParams = IPage & { |
|||
key?: string |
|||
|
|||
[key: string]: any |
|||
} |
|||
|
|||
export const domainGroupIdAtom = atom(0) |
|||
|
|||
export const domainGroupIdsAtom = atom<number[]>([]) |
|||
|
|||
export const domainGroupAtom = atom<WebSite.IDomainGroup>(undefined as unknown as WebSite.IDomainGroup ) |
|||
|
|||
export const domainGroupSearchAtom = atom<SearchParams>({ |
|||
key: '', |
|||
pageSize: 10, |
|||
page: 1, |
|||
} as SearchParams) |
|||
|
|||
export const domainGroupPageAtom = atom<IPage>({ |
|||
pageSize: 10, |
|||
page: 1, |
|||
}) |
|||
|
|||
export const domainGroupsAtom = atomWithQuery((get) => { |
|||
return { |
|||
queryKey: [ 'domainGroups', get(domainGroupSearchAtom) ], |
|||
queryFn: async ({ queryKey: [ , params ] }) => { |
|||
return await webSiteServ.list(params as SearchParams) |
|||
}, |
|||
select: res => { |
|||
const data = res.data |
|||
data.rows = data.rows?.map(row => { |
|||
return { |
|||
...row, |
|||
//status: convertToBool(row.status)
|
|||
} |
|||
}) |
|||
return data |
|||
} |
|||
} |
|||
}) |
|||
|
|||
//saveOrUpdateAtom
|
|||
export const saveOrUpdateDomainGroupAtom = atomWithMutation<IApiResult, WebSite.IDomainGroup>((get) => { |
|||
|
|||
return { |
|||
mutationKey: [ 'updateDomainGroup' ], |
|||
mutationFn: async (data) => { |
|||
//data.status = data.status ? '1' : '0'
|
|||
if (data.id === 0) { |
|||
return await webSiteServ.add(data) |
|||
} |
|||
return await webSiteServ.update(data) |
|||
}, |
|||
onSuccess: (res) => { |
|||
const isAdd = !!res.data?.id |
|||
message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) |
|||
|
|||
//更新列表
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|||
// @ts-ignore fix
|
|||
get(queryClientAtom).invalidateQueries({ queryKey: [ 'domainGroups', get(domainGroupSearchAtom) ] }) |
|||
|
|||
return res |
|||
} |
|||
} |
|||
}) |
|||
|
|||
export const deleteDomainGroupAtom = atomWithMutation((get) => { |
|||
return { |
|||
mutationKey: [ 'deleteDomainGroup' ], |
|||
mutationFn: async (ids: number[]) => { |
|||
return await webSiteServ.batchDelete(ids ?? get(domainGroupIdsAtom)) |
|||
}, |
|||
onSuccess: (res) => { |
|||
message.success('message.deleteSuccess') |
|||
//更新列表
|
|||
get(queryClientAtom).invalidateQueries({ queryKey: [ 'domainGroups', get(domainGroupSearchAtom) ] }) |
|||
return res |
|||
} |
|||
} |
|||
}) |
@ -0,0 +1,9 @@ |
|||
export namespace WebSite { |
|||
export interface IDomainGroup { |
|||
id: number; |
|||
user_id: number; |
|||
sort: number; |
|||
name: string; |
|||
created_at: string; |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue