|
@ -1,9 +1,14 @@ |
|
|
import { useTranslation } from '@/i18n.ts' |
|
|
import { useTranslation } from '@/i18n.ts' |
|
|
import { Button, Form, Divider, Space, Tooltip, Badge } from 'antd' |
|
|
|
|
|
import { useAtom, useAtomValue } from 'jotai' |
|
|
|
|
|
|
|
|
import { Button, Form, Divider, Space, Tooltip, Badge, Input, Popover } from 'antd' |
|
|
|
|
|
import { useAtom, useAtomValue, useSetAtom } from 'jotai' |
|
|
import { |
|
|
import { |
|
|
deleteWebsiteDnsRecordsAtom, |
|
|
deleteWebsiteDnsRecordsAtom, |
|
|
saveOrUpdateWebsiteDnsRecordsAtom, websiteDnsRecordsAtom, websiteDnsRecordssAtom, websiteDnsRecordsSearchAtom, |
|
|
|
|
|
|
|
|
explainTypes, |
|
|
|
|
|
saveOrUpdateWebsiteDnsRecordsAtom, ttlOptions, |
|
|
|
|
|
websiteDnsDomainIdAtom, |
|
|
|
|
|
websiteDnsRecordsAtom, |
|
|
|
|
|
websiteDnsRecordssAtom, |
|
|
|
|
|
websiteDnsRecordsSearchAtom, |
|
|
} from '@/store/websites/record' |
|
|
} from '@/store/websites/record' |
|
|
import { useEffect, useMemo, useState } from 'react' |
|
|
import { useEffect, useMemo, useState } from 'react' |
|
|
import Action from '@/components/action/Action.tsx' |
|
|
import Action from '@/components/action/Action.tsx' |
|
@ -14,19 +19,24 @@ import { |
|
|
} from '@ant-design/pro-components' |
|
|
} from '@ant-design/pro-components' |
|
|
import ListPageLayout from '@/layout/ListPageLayout.tsx' |
|
|
import ListPageLayout from '@/layout/ListPageLayout.tsx' |
|
|
import { useStyle } from './style' |
|
|
import { useStyle } from './style' |
|
|
import { FilterOutlined } from '@ant-design/icons' |
|
|
|
|
|
import { getValueCount } from '@/utils' |
|
|
|
|
|
|
|
|
import { FilterOutlined, QuestionCircleOutlined } from '@ant-design/icons' |
|
|
|
|
|
import { getValueCount, unSetColumnRules } from '@/utils' |
|
|
import { Table as ProTable } from '@/components/table' |
|
|
import { Table as ProTable } from '@/components/table' |
|
|
import Popconfirm from '@/components/popconfirm' |
|
|
import Popconfirm from '@/components/popconfirm' |
|
|
|
|
|
import { createFileRoute } from '@tanstack/react-router' |
|
|
|
|
|
import { websiteDomainsAtom } from '@/store/websites/domain.ts' |
|
|
|
|
|
import Switch from '@/components/switch' |
|
|
|
|
|
|
|
|
const i18nPrefix = 'websiteDnsRecordss.list' |
|
|
|
|
|
|
|
|
const i18nPrefix = 'websites.record' |
|
|
|
|
|
|
|
|
const WebsiteDnsRecords = () => { |
|
|
const WebsiteDnsRecords = () => { |
|
|
|
|
|
|
|
|
|
|
|
const { id } = Route.useSearch() |
|
|
const { styles, cx } = useStyle() |
|
|
const { styles, cx } = useStyle() |
|
|
const { t } = useTranslation() |
|
|
const { t } = useTranslation() |
|
|
const [ form ] = Form.useForm() |
|
|
const [ form ] = Form.useForm() |
|
|
const [ filterForm ] = Form.useForm() |
|
|
const [ filterForm ] = Form.useForm() |
|
|
|
|
|
const setDomainId = useSetAtom(websiteDnsDomainIdAtom) |
|
|
|
|
|
const { data: domainList } = useAtomValue(websiteDomainsAtom) |
|
|
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateWebsiteDnsRecordsAtom) |
|
|
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateWebsiteDnsRecordsAtom) |
|
|
const [ search, setSearch ] = useAtom(websiteDnsRecordsSearchAtom) |
|
|
const [ search, setSearch ] = useAtom(websiteDnsRecordsSearchAtom) |
|
|
const [ currentWebsiteDnsRecords, setWebsiteDnsRecords ] = useAtom(websiteDnsRecordsAtom) |
|
|
const [ currentWebsiteDnsRecords, setWebsiteDnsRecords ] = useAtom(websiteDnsRecordsAtom) |
|
@ -37,7 +47,16 @@ const WebsiteDnsRecords = () => { |
|
|
const [ openFilter, setFilterOpen ] = useState(false) |
|
|
const [ openFilter, setFilterOpen ] = useState(false) |
|
|
const [ searchKey, setSearchKey ] = useState(search?.title) |
|
|
const [ searchKey, setSearchKey ] = useState(search?.title) |
|
|
|
|
|
|
|
|
|
|
|
const currentDomain = domainList?.rows?.find?.(item => item.id === id) |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
if (id) { |
|
|
|
|
|
setDomainId(id) |
|
|
|
|
|
} |
|
|
|
|
|
}, [ id ]) |
|
|
|
|
|
|
|
|
const columns = useMemo(() => { |
|
|
const columns = useMemo(() => { |
|
|
|
|
|
|
|
|
return [ |
|
|
return [ |
|
|
{ |
|
|
{ |
|
|
title: 'ID', |
|
|
title: 'ID', |
|
@ -49,63 +68,142 @@ const WebsiteDnsRecords = () => { |
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.record_id`, 'record_id'), |
|
|
title: t(`${i18nPrefix}.columns.record_id`, 'record_id'), |
|
|
dataIndex: 'record_id', |
|
|
dataIndex: 'record_id', |
|
|
|
|
|
hideInForm: true, |
|
|
|
|
|
hideInSearch: true, |
|
|
|
|
|
hideInTable: true, |
|
|
|
|
|
hideInSetting: true, |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.domain_id`, 'domain_id'), |
|
|
title: t(`${i18nPrefix}.columns.domain_id`, 'domain_id'), |
|
|
dataIndex: 'domain_id', |
|
|
dataIndex: 'domain_id', |
|
|
|
|
|
hideInForm: true, |
|
|
|
|
|
hideInSearch: true, |
|
|
|
|
|
hideInTable: true, |
|
|
|
|
|
hideInSetting: true, |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.name`, 'name'), |
|
|
|
|
|
dataIndex: 'name', |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.type`, '记录类型'), |
|
|
|
|
|
tooltip: '指解析记录的用途,例如:网站、邮箱', |
|
|
|
|
|
dataIndex: 'type', |
|
|
|
|
|
valueType: 'select', |
|
|
|
|
|
width: 100, |
|
|
|
|
|
fieldProps: { |
|
|
|
|
|
style: { |
|
|
|
|
|
width: '100%' |
|
|
|
|
|
}, |
|
|
|
|
|
options: explainTypes.map(item => { |
|
|
|
|
|
return { |
|
|
|
|
|
value: item.value, |
|
|
|
|
|
label: <>{item.label}<span |
|
|
|
|
|
className={'color-gray'}> - {t(`websites.record.explain.${item.value}`)}</span></> |
|
|
|
|
|
} |
|
|
|
|
|
}), |
|
|
|
|
|
}, |
|
|
|
|
|
render(_dom, record) { |
|
|
|
|
|
return record.type |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.content`, 'content'), |
|
|
|
|
|
dataIndex: 'content', |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.name`, '主机记录'), |
|
|
|
|
|
dataIndex: 'name', |
|
|
|
|
|
tooltip: '指域名前缀,例如:www', |
|
|
|
|
|
formItemProps: { |
|
|
|
|
|
rules: [ |
|
|
|
|
|
{ required: true, } |
|
|
|
|
|
] |
|
|
|
|
|
}, |
|
|
|
|
|
renderFormItem: (_schema, config) => { |
|
|
|
|
|
|
|
|
|
|
|
return <div style={{ display: 'flex' }}> |
|
|
|
|
|
<Input {...config} |
|
|
|
|
|
suffix={ |
|
|
|
|
|
<> |
|
|
|
|
|
<span className={'color-65'}>.{currentDomain?.name}</span> |
|
|
|
|
|
<Popover |
|
|
|
|
|
placement={'bottomRight'} |
|
|
|
|
|
content={ |
|
|
|
|
|
<span className={'color-65'}> |
|
|
|
|
|
主机记录就是域名前缀,常见用法有:<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>www:</span>解析后的域名为www.{currentDomain?.name}。<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>@:</span>直接解析主域名 {currentDomain?.name}。<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>*:</span>泛解析,匹配其他所有域名 *.{currentDomain?.name}。<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>mail:</span>将域名解析为mail.{currentDomain?.name},通常用于解析邮箱服务器。<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>二级域名:</span>如:abc.{currentDomain?.name},填写abc。<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>手机网站:</span>如:m.{currentDomain?.name},填写m。<br/> |
|
|
|
|
|
<span className={'text-bold color-333'}>显性URL:</span>不支持泛解析(泛解析:将所有子域名解析到同一地址) |
|
|
|
|
|
</span>}> |
|
|
|
|
|
<QuestionCircleOutlined className={'color-gray'}/> |
|
|
|
|
|
</Popover> |
|
|
|
|
|
</> |
|
|
|
|
|
}/> |
|
|
|
|
|
</div> |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.poxy`, 'poxy'), |
|
|
|
|
|
dataIndex: 'poxy', |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.content`, '记录值'), |
|
|
|
|
|
tooltip: '一般是填写服务器的IP地址', |
|
|
|
|
|
dataIndex: 'content', |
|
|
|
|
|
dependencies: [ 'type' ], |
|
|
|
|
|
renderFormItem: (_schema, config, _form1) => { |
|
|
|
|
|
const type = _form1.getFieldValue('type') |
|
|
|
|
|
const help = t(`${i18nPrefix}.help.${type}`, '', { domain: currentDomain?.name }) |
|
|
|
|
|
return <Form.Item {..._schema.formItemProps} help={help}> |
|
|
|
|
|
<Input placeholder={t(`${i18nPrefix}.ttl.placeholder`, '请输入记录值,一般为服务器IP、CDN域名、邮件服务域名')} |
|
|
|
|
|
{...config} |
|
|
|
|
|
/> |
|
|
|
|
|
</Form.Item> |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.ttl`, 'ttl'), |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.ttl`, 'TTL'), |
|
|
|
|
|
tooltip: '指解析结果在Local DNS中的缓存时间', |
|
|
dataIndex: 'ttl', |
|
|
dataIndex: 'ttl', |
|
|
|
|
|
valueType: 'select', |
|
|
|
|
|
fieldProps: { |
|
|
|
|
|
options: ttlOptions |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.type`, 'type'), |
|
|
|
|
|
dataIndex: 'type', |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.poxy`, 'Proxy'), |
|
|
|
|
|
dataIndex: 'poxy', |
|
|
|
|
|
valueType: 'switch', |
|
|
|
|
|
colProps: { |
|
|
|
|
|
span: 8 |
|
|
|
|
|
}, |
|
|
|
|
|
render(_dom, record){ |
|
|
|
|
|
return <Switch size={'small'} value={record.poxy} /> |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.status`, 'status'), |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.status`, '状态'), |
|
|
dataIndex: 'status', |
|
|
dataIndex: 'status', |
|
|
|
|
|
tooltip: '解析记录在云解析DNS中的启用情况', |
|
|
|
|
|
valueType: 'switch', |
|
|
|
|
|
colProps: { |
|
|
|
|
|
span: 8 |
|
|
|
|
|
}, |
|
|
|
|
|
render(_dom, record){ |
|
|
|
|
|
return <Switch size={'small'} value={record.status} /> |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.tag`, 'tag'), |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.tag`, '标签'), |
|
|
dataIndex: 'tag', |
|
|
dataIndex: 'tag', |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.remark`, 'remark'), |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.remark`, '备注'), |
|
|
dataIndex: 'remark', |
|
|
dataIndex: 'remark', |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.created`, 'created'), |
|
|
|
|
|
|
|
|
title: t(`${i18nPrefix}.columns.created`, '创建时间'), |
|
|
dataIndex: 'created', |
|
|
dataIndex: 'created', |
|
|
|
|
|
hideInSearch: true, |
|
|
|
|
|
hideInForm: true, |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
title: t(`${i18nPrefix}.columns.modified`, 'modified'), |
|
|
|
|
|
dataIndex: 'modified', |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: t(`${i18nPrefix}.columns.option`, '操作'), |
|
|
title: t(`${i18nPrefix}.columns.option`, '操作'), |
|
|
key: 'option', |
|
|
key: 'option', |
|
@ -131,7 +229,7 @@ const WebsiteDnsRecords = () => { |
|
|
] |
|
|
] |
|
|
} |
|
|
} |
|
|
] as ProColumns[] |
|
|
] as ProColumns[] |
|
|
}, [ isDeleting, currentWebsiteDnsRecords, search ]) |
|
|
|
|
|
|
|
|
}, [ isDeleting, currentWebsiteDnsRecords, search, currentDomain ]) |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
|
|
|
|
|
@ -147,21 +245,22 @@ const WebsiteDnsRecords = () => { |
|
|
}, [ isSuccess ]) |
|
|
}, [ isSuccess ]) |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<ListPageLayout className={styles.container}> |
|
|
|
|
|
|
|
|
<ListPageLayout className={styles.container} title={currentDomain?.name}> |
|
|
<ProTable |
|
|
<ProTable |
|
|
rowKey="id" |
|
|
rowKey="id" |
|
|
headerTitle={ |
|
|
headerTitle={ |
|
|
<Space> |
|
|
|
|
|
<Button key={'add'} |
|
|
|
|
|
onClick={() => { |
|
|
|
|
|
form.resetFields() |
|
|
|
|
|
form.setFieldsValue({ |
|
|
|
|
|
id: 0, |
|
|
|
|
|
}) |
|
|
|
|
|
setOpen(true) |
|
|
|
|
|
}} |
|
|
|
|
|
type={'primary'}>{t(`${i18nPrefix}.add`, '添加记录')}</Button> |
|
|
|
|
|
</Space> |
|
|
|
|
|
|
|
|
<Space> |
|
|
|
|
|
<Button key={'add'} |
|
|
|
|
|
onClick={() => { |
|
|
|
|
|
form.resetFields() |
|
|
|
|
|
form.setFieldsValue({ |
|
|
|
|
|
id: 0, |
|
|
|
|
|
ttl: 1, |
|
|
|
|
|
}) |
|
|
|
|
|
setOpen(true) |
|
|
|
|
|
}} |
|
|
|
|
|
type={'primary'}>{t(`${i18nPrefix}.add`, '添加记录')}</Button> |
|
|
|
|
|
</Space> |
|
|
} |
|
|
} |
|
|
toolbar={{ |
|
|
toolbar={{ |
|
|
search: { |
|
|
search: { |
|
@ -313,9 +412,25 @@ const WebsiteDnsRecords = () => { |
|
|
}) |
|
|
}) |
|
|
setSearch(values) |
|
|
setSearch(values) |
|
|
}} |
|
|
}} |
|
|
columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/> |
|
|
|
|
|
|
|
|
columns={unSetColumnRules(columns.filter(item => !item.hideInSearch) as ProFormColumnsType[])}/> |
|
|
</ListPageLayout> |
|
|
</ListPageLayout> |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type RecordSearch = { |
|
|
|
|
|
id: number |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
|
// @ts-ignore fix path
|
|
|
|
|
|
export const Route = createFileRoute('/websites/record')({ |
|
|
|
|
|
validateSearch: (search: Record<string, unknown>): RecordSearch => { |
|
|
|
|
|
// validate and parse the search params into a typed state
|
|
|
|
|
|
// console.log(search.id)
|
|
|
|
|
|
return { |
|
|
|
|
|
id: (search.id ?? 0) |
|
|
|
|
|
} as RecordSearch |
|
|
|
|
|
}, |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
export default WebsiteDnsRecords |
|
|
export default WebsiteDnsRecords |