dark
5 months ago
7 changed files with 353 additions and 35 deletions
-
10src/layout/ListPageLayout.tsx
-
204src/pages/websites/cert/apply.tsx
-
14src/pages/websites/cert/index.tsx
-
75src/pages/websites/cert/style.ts
-
9src/routes.tsx
-
11src/service/websites.ts
-
31src/store/websites/cert.ts
@ -0,0 +1,204 @@ |
|||||
|
import { t } from '@/i18n.ts' |
||||
|
import { useAtomValue } from 'jotai' |
||||
|
import { algorithmTypes, bandTypes, dnsConfigAtom, saveOrUpdateCertAtom, StatusText } from '@/store/websites/cert.ts' |
||||
|
import { useCallback, useEffect, useMemo, useState } from 'react' |
||||
|
import { |
||||
|
Button, |
||||
|
Flex, |
||||
|
Form, |
||||
|
Input, |
||||
|
Select, |
||||
|
Space, |
||||
|
Table, Typography, |
||||
|
} from 'antd' |
||||
|
import google from '@/pages/websites/cert/assets/google.png' |
||||
|
import zerossl from '@/pages/websites/cert/assets/zerossl.png' |
||||
|
import lets_encrypt from '@/pages/websites/cert/assets/lets_encrypt.png' |
||||
|
import { useStyle } from './style' |
||||
|
import ListPageLayout from '@/layout/ListPageLayout.tsx' |
||||
|
import { ColumnsType } from 'antd/es/table' |
||||
|
|
||||
|
const i18nPrefix = 'cert.apply' |
||||
|
|
||||
|
const BrandSelect = (props: any) => { |
||||
|
|
||||
|
const { styles, cx } = useStyle() |
||||
|
const [ value, setValue ] = useState(() => props.value) |
||||
|
|
||||
|
useEffect(() => { |
||||
|
setValue(props.value) |
||||
|
}, [ props.value ]) |
||||
|
|
||||
|
const onChange = useCallback((val: string) => { |
||||
|
props.onChange?.(val) |
||||
|
}, []) |
||||
|
|
||||
|
return <> |
||||
|
<Space className={styles.bandSelect}> |
||||
|
<Flex vertical={true} |
||||
|
onClick={() => onChange('Google')} |
||||
|
className={cx('band-normal', { |
||||
|
'band-active': value === 'Google' |
||||
|
})}> |
||||
|
<img src={google} style={{ height: '2rem' }}/> |
||||
|
<span>Google Trust Services</span> |
||||
|
</Flex> |
||||
|
<Flex vertical={true} |
||||
|
onClick={() => onChange('ZeroSSL')} |
||||
|
className={cx('band-normal', { |
||||
|
'band-active': value === 'ZeroSSL' |
||||
|
})}> |
||||
|
<img src={zerossl} style={{ height: '2rem' }}/> |
||||
|
<span>ZeroSSL</span> |
||||
|
</Flex> |
||||
|
<Flex vertical={true} |
||||
|
onClick={() => onChange('Let\'s Encrypt')} |
||||
|
className={cx('band-normal', { |
||||
|
'band-active': value === 'Let\'s Encrypt' |
||||
|
})}> |
||||
|
<img src={lets_encrypt} style={{ height: '2rem' }}/> |
||||
|
<span>Let's Encrypt</span> |
||||
|
</Flex> |
||||
|
</Space> |
||||
|
</> |
||||
|
} |
||||
|
|
||||
|
const StatusTable = (props: { value: string }) => { |
||||
|
|
||||
|
|
||||
|
const { data, isFetching } = useAtomValue(useMemo(() => dnsConfigAtom(props.value), [ props.value ])) |
||||
|
|
||||
|
const columns = useMemo<ColumnsType>(() => { |
||||
|
|
||||
|
return [ |
||||
|
{ |
||||
|
title: t(`${i18nPrefix}.status.columns.status`, '状态'), |
||||
|
tooltip: t(`${i18nPrefix}.status.columns.statusTip`, '正确配置DNS解析后,域名验证会自动通过'), |
||||
|
dataIndex: 'status', |
||||
|
}, |
||||
|
{ |
||||
|
//服务商
|
||||
|
title: t(`${i18nPrefix}.status.columns.name_servers`, '服务商'), |
||||
|
dataIndex: 'name_servers', |
||||
|
}, |
||||
|
{ |
||||
|
//域名
|
||||
|
title: t(`${i18nPrefix}.status.columns.domain`, '域名'), |
||||
|
dataIndex: 'domain', |
||||
|
}, |
||||
|
{ |
||||
|
//主机记录
|
||||
|
title: t(`${i18nPrefix}.status.columns.record`, '主机记录'), |
||||
|
dataIndex: 'record', |
||||
|
}, |
||||
|
{ |
||||
|
//记录类型
|
||||
|
title: t(`${i18nPrefix}.status.columns.record_type`, '记录类型'), |
||||
|
dataIndex: 'record_type', |
||||
|
}, |
||||
|
{ |
||||
|
//记录值
|
||||
|
title: t(`${i18nPrefix}.status.columns.record_value`, '记录值'), |
||||
|
dataIndex: 'record_value', |
||||
|
render:(text)=>{ |
||||
|
return <Typography.Text copyable={{ text: text }}>{text}</Typography.Text> |
||||
|
} |
||||
|
} |
||||
|
] as ColumnsType |
||||
|
|
||||
|
}, []) |
||||
|
|
||||
|
|
||||
|
return <> |
||||
|
<div style={{ paddingBlock: 5, color: '#5a5a5a' }}> |
||||
|
<div>请您添加以下DNS解析记录 参考文档</div> |
||||
|
<div> 1. 只需要添加一次即可,添加后请勿删除记录。</div> |
||||
|
<div> 2. 耐心等待1~2分钟。</div> |
||||
|
</div> |
||||
|
<Table columns={columns} |
||||
|
dataSource={(data as any)?.dns_list} |
||||
|
loading={isFetching} |
||||
|
size={'small'} |
||||
|
pagination={false} |
||||
|
bordered={true}/> |
||||
|
</> |
||||
|
} |
||||
|
|
||||
|
const Apply = (props: any) => { |
||||
|
const { styles, cx } = useStyle() |
||||
|
const [ form ] = Form.useForm() |
||||
|
const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateCertAtom) |
||||
|
const [ domains, setDomains ] = useState<string>('') |
||||
|
|
||||
|
return ( |
||||
|
<ListPageLayout |
||||
|
childrenClassName={styles.applyContent} |
||||
|
className={styles.applyContainer} title={t(`${i18nPrefix}.apply.title`, '证书申请')}> |
||||
|
|
||||
|
<Form |
||||
|
form={form} |
||||
|
{...{ |
||||
|
labelCol: { span: 3 }, |
||||
|
wrapperCol: { span: 16 }, |
||||
|
}} |
||||
|
onValuesChange={(values) => { |
||||
|
// console.log('onValuesChange', values)
|
||||
|
if (values.domains) { |
||||
|
setDomains(values.domains) |
||||
|
} |
||||
|
}} |
||||
|
onFinish={async (values) => { |
||||
|
console.log(values) |
||||
|
saveOrUpdate(values) |
||||
|
}} |
||||
|
> |
||||
|
<Form.Item |
||||
|
name={'domains'} |
||||
|
label={t(`${i18nPrefix}.columns.domains`, '域名')} |
||||
|
rules={[ { required: true, message: t(`${i18nPrefix}.columns.domains.required`, '请输入域名') } ]} |
||||
|
> |
||||
|
<Input.TextArea rows={5} |
||||
|
placeholder={`请输入域名,每行一个,支持泛解析域名;如:
|
||||
|
*.google.com |
||||
|
*.a.baidu.com |
||||
|
hello.alibaba.com`}/>
|
||||
|
</Form.Item> |
||||
|
<Form.Item |
||||
|
label={t(`${i18nPrefix}.columns.type`, '域名验证')} |
||||
|
rules={[ { required: true, message: t(`${i18nPrefix}.columns.type`, '域名验证没有通过') } ]} |
||||
|
> |
||||
|
<StatusTable value={domains}/> |
||||
|
</Form.Item> |
||||
|
<Form.Item |
||||
|
name={'brand'} |
||||
|
label={t(`${i18nPrefix}.columns.brand`, '证书品牌')} |
||||
|
rules={[ { |
||||
|
required: true, |
||||
|
message: t(`${i18nPrefix}.columns.brand.required`, '请选择证书品牌') |
||||
|
} ]} |
||||
|
> |
||||
|
<BrandSelect/> |
||||
|
</Form.Item> |
||||
|
<Form.Item name={'algorithm'} |
||||
|
label={t(`${i18nPrefix}.columns.algorithm`, '加密方式')} |
||||
|
rules={[ { |
||||
|
required: true, |
||||
|
message: t(`${i18nPrefix}.columns.algorithm.required`, '请选择加密方式') |
||||
|
} ]} |
||||
|
> |
||||
|
<Select options={algorithmTypes}/> |
||||
|
</Form.Item> |
||||
|
<Form.Item name={'remark'} |
||||
|
label={t(`${i18nPrefix}.columns.remark`, '备注 ')} |
||||
|
> |
||||
|
<Input/> |
||||
|
</Form.Item> |
||||
|
<Form.Item label={' '} colon={false}> |
||||
|
<Button type={'primary'} htmlType={'submit'}>{t(`${i18nPrefix}.apply.submit`, '提交申请')}</Button> |
||||
|
</Form.Item> |
||||
|
</Form> |
||||
|
</ListPageLayout> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Apply |
@ -1,26 +1,93 @@ |
|||||
import { createStyles } from '@/theme' |
import { createStyles } from '@/theme' |
||||
|
import { useScrollStyle } from '@/hooks/useScrollStyle.ts' |
||||
|
|
||||
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
||||
const prefix = `${prefixCls}-${token?.proPrefix}-domainGroup-list-page` |
const prefix = `${prefixCls}-${token?.proPrefix}-domainGroup-list-page` |
||||
|
|
||||
|
const { scrollbarBackground } = useScrollStyle() |
||||
|
|
||||
const container = css`
|
const container = css`
|
||||
.ant-table-cell{ |
|
||||
.ant-tag{ |
|
||||
|
.ant-table-cell { |
||||
|
.ant-tag { |
||||
padding-inline: 3px; |
padding-inline: 3px; |
||||
margin-inline-end: 3px; |
margin-inline-end: 3px; |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
.ant-table-empty { |
.ant-table-empty { |
||||
.ant-table-body{ |
|
||||
|
.ant-table-body { |
||||
height: calc(100vh - 350px) |
height: calc(100vh - 350px) |
||||
} |
} |
||||
} |
} |
||||
.ant-pro-table-highlight{ |
|
||||
|
|
||||
|
.ant-pro-table-highlight { |
||||
|
|
||||
|
} |
||||
|
`
|
||||
|
const bandSelect = css`
|
||||
|
.band-normal { |
||||
|
border: 2px solid #ebebeb; |
||||
|
border-radius: 4px; |
||||
|
color: #575757; |
||||
|
padding: 10px 20px; |
||||
|
user-select: none; |
||||
|
cursor: pointer; |
||||
|
|
||||
|
&:not(.band-active):hover { |
||||
|
border: 2px solid #d6d6d6; |
||||
|
color: #393939; |
||||
|
} |
||||
|
|
||||
|
img { |
||||
|
object-fit: contain; |
||||
|
object-position: left; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.band-active { |
||||
|
border: 2px solid #3f9eff; |
||||
|
color: #3f9eff; |
||||
} |
} |
||||
`
|
`
|
||||
|
|
||||
|
const applyContainer = css`
|
||||
|
|
||||
|
.ant-pro-grid-content{ |
||||
|
|
||||
|
height: calc(100vh - 122px); |
||||
|
min-height: calc(100vh - 122px); |
||||
|
overflow: hidden; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//padding: 20px;
|
||||
|
.ant-pro-card{ |
||||
|
border-radius: 0; |
||||
|
} |
||||
|
.ant-pro-card-header{ |
||||
|
border-bottom: 1px solid #ebebeb; |
||||
|
} |
||||
|
.ant-pro-card-body{ |
||||
|
padding-block-start: 24px; |
||||
|
} |
||||
|
`
|
||||
|
|
||||
|
const applyContent = css`
|
||||
|
height: 100%; |
||||
|
overflow: auto; |
||||
|
${scrollbarBackground} |
||||
|
|
||||
|
.ant-form{ |
||||
|
|
||||
|
padding-block-start: 44px; |
||||
|
} |
||||
|
|
||||
|
`
|
||||
|
|
||||
return { |
return { |
||||
container: cx(prefix, props?.className, container), |
container: cx(prefix, props?.className, container), |
||||
|
applyContainer, |
||||
|
applyContent, |
||||
|
bandSelect, |
||||
} |
} |
||||
}) |
}) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue