diff --git a/package.json b/package.json index 3bac782..a5222fb 100644 --- a/package.json +++ b/package.json @@ -12,58 +12,59 @@ }, "dependencies": { "@ant-design/cssinjs": "^1.21.1", - "@ant-design/icons": "^5.3.6", - "@ant-design/pro-components": "^2.7.0", + "@ant-design/icons": "^5.4.0", + "@ant-design/pro-components": "^2.7.15", "@ant-design/pro-layout": "^7.19.12", "@ant-design/pro-provider": "^2.14.9", - "@formily/antd-v5": "^1.2.0", - "@formily/core": "^2.3.1", - "@formily/react": "^2.3.1", + "@formily/antd-v5": "^1.2.3", + "@formily/core": "^2.3.2", + "@formily/react": "^2.3.2", "@icon-park/react": "^1.4.2", "@melloware/react-logviewer": "^5.2.0", - "@tanstack/query-core": "^5.52.0", - "@tanstack/react-query": "^5.52.1", - "@tanstack/react-router": "^1.50.0", - "antd": "^5.16.1", + "@tanstack/query-core": "^5.54.1", + "@tanstack/react-query": "^5.54.1", + "@tanstack/react-router": "^1.53.1", + "antd": "^5.20.5", "antd-style": "^3.6.2", - "axios": "^1.6.8", - "bunshi": "^2.1.4", - "dayjs": "^1.11.10", + "axios": "^1.7.7", + "bunshi": "^2.1.5", + "date-fns": "^3.6.0", + "dayjs": "^1.11.13", "fast-copy": "^3.0.2", "fast-deep-equal": "^3.1.3", - "i18next": "^23.11.2", + "i18next": "^23.14.0", "i18next-browser-languagedetector": "^7.2.1", - "jotai": "^2.8.0", + "jotai": "^2.9.3", "jotai-devtools": "^0.9.1", - "jotai-scope": "^0.5.1", - "jotai-tanstack-query": "^0.8.5", + "jotai-scope": "^0.5.2", + "jotai-tanstack-query": "^0.8.7", "lodash": "^4.17.21", - "re-resizable": "^6.9.11", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-i18next": "^14.1.0", + "re-resizable": "^6.9.17", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-i18next": "^14.1.3", "react-if": "^4.1.5", "react-layout-kit": "^1.9.0", - "react-rnd": "^10.4.2-test2", - "react-use": "^17.5.0", - "throttle-debounce": "^5.0.0", + "react-rnd": "^10.4.12", + "react-use": "^17.5.1", + "throttle-debounce": "^5.0.2", "use-merge-value": "^1.2.0", "wonka": "^6.3.4" }, "devDependencies": { - "@tanstack/router-devtools": "^1.26.20", - "@tanstack/router-vite-plugin": "^1.26.16", - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", - "@typescript-eslint/eslint-plugin": "^7.2.0", - "@typescript-eslint/parser": "^7.2.0", - "@vitejs/plugin-react": "^4.2.1", + "@tanstack/router-devtools": "^1.53.1", + "@tanstack/router-vite-plugin": "^1.54.0", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "@vitejs/plugin-react": "^4.3.1", "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.11", "mockjs": "^1.1.0", - "typescript": "^5.2.2", - "vite": "^5.2.0", - "vite-plugin-mock": "^3.0.1" + "typescript": "^5.5.4", + "vite": "^5.4.3", + "vite-plugin-mock": "^3.0.2" } } diff --git a/src/App.css b/src/App.css index f457a94..7edb8e1 100644 --- a/src/App.css +++ b/src/App.css @@ -7,12 +7,10 @@ display: inherit; line-height: 28px; } - } .top-breadcrumb { .item { - display: flex; align-items: center; justify-content: center; @@ -28,7 +26,6 @@ cursor: pointer; &:hover { - color: #1c7ed6; } } @@ -50,16 +47,19 @@ color: green; } -.color-green1{ - color: rgb(18 185 128 / 1 ); +.color-green1 { + color: rgb(18 185 128 / 1); +} +.color-blue { + color: #1c7ed6; } .color-red { - color: #F56C6C; + color: #f56c6c; } .color-yellow { - color: rgb(250 145 0) + color: rgb(250 145 0); } .text-bold { diff --git a/src/components/action/Action.tsx b/src/components/action/Action.tsx index 0bc3477..af949db 100644 --- a/src/components/action/Action.tsx +++ b/src/components/action/Action.tsx @@ -2,23 +2,27 @@ import { Button, ButtonProps } from 'antd' import { useStyle } from './style' export interface ActionProps extends ButtonProps { - as?: string + as?: string } const Action = ({ title, as, children, ...props }: ActionProps) => { - const { styles } = useStyle() + const { styles } = useStyle() - const isLink = as === 'a' || props.type === 'link' - //fixme 如果外部同时设置 as={'a'} disabled={true} ,这里a标签会置灰,但是仍可点击,为什么不直接用Button? - const Comp = isLink ? 'a' : Button - return ( - + const isLink = as === 'a' || props.type === 'link' + const Comp = isLink ? 'a' : Button + return ( + {title ?? children} + onClick={(e) => { + if (props.onClick && !props.disabled) { + props.onClick(e) + } + }} + type={isLink ? 'link' : props.type} + className={as === 'a' ? styles.actionA : ''}>{title ?? children} - ) + ) } export default Action \ No newline at end of file diff --git a/src/components/r-form/index.tsx b/src/components/r-form/index.tsx index c47f638..41dcc30 100644 --- a/src/components/r-form/index.tsx +++ b/src/components/r-form/index.tsx @@ -13,15 +13,32 @@ import { BetaSchemaForm, ProColumns, ProFormColumnsType } from '@ant-design/pro- import { useApiContext } from '@/context.ts' import { useDeepCompareEffect } from 'react-use' import { RFormTypes } from '@/types/r-form/model' +import { ProCoreActionType } from '@ant-design/pro-utils/es/typing' +import { getI18nTitle } from '@/i18n.ts' export interface RFormProps { title?: ReactNode namespace?: string columns?: ProColumns[] //重写columns + actions?: ReactNode[] | JSX.Element[] //左上角的操作按钮 + + toolbar?: ReactNode //工具栏 + renderActions?: (addAction: ReactNode) => ReactNode //渲染操作按钮 + resolveColumns?: (columns: ProColumns[]) => ProColumns[] //处理columns + renderColumnOptions?: (record: any, defaultOptions: ReactNode[], index: number, action: ProCoreActionType | undefined) => ReactNode //渲染列的操作 } -const RForm = ({ namespace, columns: propColumns = [], title }: RFormProps) => { +const RForm = ( + { + namespace, + actions = [], + toolbar, + resolveColumns, + renderActions, + renderColumnOptions, + columns: propColumns = [], title + }: RFormProps) => { const { styles, cx } = useStyle() const apiCtx = useApiContext() @@ -50,7 +67,33 @@ const RForm = ({ namespace, columns: propColumns = [], title }: RFormProps) => { useDeepCompareEffect(() => { - const res = transformAntdTableProColumns(curdModal?.columns || [], propColumns) + let res = transformAntdTableProColumns(curdModal?.columns || [], propColumns, curdModal?.config?.i18n) + if (resolveColumns) { + res = resolveColumns(res) + } + + const options = (record: any) => { + return [ + { + form.setFieldsValue(record) + setOpen(true) + }}>{'编辑'}, + { + deleteModel([ record.id ]) + }} + title={'确定要删除吗?'}> + + 删除 + + + ] + } + const _columns = [ { title: 'ID', dataIndex: 'id', @@ -59,33 +102,20 @@ const RForm = ({ namespace, columns: propColumns = [], title }: RFormProps) => { formItemProps: { hidden: true } } ].concat(res as any).concat([ { - title: '操作', + title: getI18nTitle(curdModal?.config?.i18n, { dataIndex: 'option', title: '操作' },), dataIndex: 'option', valueType: 'option', fixed: 'right', - render: (_, record) => [ - { - form.setFieldsValue(record) - setOpen(true) - }}>{'编辑'}, - { - deleteModel([ record.id ]) - }} - title={'确定要删除吗?'}> - - 删除 - - - ] + render: (_, record, index, action) => { + if (renderColumnOptions) { + return renderColumnOptions(record, options(record), index, action) + } + return options(record) + } } as any ]) setColumns(_columns) - }, [ curdModal?.columns, propColumns, deleteModel, form, isDeleting, setOpen, ]) + }, [ curdModal?.columns, curdModal?.config?.i18n, propColumns, renderColumnOptions, resolveColumns, deleteModel, form, isDeleting, setOpen, ]) useEffect(() => { if (apiCtx.isApi && apiCtx.api) { @@ -141,9 +171,18 @@ const RForm = ({ namespace, columns: propColumns = [], title }: RFormProps) => { }) setOpen(true) }} - type={'primary'}>{'添加'} + type={'primary'}>{getI18nTitle('actions.add','添加')} + const _renderActions = () => { + if (renderActions) { + return renderActions(tableTitle) + } + return + {[ tableTitle, ...actions ]} + + } + return ( <> { { placeholder: '输入关键字搜索', },*/ actions: [ - + + + + + + + ); +}); - return ( -
-
- -
-
-
+export const Route = createFileRoute("/login")({ + component: Login, +}); -
- - {t('login.title')} - - -
- -
{ - setValues(allValues) - }} - size="large"> - - - - - - - - - - - {/*验证码*/} - - - - - - -
-
-
- -
- ) -}) - -export const Route = createFileRoute('/login')({ - component: Login -}) - -export default Login \ No newline at end of file +export default Login; diff --git a/src/pages/r-form/index.tsx b/src/pages/r-form/index.tsx index 8715a53..a51a49b 100644 --- a/src/pages/r-form/index.tsx +++ b/src/pages/r-form/index.tsx @@ -1,10 +1,68 @@ import { createFileRoute } from '@tanstack/react-router' import RForm from '@/components/r-form' +import { Button, Input, message, Modal } from 'antd' +import { ReactNode, useState } from 'react' +import { get, hasIn } from 'lodash' +import { ProCoreActionType } from '@ant-design/pro-utils/es/typing' const RFormRender = () => { + const [ json, setJson ] = useState('') + const [ open, setOpen ] = useState(false) + const [ columns, setColumns ] = useState() + + const actions = [ + + ] + + const renderOptions = (record: any, defaultOptions: ReactNode[], index: number, action: ProCoreActionType | undefined) => { + return [ + ...defaultOptions, + + ] + } + return <> - + columns ?? cols}/> + setOpen(false)} + onOk={() => { + try { + const data = JSON.parse(json) + //如果是Array, 检查元素是否为ProColumns + if (Array.isArray(data)) { + if (data.length > 0 && hasIn(data[0], 'dataIndex')) { + setColumns(data as any) + setOpen(false) + return + } + } + if (hasIn(data, 'data.page.columns')) { + setColumns(get(data, 'data.page.columns')) + setOpen(false) + return + } + message.error('JSON格式错误, 请确保是ProColumns或者包含data.page.columns的对象') + + } catch (e) { + message.error('JSON格式错误') + } + }} + > + { + setJson(e.target.value) + }}/> + 请确保是Antd.Table.Columns数组或者xxx/ui/curd返回的结构 + } diff --git a/src/pages/websites/cert/apply.tsx b/src/pages/websites/cert/apply.tsx index 5a271d5..9ec33ea 100644 --- a/src/pages/websites/cert/apply.tsx +++ b/src/pages/websites/cert/apply.tsx @@ -2,122 +2,228 @@ import { t } from "@/i18n.ts"; import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { algorithmTypes, - dnsConfigAtom, - dnsVerifyAtom, - dnsVerifyOKAtom, - saveOrUpdateCertAtom, + certAddCnameAtom, + checkDomainAtom, + applyTxtCertificateAtom, + Req_ApplyTxtCertificate, + getCertConfigAtom, } from "@/store/websites/cert.ts"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Button, Flex, Form, Input, Progress, Select, Space, Table, Tooltip } from "antd"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Button, Flex, Form, Input, message, Radio, Select, Space, Steps, Table, Tooltip } 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"; -import { atomWithStorage } from "jotai/utils"; +import { atomWithStorage, RESET } from "jotai/utils"; import Copy from "@/components/copy"; -import { InfoCircleOutlined, LoadingOutlined } from "@ant-design/icons"; - +import { + ClusterOutlined, + CopyOutlined, + InfoCircleOutlined, + LoadingOutlined, + LoginOutlined, + TagOutlined, +} from "@ant-design/icons"; +import { useQueryClient } from "@tanstack/react-query"; const i18nPrefix = "cert.apply"; -const BrandSelect = (props: any) => { - const { styles, cx } = useStyle(); - const [value, setValue] = useState(() => props.value); - +const DomainsInput = (props: { domains; setDomains; currentDomainMod; setCurrentDomainMod; currentStep }) => { useEffect(() => { - setValue(props.value); - }, [props.value]); + if (props.currentDomainMod === "single" && props.domains !== "") { + props.setDomains(props.domains.replace(/[\r\n]/g, "")); + } + }, [props.currentDomainMod]); + return ( + + props.setCurrentDomainMod(e.target.value)} + > + + 单证书申请 + + + 多证书申请 + + + {props.currentDomainMod === "single" && ( + { + props.setDomains(e.target.value); + }} + value={props.domains} // 设置输入框的值 + /> + )} + {props.currentDomainMod === "multiple" && ( + { + props.setDomains(e.target.value); + }} + value={props.domains} // 设置输入框的值 + /> + )} + + ); +}; + +const BrandSelect = (props: { acme_type; setAcme_type }) => { + const { styles, cx } = useStyle(); - const onChange = useCallback((val: string) => { - props.onChange?.(val); - }, []); + const onChange = (val: string) => { + props.setAcme_type(val); + }; return ( - <> - - onChange("Google")} - className={cx("band-normal", { - "band-active": value === "Google", - })} - > - - Google Trust Services - - onChange("ZeroSSL")} - className={cx("band-normal", { - "band-active": value === "ZeroSSL", - })} - > - - ZeroSSL - - onChange("Let's Encrypt")} - className={cx("band-normal", { - "band-active": value === "Let's Encrypt", - })} - > - - Let's Encrypt - - - + <> + + onChange("Google")} + className={cx("band-normal", { + "band-active": props.acme_type === "Google", + })} + > + + Google Trust Services + + onChange("ZeroSSL")} + className={cx("band-normal", { + "band-active": props.acme_type === "ZeroSSL", + })} + > + + ZeroSSL + + onChange("Let's Encrypt")} + className={cx("band-normal", { + "band-active": props.acme_type === "Let's Encrypt", + })} + > + + Let's Encrypt + + + ); }; -const StatusTable = (props: { value: string }) => { - const [data, setData] = useState(null); // 临时状态来存储模拟数据 - const { isFetching } = useAtomValue(useMemo(() => dnsConfigAtom(props.value), [props.value])); - - const { - data: dnsVerifyStatus, - isFetching: isVerifyFetching, - refetch, - } = useAtomValue(useMemo(() => dnsVerifyAtom(props.value, isFetching), [props.value, isFetching])); +const StatusTable = (props: { value: string; setCurrentStep; setDns_list }) => { + // const [data, setData] = useState(null); // 临时状态来存储模拟数据 + const [forceUpdate, setForceUpdate] = useState(0); + const { data, isFetching: isVerifyFetching } = useAtomValue( + useMemo(() => checkDomainAtom(props.value, forceUpdate === 0), [props.value, forceUpdate]), + ); + const refetch = () => { + setForceUpdate((prev) => prev + 1); + }; - const setDnsVerifyOK = useSetAtom(dnsVerifyOKAtom); + const { mutate: addCnameFun, isPending: addCnamePending, isSuccess } = useAtomValue(certAddCnameAtom); + const handleAddCname = async (info: any) => { + addCnameFun(info); + }; + // const { + // data: dnsVerifyStatus, + // isFetching: isVerifyFetching, + // refetch, + // } = useAtomValue(useMemo(() => dnsVerifyAtom(props.value, isFetching), [props.value, isFetching])); + // + // const setDnsVerifyOK = useSetAtom(dnsVerifyOKAtom); const timerRef = useRef(); + const handleCopy = (str: string) => { + navigator.clipboard + .writeText(str) + .then(() => { + message.info("复制成功!"); + }) + .catch((err) => { + message.info(err); + }); + }; + const columns = useMemo(() => { return [ { title: ( - <> - {t(`${i18nPrefix}.status.columns.status`, "状态")} - - - {" "} - + <> + {t(`${i18nPrefix}.status.columns.status`, "状态")} + + + {" "} + ), dataIndex: "status", - width: 100, + width: 150, render: (_, record) => { - if (isFetching) { - return {t(`${i18nPrefix}.actions.dnsVerifyStatus.0`, "等待")}; - } - - if (isVerifyFetching) { - //0,等待 1,域名OK,2,域名分析错误,3:检测中 4:检测成功,匹配失败 5:检测失败,9:检测成功 - return ( - - - {t(`${i18nPrefix}.actions.dnsVerifyStatus.3`, "检测中")} - - ); - } const dns = record.dns_name; - const info = (dnsVerifyStatus as any)?.find((item) => item.dns_name === dns) as any; + const info = (data as any)?.dns_list?.find((item) => item.dns_name === dns) as any; if (info) { - return {t(`${i18nPrefix}.actions.dnsVerifyStatus.${info.status}`, `${info?.status_txt}`)}; + if (info.is_self === true) { + return ( + // + { + handleAddCname(info); + }} + > + + 自动注册 + + ); + } else { + if (isVerifyFetching) { + //0,等待 1,域名OK,2,域名分析错误,3:检测中 4:检测成功,匹配失败 5:检测失败,9:检测成功 + return ( + + + {t(`${i18nPrefix}.actions.dnsVerifyStatus.3`, "检测中")} + + ); + } + //0,等待 1,域名OK,2,域名分析错误,3:检测中 4:检测成功,匹配失败 5:检测失败,9:检测成功 + return ( + + {t(`${i18nPrefix}.actions.dnsVerifyStatus.${info.status}`, `${info?.status_txt}`)} + + ); + } } - return {t(`${i18nPrefix}.actions.dnsVerifyStatus.0`, "等待")}; + return ( + + {t(`${i18nPrefix}.actions.dnsVerifyStatus.0`, "等待")} + + ); }, }, { @@ -127,9 +233,9 @@ const StatusTable = (props: { value: string }) => { width: 150, render(text) { if (text) { - return {text}; + return {text}; } - return 未知; + return 未知; }, }, { @@ -137,6 +243,20 @@ const StatusTable = (props: { value: string }) => { title: t(`${i18nPrefix}.status.columns.domain`, "域名"), dataIndex: "dns_name", width: 200, + render(text) { + return ( + + ); + }, }, { //主机记录 @@ -162,39 +282,29 @@ const StatusTable = (props: { value: string }) => { dataIndex: "record_value", width: 200, render: (text) => { - return ; + return ( + + ); }, }, ] as ColumnsType; - }, [isFetching, isVerifyFetching, dnsVerifyStatus]); + }, [isVerifyFetching, data]); useEffect(() => { // 模拟数据 - const mockData = { - dns_list: [ - { - status: 1, - name_servers: "Example NS", - dns_name: "example.com", - host: "example", - type: "A", - record_value: "192.168.1.1" - }, - { - status: 3, - name_servers: "Another NS", - dns_name: "another.com", - host: "another", - type: "CNAME", - record_value: "cname.another.com" - } - ] - }; - - setData(mockData); - if ((dnsVerifyStatus as any)?.every((item) => item.status === 9)) { - setDnsVerifyOK(true); + if ((data as any)?.check_ok === true) { + props.setCurrentStep(2); + props.setDns_list((data as any).dns_list); return; } @@ -203,8 +313,12 @@ const StatusTable = (props: { value: string }) => { return; } // dnsVerifyStatus 如果所有status 为 9 则说明域名验证通过 - if ((dnsVerifyStatus as any)?.every((item) => item.status === 9)) { - setDnsVerifyOK(true); + // if ((data as any)?.dns_list?.every((item) => item.status === 9)) { + // props.setCurrentStep(2); + // window.clearInterval(timerRef.current); + if ((data as any)?.check_ok === true) { + props.setCurrentStep(2); + props.setDns_list((data as any).dns_list); window.clearInterval(timerRef.current); } else { refetch(); @@ -214,145 +328,205 @@ const StatusTable = (props: { value: string }) => { return () => { window.clearInterval(timerRef.current); }; - }, [dnsVerifyStatus, isVerifyFetching]); + }, [data, isVerifyFetching]); return ( - <> -
- -
请您添加以下DNS解析记录
- -
-
1. 只需要添加一次即可,添加后请勿删除记录。
-
2. 耐心等待1~2分钟。
-
- - + <> +
+ +
请您添加以下DNS解析记录
+ +
+
1. 只需要添加一次即可,添加后请勿删除记录。
+
2. 耐心等待1~2分钟。
+
+
+ ); }; -const domainsAtom = atomWithStorage("domains", ""); +const domainsAtom = atomWithStorage("cert_domains", ""); +// 打印初始值 +console.log("初始文本值:", localStorage.getItem("cert_domains") || ""); const Apply = () => { const { styles } = useStyle(); const [form] = Form.useForm(); - const { mutate: saveOrUpdate, isPending: isSubmitting } = useAtomValue(saveOrUpdateCertAtom); + const { + mutate: applyTxtCertificateFun, + isPending: applyTxtCertificatePending, + isSuccess: applyTxtCertificateIsSuccess, + } = useAtomValue(applyTxtCertificateAtom); + const [domains, setDomains] = useAtom(domainsAtom); - const dnsVerifyOK = useAtomValue(dnsVerifyOKAtom); + const [currentStep, setCurrentStep] = useState(0); + const [currentDomainMod, setCurrentDomainMod] = useState<"single" | "multiple">("single"); + const [dns_list, setDns_list] = useState([]); + const [acme_type, setAcme_type] = useState("Google"); + const [key_rsa, setKey_rsa] = useState(""); + + const { data: configData } = useAtomValue(useMemo(() => getCertConfigAtom(), [])); + + const handleAlgorithmChange = (value: string) => { + setKey_rsa(value); + }; + const applyTxtCertificateClick = () => { + const data: Req_ApplyTxtCertificate = { + is_sync: true, + acme_type: acme_type, + key_rsa: key_rsa, + dns_list: dns_list, + remark: form.getFieldValue("remark"), + }; + applyTxtCertificateFun(data); + }; useEffect(() => { - if (domains) { - form.setFieldsValue({ - domains, - }); - } - }, [domains]); + form.setFieldsValue({ domains: domains }); + form.setFieldsValue({ brand: acme_type }); + form.setFieldsValue({ algorithm: key_rsa }); + }, [domains, acme_type, key_rsa, form]); return ( - -
{ - // console.log('onValuesChange', values) - if (values.domains) { - // setDomains(values.domains) - } - }} - onFinish={async (values) => { - if (dnsVerifyOK) { - saveOrUpdate(values); - } - }} + +
+ +
+ { + // console.log('onValuesChange', values) + if (values.domains) { + // setDomains(values.domains) + } + }} + onFinish={async (values) => { + // if (dnsVerifyOK) { + // saveOrUpdate(values); + // } + }} + > + {/* */} + {/* */} + {/**/} + {/* {t(`${i18nPrefix}.apply.remaining`, "剩余5张")}*/} + {/* / {t(`${i18nPrefix}.apply.total`, "共5张")}*/} + {/**/} + {/* */} + {/* */} + {/* */} + + + + {currentStep !== 0 && ( + <> + - {/* */} - {/* */} - {/**/} - {/* {t(`${i18nPrefix}.apply.remaining`, "剩余5张")}*/} - {/* / {t(`${i18nPrefix}.apply.total`, "共5张")}*/} - {/**/} - {/* */} - {/* */} - {/* */} - - { - setDomains(e.target.value); - }} - /> - - - - - - - - + + + + + + + + + + )} + + + {currentStep !== 0 && ( + + )} + + {currentStep === 0 && ( + + )} + + {currentStep !== 0 && ( + - - - - -
+ {t(`${i18nPrefix}.apply.submit`, "提交申请")} + + )} + + + +
); }; -export default Apply; \ No newline at end of file +export default Apply; diff --git a/src/service/websites.ts b/src/service/websites.ts index aff0817..2f7f1fc 100644 --- a/src/service/websites.ts +++ b/src/service/websites.ts @@ -1,84 +1,100 @@ -import { createCURD } from '@/service/base.ts' -import { WebSite } from '@/types' -import request from '@/request.ts' -import { IWebsiteDomain, INameServer } from '@/types/website/domain' -import { IWebsiteDnsRecords } from '@/types/website/record' -import { IWebsiteDnsAccount } from '@/types/website/dns_account' +import { createCURD } from "@/service/base.ts"; +import { WebSite } from "@/types"; +import request from "@/request.ts"; +import { IWebsiteDomain, INameServer } from "@/types/website/domain"; +import { IWebsiteDnsRecords } from "@/types/website/record"; +import { IWebsiteDnsAccount } from "@/types/website/dns_account"; const websitesServ = { cert: { - ...createCURD('/website/cert'), - //dns_config - dnsConfig: async (params: any) => { - return request.post('/website/cert/dns_config', params) + ...createCURD("/website/cert"), + // 发起域名检测 + checkDomain: async (params: any) => { + return request.post("/cert/apply/dns_config", params); }, - //dns_verify - dnsVerify: async (params: any) => { - return request.post('/website/cert/dns_verify', params) + getCertConfig: async () => { + return request.get("/cert/apply/acme/key"); }, - //cert-apply - certApply: async (params: any) => { - return request.post('/website/cert/dns_verify', params) + // 申请list + getCertList: async (arams: any) => { + return request.post("/cert/apply/list"); + }, + // 证书续签 + renewCertificate: async (params: any) => { + return request.post("/website/cert/renew_certificate", params); + }, + // 添加记录 + addCnameCertificate: async (params: any) => { + return request.post("/cert/apply/add/cname", params); + }, + // 下载证书 + downloadCertificate: async (params: any) => { + return request.post("/website/cert/download_certificate", params); + }, + // 获取证书申请日志 + getCertificateLogs: async (params: any) => { + return request.get("/website/cert/get_certificate_logs", { params }); + }, + applyTxtCertificate: async (params: any) => { + return request.post("/cert/apply/resolve", params); }, - }, ssl: { - ...createCURD('/website/ssl'), + ...createCURD("/website/ssl"), upload: async (params: WebSite.SSLUploadDto) => { - return request.post('/website/ssl/upload', params) + return request.post("/website/ssl/upload", params); }, download: async (params: any) => { - return request.download('/website/ssl/download', params) + return request.download("/website/ssl/download", params); }, }, acme: { - ...createCURD('/website/acme') + ...createCURD("/website/acme"), }, dns: { - ...createCURD('/cert/dns_account'), + ...createCURD("/cert/dns_account"), sync: async (id: any) => { - return request.post('/cert/dns_account/sync', { id: id }) - } + return request.post("/cert/dns_account/sync", { id: id }); + }, }, ca: { - ...createCURD('/website/ca'), + ...createCURD("/website/ca"), obtainSsl: async (params: WebSite.ISSLObtainByCA) => { - return request.post('/website/ca/obtain_ssl', params) + return request.post("/website/ca/obtain_ssl", params); }, }, domain: { - ...createCURD('/cert/domain'), + ...createCURD("/cert/domain"), //remark - remark: async (params: { id: string, remark: string }) => { - return request.post('/cert/domain/remark', params) + remark: async (params: { id: string; remark: string }) => { + return request.post("/cert/domain/remark", params); }, //tag - tag: async (params: { id: string, tags: string }) => { - return request.post('/cert/domain/tag', params) + tag: async (params: { id: string; tags: string }) => { + return request.post("/cert/domain/tag", params); }, //binding - binding: async (params: { id: string, user_id: string }) => { - return request.post('/cert/domain/binding', params) + binding: async (params: { id: string; user_id: string }) => { + return request.post("/cert/domain/binding", params); }, //group - group: async (params: { id: string[], group_id: string }) => { - return request.post('/cert/domain/group', params) + group: async (params: { id: string[]; group_id: string }) => { + return request.post("/cert/domain/group", params); }, describeDomainNS: async (params: { id: number }) => { - return request.post('/cert/domain/describe_domain_ns', params) + return request.post("/cert/domain/describe_domain_ns", params); }, - }, record: { - ...createCURD('/cert/dns_records'), + ...createCURD("/cert/dns_records"), // }, dnsAccount: { - ...createCURD('/cert/dns_account'), + ...createCURD("/cert/dns_account"), sync: async (params: IWebsiteDnsAccount) => { - return request.post('/cert/dns_account/sync', params) - } + return request.post("/cert/dns_account/sync", params); + }, }, -} +}; -export default websitesServ \ No newline at end of file +export default websitesServ; diff --git a/src/store/websites/cert.ts b/src/store/websites/cert.ts index a1f9038..a34458c 100644 --- a/src/store/websites/cert.ts +++ b/src/store/websites/cert.ts @@ -1,185 +1,248 @@ -import { atom } from 'jotai' -import { IApiResult, IPage } from '@/global' -import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' -import { message } from 'antd' -import { t } from 'i18next' -import websitesServ from '@/service/websites.ts' - - -type SearchParams = IPage & { - name?: string +import { atom } from "jotai"; +import { IApiResult, IPage } from "@/global"; +import { atomWithMutation, atomWithQuery, queryClientAtom } from "jotai-tanstack-query"; +import { message } from "antd"; +import { t } from "i18next"; +import websitesServ from "@/service/websites.ts"; + +export type Req_SearchParams = IPage & { + name?: string; +}; + +export interface Req_AddCname { + is_sync: boolean; + dns_list: ICertificate[]; } +export interface Req_ApplyTxtCertificate { + is_sync: boolean; + acme_type: string; + key_rsa: string; + dns_list: ICertificate[]; + remark: string; +} +//=========================证书列表 +export interface Req_CertList { + order: string; + prop: string; + page: number; + pageSize: number; +} +export interface Resp_CertList { + page: number; + pageSize: number; + total: number; + rows: any; +} +//=========================证书列表 export const bandTypes = [ - { label: 'Google', value: 'Google' }, - { label: 'ZeroSSL', value: 'ZeroSSL' }, - { label: 'Let\'s Encrypt', value: 'Let\'s Encrypt' }, -] + { + label: "LetsEncrypt", + value: "LetsEncrypt", + }, + { + label: "ZeroSsl", + value: "ZeroSsl", + }, + { + label: "Google", + value: "Google", + }, +]; export const algorithmTypes = [ - { label: 'RSA', value: 'RSA' }, - { label: 'ECC', value: 'ECC' }, -] - + { label: "RSA", value: "RSA" }, + { label: "ECC", value: "ECC" }, +]; export const StatusText = { - 1: [ '已签发', 'green' ], - 2: [ '申请中', 'default' ], - 3: [ '申请失败', 'red' ] -} - + 1: ["已签发", "green"], + 2: ["申请中", "default"], + 3: ["申请失败", "red"], +}; -export const certIdAtom = atom(0) +export const certIdAtom = atom(0); -export const certIdsAtom = atom([]) +export const certIdsAtom = atom([]); -export const certAtom = atom(undefined as unknown as ICertificate) +export const certAtom = atom(undefined as unknown as ICertificate); -export const certSearchAtom = atom({ +export const certSearchAtom = atom({ // key: '', pageSize: 10, page: 1, -} as SearchParams) +} as Req_SearchParams); export const certPageAtom = atom({ pageSize: 10, page: 1, -}) - -//certApple -export const certAppleCertAtom = atomWithMutation((get) => { +}); + +//=================================================================================================================================================kelis +export const getCertConfigAtom = () => + atomWithQuery(() => { + return { + queryKey: ["getCertConfig"], + queryFn: async () => { + return await websitesServ.cert.getCertConfig(); + }, + select: (res) => { + return res.data; + }, + }; + }); + +export const checkDomainAtom = (domains: string, isClear: boolean) => + atomWithQuery(() => { + return { + enabled: domains.length > 0 && domains.includes("."), + queryKey: ["checkDomain", domains], + queryFn: async ({ queryKey: [, domains] }) => { + if ((domains as string).length === 0) { + return Promise.reject({ + data: [], + }); + } + return await websitesServ.cert.checkDomain({ + dns_full_list: domains, + parse: true, + is_clear: isClear, + }); + }, + select: (res) => { + return res.data; + }, + }; + }); + +export const certListAtom = (params: Req_CertList) => + atomWithQuery(() => { + return { + queryKey: ["certList", params], + queryFn: async ({ queryKey: [, params] }) => { + return await websitesServ.cert.getCertList(params); + }, + select: (res) => { + return res.data; + }, + }; + }); + +export const certAddCnameAtom = atomWithMutation(() => { return { - mutationKey: [ 'appleCert' ], + mutationKey: ["certAddCname"], mutationFn: async (data) => { - //data.status = data.status ? '1' : '0' - return await websitesServ.cert.certApply(data) + const dData: Req_AddCname = { + is_sync: true, + dns_list: [data], + }; + return await websitesServ.cert.addCnameCertificate(dData); }, onSuccess: (res) => { - const isAdd = !!res.data?.id - message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) - - //更新列表 - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore fix - get(queryClientAtom).invalidateQueries({ queryKey: [ 'certs', get(certSearchAtom) ] }) - - return res - } - } -}) - + const status = res.data?.item[0]?.status || 0; + const status_txt = res.data?.item[0]?.status_txt; + if (status || status === 5) { + message.error(status_txt); + } else { + message.success(status_txt); + } + return res; + }, + }; +}); +export const applyTxtCertificateAtom = atomWithMutation(() => { + return { + mutationKey: ["applyTxtCertificate"], + mutationFn: async (data: Req_ApplyTxtCertificate) => { + return await websitesServ.cert.applyTxtCertificate(data); + }, + onSuccess: (res) => { + return res; + }, + }; +}); + +//==================================================================================================================================================kelis + +// //certApple +// export const certAppleCertAtom = atomWithMutation((get) => { +// return { +// mutationKey: ["appleCert"], +// mutationFn: async (data) => { +// //data.status = data.status ? '1' : '0' +// return await websitesServ.cert.certApply(data); +// }, +// onSuccess: (res) => { +// const isAdd = !!res.data?.id; +// message.success(t(isAdd ? "message.saveSuccess" : "message.editSuccess", "保存成功")); +// +// //更新列表 +// // eslint-disable-next-line @typescript-eslint/ban-ts-comment +// // @ts-ignore fix +// get(queryClientAtom).invalidateQueries({ queryKey: ["certs", get(certSearchAtom)] }); +// +// return res; +// }, +// }; +// }); export const certsAtom = atomWithQuery((get) => { return { - queryKey: [ 'certs', get(certSearchAtom) ], - queryFn: async ({ queryKey: [ , params ] }) => { - return await websitesServ.cert.list(params as SearchParams) + queryKey: ["certs", get(certSearchAtom)], + queryFn: async ({ queryKey: [, params] }) => { + return await websitesServ.cert.list(params as Req_SearchParams); }, - select: res => { - const data = res.data - data.rows = data.rows?.map(row => { + select: (res) => { + const data = res.data; + data.rows = data.rows?.map((row) => { return { ...row, //status: convertToBool(row.status) - } - }) - return data - } - } -}) + }; + }); + return data; + }, + }; +}); //saveOrUpdateAtom export const saveOrUpdateCertAtom = atomWithMutation((get) => { - return { - mutationKey: [ 'updateCert' ], + mutationKey: ["updateCert"], mutationFn: async (data) => { //data.status = data.status ? '1' : '0' if (data.id) { - return await websitesServ.cert.update(data) + return await websitesServ.cert.update(data); } - return await websitesServ.cert.add(data) + return await websitesServ.cert.add(data); }, onSuccess: (res) => { - const isAdd = !!res.data?.id - message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) + const isAdd = !!res.data?.id; + message.success(t(isAdd ? "message.saveSuccess" : "message.editSuccess", "保存成功")); //更新列表 // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore fix - get(queryClientAtom).invalidateQueries({ queryKey: [ 'certs', get(certSearchAtom) ] }) + get(queryClientAtom).invalidateQueries({ queryKey: ["certs", get(certSearchAtom)] }); - return res - } - } -}) + return res; + }, + }; +}); export const deleteCertAtom = atomWithMutation((get) => { return { - mutationKey: [ 'deleteCert' ], + mutationKey: ["deleteCert"], mutationFn: async (ids: number[]) => { - return await websitesServ.cert.batchDelete(ids ?? get(certIdsAtom)) + return await websitesServ.cert.batchDelete(ids ?? get(certIdsAtom)); }, onSuccess: (res) => { - message.success('message.deleteSuccess') + message.success("message.deleteSuccess"); //更新列表 - get(queryClientAtom).invalidateQueries({ queryKey: [ 'certs', get(certSearchAtom) ] }) - return res - } - } -}) - -//dnsConfig -export const dnsConfigAtom = (domains: string) => atomWithQuery(() => { - - return { - enabled: domains.length > 0 && domains.includes('.'), - 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, - parse: (domains as string)?.includes('*') - }) - }, - select: res => { - return res.data - } - } -}) - -export const dnsVerifyOKAtom = atom(false) - -//query dnsVerify -export const dnsVerifyAtom = (domains: string, block: boolean) => atomWithQuery(() => { - - return { - enabled: !block && domains.length > 0 && domains.includes('.'), - queryKey: [ 'dnsVerify', domains ], - queryFn: async ({ queryKey: [ , domains ] }) => { - - if ((domains as string).length === 0) { - return Promise.reject({ - data: [] - }) - } - - return await websitesServ.cert.dnsVerify({ - dns_list: domains, - }) + get(queryClientAtom).invalidateQueries({ queryKey: ["certs", get(certSearchAtom)] }); + return res; }, - select: res => { - return res.data?.dns_list - } - } -}) - - + }; +}); diff --git a/src/types/r-form/model.d.ts b/src/types/r-form/model.d.ts index b5c1cc3..10a635c 100644 --- a/src/types/r-form/model.d.ts +++ b/src/types/r-form/model.d.ts @@ -27,6 +27,9 @@ export namespace RFormTypes { params?: any; resultPath?: string; + //是否需要转换成下拉框的数据或者树形数据格式, 默认为true + transform: boolean; + fieldNames?: { label: string; value: string; diff --git a/src/utils/index.ts b/src/utils/index.ts index 3abb93f..d75d7c4 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -216,3 +216,5 @@ export const genProTableColumnWidthProps = (width: string | number) => { } } + + diff --git a/vite.config.ts b/vite.config.ts index 75bb7c0..5a9c23e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,6 +13,7 @@ const proxyMap = { '/api/v1/movie': 'http://47.113.117.106:10000', //'/api/v1/certold': 'http://192.168.31.41:8000', '/api/v1/cert': 'http://127.0.0.1:8000', + //'/api/v1/cert': 'http://192.168.31.41:8000', } as Record const proxyConfig = Object.keys(proxyMap).reduce((acc, key) => {