Browse Source

调整证书申请页面

main
dark 5 months ago
parent
commit
ba4ad24ba2
  1. 10
      src/layout/ListPageLayout.tsx
  2. 204
      src/pages/websites/cert/apply.tsx
  3. 14
      src/pages/websites/cert/index.tsx
  4. 67
      src/pages/websites/cert/style.ts
  5. 9
      src/routes.tsx
  6. 11
      src/service/websites.ts
  7. 31
      src/store/websites/cert.ts

10
src/layout/ListPageLayout.tsx

@ -11,7 +11,9 @@ import { useNavigate } from '@tanstack/react-router'
interface IListPageLayoutProps extends PageContainerProps {
children: React.ReactNode
authHeight?: boolean
authHeight?: boolean,
childrenStyle?: React.CSSProperties,
childrenClassName?: string,
}
const ListPageLayout: React.FC<IListPageLayoutProps> = (
@ -19,6 +21,8 @@ const ListPageLayout: React.FC<IListPageLayoutProps> = (
className,
children,
authHeight = true,
childrenClassName,
childrenStyle,
title,
...props
}) => {
@ -61,7 +65,9 @@ const ListPageLayout: React.FC<IListPageLayoutProps> = (
{...props}
>
<div className={cx({
<div data-childen={true}
style={childrenStyle}
className={cx(childrenClassName,{
[styles.authHeight]: authHeight
})}>
{children}

204
src/pages/websites/cert/apply.tsx

@ -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

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

@ -24,11 +24,13 @@ import google from './assets/google.png'
import lets_encrypt from './assets/lets_encrypt.png'
import zerossl from './assets/zerossl.png'
import ModalPro from '@/components/modal-pro'
import { useNavigate } from '@tanstack/react-router'
const i18nPrefix = 'cert.list'
const Cert = () => {
const navigate = useNavigate()
const { styles, cx } = useStyle()
const { t } = useTranslation()
const [ form ] = Form.useForm()
@ -222,11 +224,9 @@ const Cert = () => {
headerTitle={
<Button key={'add'}
onClick={() => {
form.resetFields()
form.setFieldsValue({
id: 0,
navigate({
to: '/websites/cert/apply'
})
setOpen(true)
}}
type={'primary'}>{t(`${i18nPrefix}.add`, '免费申请证明')}</Button>
@ -303,7 +303,7 @@ const Cert = () => {
},
}}
/>
<BetaSchemaForm
{/* <BetaSchemaForm
grid={true}
shouldUpdate={false}
width={1000}
@ -311,7 +311,7 @@ const Cert = () => {
layout={'vertical'}
scrollToFirstError={true}
title={t(`${i18nPrefix}.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '账号管理编辑' : '账号管理添加')}
layoutType={'DrawerForm'}
layoutType={'Embed'}
open={open}
drawerProps={{
maskClosable: false,
@ -326,7 +326,7 @@ const Cert = () => {
onFinish={async (values) => {
saveOrUpdate(values)
}}
columns={columns as ProFormColumnsType[]}/>
columns={columns as ProFormColumnsType[]}/>*/}
<BetaSchemaForm
title={t(`${i18nPrefix}.filter.title`, '账号管理高级查询')}
grid={true}

67
src/pages/websites/cert/style.ts

@ -1,8 +1,11 @@
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 { scrollbarBackground } = useScrollStyle()
const container = css`
.ant-table-cell {
.ant-tag {
@ -10,17 +13,81 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
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;
}
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,
}
})

9
src/routes.tsx

@ -242,7 +242,14 @@ const generateDynamicRoutes = (menuData: MenuItem[], parentRoute: AnyRoute) => {
component = component.replace(/\/index$/, '')
}
const module = modules[`./pages/${component}/index.tsx`] || modules[`./pages/${component}/index.jsx`]
let module: () => Promise<any>
//优先匹配无index的情况
if (modules[`./pages/${component}.tsx`] || modules[`./pages/${component}.jsx`]) {
module = modules[`./pages/${component}.tsx`] || modules[`./pages/${component}.jsx`]
} else {
module = modules[`./pages/${component}/index.tsx`] || modules[`./pages/${component}/index.jsx`]
}
if (!module) {
return NotFound
}

11
src/service/websites.ts

@ -64,7 +64,16 @@ const websitesServ = {
},
})
// return request.post<any, any>('/website/cert/list', params)
}
},
//dns_config
dnsConfig: async (params: any) => {
return request.post<any, any>('/website/cert/dns_config', params)
},
//dns_verify
dnsVerify: async (params: any) => {
return request.post<any, any>('/website/cert/dns_verify', params)
},
},
ssl: {
...createCURD<any, WebSite.ISSL>('/website/ssl'),

31
src/store/websites/cert.ts

@ -72,10 +72,10 @@ export const saveOrUpdateCertAtom = atomWithMutation<IApiResult, ICertificate>((
mutationKey: [ 'updateCert' ],
mutationFn: async (data) => {
//data.status = data.status ? '1' : '0'
if (data.id === 0) {
return await websitesServ.cert.add(data)
}
if ( data.id) {
return await websitesServ.cert.update(data)
}
return await websitesServ.cert.add(data)
},
onSuccess: (res) => {
const isAdd = !!res.data?.id
@ -105,3 +105,28 @@ export const deleteCertAtom = atomWithMutation((get) => {
}
}
})
//dnsConfig
export const dnsConfigAtom = (domains: string[] | string) => atomWithQuery<IApiResult, any>(() => {
if (typeof domains === 'string') {
domains = domains.split('\n').filter(Boolean)
}
return {
enabled: domains.length > 0,
queryKey: [ 'dnsConfig', domains ],
queryFn: async ({ queryKey: [ , domains ] }) => {
if ((domains as string[]).length === 0){
return Promise.reject({
data: []
})
}
return await websitesServ.cert.dnsConfig({ dns_full_list: (domains as string[]).filter(Boolean), parse: true })
},
select: res => {
return res.data
}
}
})
Loading…
Cancel
Save