dark
4 months ago
7 changed files with 353 additions and 35 deletions
-
14src/layout/ListPageLayout.tsx
-
204src/pages/websites/cert/apply.tsx
-
16src/pages/websites/cert/index.tsx
-
103src/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 { useScrollStyle } from '@/hooks/useScrollStyle.ts' |
|||
|
|||
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
|||
const prefix = `${prefixCls}-${token?.proPrefix}-domainGroup-list-page` |
|||
|
|||
const container = css`
|
|||
.ant-table-cell{ |
|||
.ant-tag{ |
|||
padding-inline: 3px; |
|||
margin-inline-end: 3px; |
|||
} |
|||
} |
|||
.ant-table-empty { |
|||
.ant-table-body{ |
|||
height: calc(100vh - 350px) |
|||
} |
|||
} |
|||
.ant-pro-table-highlight{ |
|||
const prefix = `${prefixCls}-${token?.proPrefix}-domainGroup-list-page` |
|||
|
|||
const { scrollbarBackground } = useScrollStyle() |
|||
|
|||
const container = css`
|
|||
.ant-table-cell { |
|||
.ant-tag { |
|||
padding-inline: 3px; |
|||
margin-inline-end: 3px; |
|||
} |
|||
} |
|||
|
|||
.ant-table-empty { |
|||
.ant-table-body { |
|||
height: calc(100vh - 350px) |
|||
} |
|||
} |
|||
|
|||
.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; |
|||
} |
|||
`
|
|||
|
|||
return { |
|||
container: cx(prefix, props?.className, container), |
|||
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 { |
|||
container: cx(prefix, props?.className, container), |
|||
applyContainer, |
|||
applyContent, |
|||
bandSelect, |
|||
} |
|||
}) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue