Browse Source

调整页面

main
dark 3 months ago
parent
commit
7043a24759
  1. 26
      src/App.css
  2. 8
      src/layout/ListPageLayout.tsx
  3. 8
      src/locales/lang/en-US.ts
  4. 22
      src/locales/lang/pages/websites/en-US.ts
  5. 20
      src/locales/lang/pages/websites/record/en-US.ts
  6. 26
      src/locales/lang/pages/websites/record/zh-CN.ts
  7. 3
      src/locales/lang/pages/websites/zh-CN.ts
  8. 6
      src/locales/lang/zh-CN.ts
  9. 14
      src/pages/websites/account/index.tsx
  10. 3
      src/pages/websites/dns/DNSList.tsx
  11. 6
      src/pages/websites/domain/index.tsx
  12. 183
      src/pages/websites/record/index.tsx
  13. 10
      src/routes.tsx
  14. 89
      src/store/websites/record.ts
  15. 10
      src/utils/index.ts

26
src/App.css

@ -30,3 +30,29 @@
color: #1c7ed6;
}
}
/*灰色*/
.color-gray{
color: #999;
}
.color-65{
color:rgba(0, 0, 0, 0.65);
}
.color-333{
color: #333;
}
.text-bold{
font-weight: bold;
}
/* */
/* .ant-table-thead > tr > th{ */
/* background-color: #fcfcfc; */
/* } */
/* */
/* .ant-table-tbody > tr > td{ */
/* background-color: #fff; */
/* } */
/* */

8
src/layout/ListPageLayout.tsx

@ -16,7 +16,11 @@ interface IListPageLayoutProps extends PageContainerProps {
const ListPageLayout: React.FC<IListPageLayoutProps> = (
{
className, children, authHeight = true, ...props
className,
children,
authHeight = true,
title,
...props
}) => {
const navigate = useNavigate()
@ -50,7 +54,7 @@ const ListPageLayout: React.FC<IListPageLayoutProps> = (
})
}}> <ArrowLeftOutlined/></span>
}
<span>{currentMenu?.title}</span>
<span>{title || currentMenu?.title}</span>
</Space>
}
className={cx(styles.container, styles.pageCard, styles.layoutTable, className)}

8
src/locales/lang/en-US.ts

@ -1,6 +1,8 @@
import antdEN from 'antd/locale/en_US'
import menus from './pages/system/menus/en-US'
import roles from './pages/system/roles/en-US'
import record from './pages/websites/record/en-US.ts'
import websites from '@/locales/lang/pages/websites/en-US.ts'
export default {
...antdEN,
@ -40,6 +42,12 @@ export default {
home: {
welcome: 'Welcome to'
},
websites: {
common: {
...websites,
},
record,
},
system: {
menus,
roles,

22
src/locales/lang/pages/websites/en-US.ts

@ -0,0 +1,22 @@
export default {
status: {
/**
* StatusSuccess StatusEnum = "success" // 成功
* StatusFail StatusEnum = "fail" // 失败
* StatusEnable StatusEnum = "enable" // 启用
* StatusPending StatusEnum = "pending" // 等待
* StatusDelete StatusEnum = "delete" // 删除
* StatusDisable StatusEnum = "disable" // 禁用
* StatusSyncing StatusEnum = "syncing" // 同步
*/
success: 'Success',
fail: 'Fail',
enable: 'Enable',
pending: 'Pending',
delete: 'Delete',
disable: 'Disable',
syncing: 'Syncing',
},
}

20
src/locales/lang/pages/websites/record/en-US.ts

@ -0,0 +1,20 @@
export default {
explain: {
'PTR': 'Points an IP address to a domain name.',
'forward_url': 'Redirects a domain name to another address but hides the destination address.',
'AAAA': 'Points a domain name to an IPv6 address.',
'TXT': 'Serves as an SPF record to protect against spam and can be up to 512 characters in length.',
'SRV': 'Specifies the servers that host specific services.',
'MX': 'Points a domain name to an email server address.',
'NS': 'Delegates a subdomain name to third-party DNS servers.',
'A': 'Points a domain name to an IPv4 address.',
'CAA': 'Specifies a CA that is authorized to issue certificates for a domain name.',
'redirect_url': 'Redirects a domain name to another address.',
'CNAME': 'Points a domain name to another domain name.',
},
"forward_url": "Implicit URL Forwarding",
"redirect_url": "Explicit URL Forwarding",
}

26
src/locales/lang/pages/websites/record/zh-CN.ts

@ -0,0 +1,26 @@
export default {
explain: {
'AAAA': '将域名指向一个IPV6地址',
'TXT': '文本长度限制512,通常做SPF记录(反垃圾邮件)',
'SRV': '记录提供特定的服务的服务器',
'MX': '将域名指向邮件服务器地址',
'NS': '将子域名指定其他DNS服务器解析',
'A': '将域名指向一个IPV4地址',
'CAA': 'CA证书颁发机构授权校验',
'REDIRECT_URL': '将域名重定向到另外一个地址',
'CNAME': '将域名指向另外一个域名',
'PTR': '将IP地址指向一个域名',
'FORWARD_URL': '与显性URL类似,但是会隐藏真实目标地址',
},
help:{
"A": "请填写 IPv4 地址,通常为服务器IP地址",
"CNAME": '请填写 CNAME 指向的域名,如{{domain}}',
"AAAA": "请填写 IPv6 地址",
"NS": "详细说明",
"MX": '请填写邮箱厂商提供的MX记录值',
},
"forward_url": "隐性URL",
"redirect_url": "显性URL",
}

3
src/locales/lang/pages/websites/zh-CN.ts

@ -17,5 +17,6 @@ export default {
disable: '禁用',
syncing: '同步',
}
},
}

6
src/locales/lang/zh-CN.ts

@ -8,6 +8,7 @@ import videoCloud from './pages/cms/videoCloud/zh-CN.ts'
import videoMagnet from './pages/cms/videoMagnet/zh-CN.ts'
import list from './pages/videos/list/zh-CN.ts'
import websites from './pages/websites/zh-CN.ts'
import record from './pages/websites/record/zh-CN.ts'
export default {
...antdZh,
@ -47,10 +48,11 @@ export default {
home: {
welcome: '欢迎使用'
},
websites:{
websites: {
common: {
...websites,
},
record,
},
system: {
menus,
@ -59,7 +61,7 @@ export default {
cms: {
collect, video, videoCloud, videoMagnet,
},
videos:{
videos: {
list,
},
actions: {

14
src/pages/websites/account/index.tsx

@ -1,6 +1,6 @@
import { useTranslation } from '../../../i18n.ts'
import { Button, Form, Space, Tooltip, Badge, Divider } from 'antd'
import { useAtom, useAtomValue } from 'jotai'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import {
deleteWebsiteDnsAccountAtom,
saveOrUpdateWebsiteDnsAccountAtom, websiteDnsAccountAtom, websiteDnsAccountsAtom, websiteDnsAccountSearchAtom,
@ -15,7 +15,7 @@ import {
import ListPageLayout from '@/layout/ListPageLayout.tsx'
import { useStyle } from './style.ts'
import { FilterOutlined } from '@ant-design/icons'
import { getValueCount } from '@/utils'
import { getValueCount, unSetColumnRules } from '@/utils'
import { Table as ProTable } from '@/components/table'
import { DNSTypeEnum, DNSTypes, syncDNSAtom } from '@/store/websites/dns.ts'
import { WebSite } from '@/types'
@ -162,7 +162,7 @@ const WebsiteDnsAccount = () => {
const [ filterForm ] = Form.useForm()
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateWebsiteDnsAccountAtom)
const [ search, setSearch ] = useAtom(websiteDnsAccountSearchAtom)
const [ setWebsiteDnsAccount ] = useAtom(websiteDnsAccountAtom)
const setWebsiteDnsAccount = useSetAtom(websiteDnsAccountAtom)
const { data, isFetching, isLoading, refetch } = useAtomValue(websiteDnsAccountsAtom)
const { mutate: deleteWebsiteDnsAccount, isPending: isDeleting } = useAtomValue(deleteWebsiteDnsAccountAtom)
const { mutate: asyncDNS, isPending: isAsyncing } = useAtomValue(syncDNSAtom)
@ -283,12 +283,12 @@ const WebsiteDnsAccount = () => {
]
}
] as ProColumns[]
}, [ isAsyncing, isDeleting, form, asyncDNS, deleteWebsiteDnsAccount])
}, [ isAsyncing, isDeleting, form, asyncDNS, deleteWebsiteDnsAccount ])
useEffect(() => {
setSearchKey(search?.title)
filterForm.setFieldsValue(search)
}, [ search])
}, [ search ])
useEffect(() => {
if (isSuccess) {
@ -353,7 +353,7 @@ const WebsiteDnsAccount = () => {
// 'ant-table-row-selected': currentWebsiteDnsAccount?.id === record.id
}),
onClick: () => {
setWebsiteDnsAccount(record)
setWebsiteDnsAccount(record as any)
}
}
}}
@ -473,7 +473,7 @@ const WebsiteDnsAccount = () => {
setSearch(values)
}}
columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/>
columns={unSetColumnRules(columns.filter(item => !item.hideInSearch) as ProFormColumnsType[])}/>
</ListPageLayout>
)

3
src/pages/websites/dns/DNSList.tsx

@ -13,6 +13,7 @@ import {
saveOrUpdateDNSAtom, syncDNSAtom
} from '@/store/websites/dns.ts'
import { WebSite } from '@/types'
import { unSetColumnRules } from '@/utils'
const getKeyColumn = (type: string, t) => {
const columns: ProColumns<WebSite.IDnsAccount>[] = []
@ -309,7 +310,7 @@ const DNSList = () => {
// console.log('values', values)
saveOrUpdate(values)
}}
columns={columns as ProFormColumnsType[]}/>
columns={unSetColumnRules(columns as ProFormColumnsType[])}/>
</>
)
}

6
src/pages/websites/domain/index.tsx

@ -15,7 +15,7 @@ import {
import ListPageLayout from '@/layout/ListPageLayout.tsx'
import { useStyle } from './style'
import { FilterOutlined } from '@ant-design/icons'
import { getValueCount } from '@/utils'
import { getValueCount, unSetColumnRules } from '@/utils'
import { Table as ProTable } from '@/components/table'
import { Link } from '@tanstack/react-router'
import Popconfirm from '@/components/popconfirm'
@ -78,7 +78,7 @@ const WebsiteDomain = () => {
}}
style={{ paddingBlockStart: 0 }} type={'EditTwo'} size={14}/>
return <Space>
<Link to={'/websites/record'}>{record.name}</Link>
<Link to={`/websites/record?id=${record.id}`}>{record.name}</Link>
{edit}
</Space>
}
@ -347,7 +347,7 @@ const WebsiteDomain = () => {
setSearch(values)
}}
columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/>
columns={unSetColumnRules(columns.filter(item => !item.hideInSearch) as ProFormColumnsType[])}/>
{
dialog
}

183
src/pages/websites/record/index.tsx

@ -1,9 +1,14 @@
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 {
deleteWebsiteDnsRecordsAtom,
saveOrUpdateWebsiteDnsRecordsAtom, websiteDnsRecordsAtom, websiteDnsRecordssAtom, websiteDnsRecordsSearchAtom,
explainTypes,
saveOrUpdateWebsiteDnsRecordsAtom, ttlOptions,
websiteDnsDomainIdAtom,
websiteDnsRecordsAtom,
websiteDnsRecordssAtom,
websiteDnsRecordsSearchAtom,
} from '@/store/websites/record'
import { useEffect, useMemo, useState } from 'react'
import Action from '@/components/action/Action.tsx'
@ -14,19 +19,24 @@ import {
} from '@ant-design/pro-components'
import ListPageLayout from '@/layout/ListPageLayout.tsx'
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 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 { id } = Route.useSearch()
const { styles, cx } = useStyle()
const { t } = useTranslation()
const [ form ] = Form.useForm()
const [ filterForm ] = Form.useForm()
const setDomainId = useSetAtom(websiteDnsDomainIdAtom)
const { data: domainList } = useAtomValue(websiteDomainsAtom)
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateWebsiteDnsRecordsAtom)
const [ search, setSearch ] = useAtom(websiteDnsRecordsSearchAtom)
const [ currentWebsiteDnsRecords, setWebsiteDnsRecords ] = useAtom(websiteDnsRecordsAtom)
@ -37,7 +47,16 @@ const WebsiteDnsRecords = () => {
const [ openFilter, setFilterOpen ] = useState(false)
const [ searchKey, setSearchKey ] = useState(search?.title)
const currentDomain = domainList?.rows?.find?.(item => item.id === id)
useEffect(() => {
if (id) {
setDomainId(id)
}
}, [ id ])
const columns = useMemo(() => {
return [
{
title: 'ID',
@ -49,63 +68,142 @@ const WebsiteDnsRecords = () => {
{
title: t(`${i18nPrefix}.columns.record_id`, 'record_id'),
dataIndex: 'record_id',
hideInForm: true,
hideInSearch: true,
hideInTable: true,
hideInSetting: true,
},
{
title: t(`${i18nPrefix}.columns.domain_id`, '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',
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',
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',
},
{
title: t(`${i18nPrefix}.columns.remark`, 'remark'),
title: t(`${i18nPrefix}.columns.remark`, '备注'),
dataIndex: 'remark',
},
{
title: t(`${i18nPrefix}.columns.created`, 'created'),
title: t(`${i18nPrefix}.columns.created`, '创建时间'),
dataIndex: 'created',
hideInSearch: true,
hideInForm: true,
},
{
title: t(`${i18nPrefix}.columns.modified`, 'modified'),
dataIndex: 'modified',
},
{
title: t(`${i18nPrefix}.columns.option`, '操作'),
key: 'option',
@ -131,7 +229,7 @@ const WebsiteDnsRecords = () => {
]
}
] as ProColumns[]
}, [ isDeleting, currentWebsiteDnsRecords, search ])
}, [ isDeleting, currentWebsiteDnsRecords, search, currentDomain ])
useEffect(() => {
@ -147,7 +245,7 @@ const WebsiteDnsRecords = () => {
}, [ isSuccess ])
return (
<ListPageLayout className={styles.container}>
<ListPageLayout className={styles.container} title={currentDomain?.name}>
<ProTable
rowKey="id"
headerTitle={
@ -157,6 +255,7 @@ const WebsiteDnsRecords = () => {
form.resetFields()
form.setFieldsValue({
id: 0,
ttl: 1,
})
setOpen(true)
}}
@ -313,9 +412,25 @@ const WebsiteDnsRecords = () => {
})
setSearch(values)
}}
columns={columns.filter(item => !item.hideInSearch) as ProFormColumnsType[]}/>
columns={unSetColumnRules(columns.filter(item => !item.hideInSearch) as ProFormColumnsType[])}/>
</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

10
src/routes.tsx

@ -247,7 +247,15 @@ const generateDynamicRoutes = (menuData: MenuItem[], parentRoute: AnyRoute) => {
return NotFound
}
return module()
return module().then((d: any) => {
// console.log(d)
if (d.Route) {
d.Route.update({
path: menu.path,
})
}
return d
})
}),
notFoundComponent: NotFound,
})

89
src/store/websites/record.ts

@ -6,34 +6,103 @@ import { t } from 'i18next'
import websitesServ from '@/service/websites'
import { IWebsiteDnsRecords } from '@/types/website/record'
const i18nPrifx = 'websites.record'
type SearchParams = IPage & {
key?: string
[key: string]: any
}
export const explainTypes = [
{
label: 'A',
value: 'A'
}, {
label: 'CNAME',
value: 'CNAME'
}, {
label: 'AAAA',
value: 'AAAA'
}, {
label: 'NS',
value: 'NS'
}, {
label: 'MX',
value: 'MX'
}, {
label: 'SRV',
value: 'SRV'
}, {
label: 'TXT',
value: 'TXT'
}, {
label: 'CAA',
value: 'CAA'
}, {
label: 'PTR',
value: 'PTR'
}, {
label: t('websites.record.redirect_url'),
value: 'REDIRECT_URL'
}, {
label: t('websites.record.forward_url'),
value: 'FORWARD_URL'
} ]
export const ttlOptions = [
{
label: t(`${i18nPrifx}.ttl.1`, '自动'),
value: 1
},
{
label: t(`${i18nPrifx}.ttl.10`, '10分钟'),
value: 10
},
{
label: t(`${i18nPrifx}.ttl.30`, '30分钟'),
value: 30
},
{
label: t(`${i18nPrifx}.ttl.60`, '1小时'),
value: 60
},
{
label: t(`${i18nPrifx}.ttl.60`, '12小时'),
value: 60 * 12
},
{
label: t(`${i18nPrifx}.ttl.60`, '1天'),
value: 60 * 24
},
]
export const websiteDnsRecordsIdAtom = atom(0)
export const websiteDnsRecordsIdsAtom = atom<number[]>([])
export const websiteDnsRecordsAtom = atom<IWebsiteDnsRecords>(undefined as unknown as IWebsiteDnsRecords )
export const websiteDnsRecordsAtom = atom<IWebsiteDnsRecords>(undefined as unknown as IWebsiteDnsRecords)
export const websiteDnsRecordsSearchAtom = atom<SearchParams>({
key: '',
// key: '',
pageSize: 10,
page: 1,
} as SearchParams)
export const websiteDnsRecordsPageAtom = atom<IPage>({
pageSize: 10,
page: 1,
})
export const websiteDnsDomainIdAtom = atom<number>(0)
export const websiteDnsRecordssAtom = atomWithQuery((get) => {
return {
queryKey: [ 'websiteDnsRecordss', get(websiteDnsRecordsSearchAtom) ],
queryFn: async ({ queryKey: [ , params ] }) => {
return await websitesServ.record.list(params as SearchParams)
queryKey: [ 'websiteDnsRecordss', get(websiteDnsRecordsSearchAtom), get(websiteDnsDomainIdAtom) ],
queryFn: async ({ queryKey: [ , params, id ] }) => {
if (!id) {
return {
data: {
rows: [],
total: 0
}
}
}
return await websitesServ.record.list({ ...(params as any), domain_id: id })
},
select: res => {
const data = res.data

10
src/utils/index.ts

@ -173,3 +173,13 @@ export const getValueCount = (obj: any, filterObj: any = {}) => {
}
return count
}
export const unSetColumnRules = (columns: any[]) => {
return columns.map(col => {
if (col.formItemProps?.rules?.length) {
col.formItemProps.rules = []
}
return col
})
}
Loading…
Cancel
Save