1 changed files with 590 additions and 0 deletions
			
			
		@ -0,0 +1,590 @@ | 
			
		|||||
 | 
				import React, { useEffect, useMemo, useState } from "react"; | 
			
		||||
 | 
				import { | 
			
		||||
 | 
				  Table, | 
			
		||||
 | 
				  Input, | 
			
		||||
 | 
				  Button, | 
			
		||||
 | 
				  Tag, | 
			
		||||
 | 
				  Progress, | 
			
		||||
 | 
				  Space, | 
			
		||||
 | 
				  Drawer, | 
			
		||||
 | 
				  Typography, | 
			
		||||
 | 
				  Form, | 
			
		||||
 | 
				  DatePicker, | 
			
		||||
 | 
				  Tooltip, | 
			
		||||
 | 
				  Modal, | 
			
		||||
 | 
				  ProgressProps, | 
			
		||||
 | 
				} from "antd"; | 
			
		||||
 | 
				import { CheckCircleFilled, CheckCircleOutlined, CopyOutlined, SearchOutlined } from "@ant-design/icons"; | 
			
		||||
 | 
				import { t } from "@/i18n.ts"; | 
			
		||||
 | 
				import ListPageLayout from "@/layout/ListPageLayout.tsx"; | 
			
		||||
 | 
				import TextArea from "antd/es/input/TextArea"; | 
			
		||||
 | 
				import { useNavigate } from "@tanstack/react-router"; | 
			
		||||
 | 
				import { | 
			
		||||
 | 
				  certListAtom, | 
			
		||||
 | 
				  checkDomainAtom, | 
			
		||||
 | 
				  deletesCertificateAtom, | 
			
		||||
 | 
				  downloadCertificateAtom, | 
			
		||||
 | 
				  editCertificateAtom, | 
			
		||||
 | 
				  getCertificateLogsAtom, | 
			
		||||
 | 
				  Req_CertList, | 
			
		||||
 | 
				  req_CertLogs, | 
			
		||||
 | 
				  Req_DeletesCert, | 
			
		||||
 | 
				  Req_DownloadCert, | 
			
		||||
 | 
				  Req_UpdateCert, | 
			
		||||
 | 
				  saveOrUpdateCertAtom, | 
			
		||||
 | 
				} from "@/store/websites/cert.ts"; | 
			
		||||
 | 
				import { useAtomValue } from "jotai/index"; | 
			
		||||
 | 
				import { format } from "date-fns"; | 
			
		||||
 | 
				import websitesServ from "@/service/websites.ts"; | 
			
		||||
 | 
				import { useAtom } from "jotai"; | 
			
		||||
 | 
				import { getToken } from "@/store/system.ts"; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const { confirm } = Modal; | 
			
		||||
 | 
				const { Text, Link } = Typography; | 
			
		||||
 | 
				const i18nPrefix = "cert.management"; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const CertApplyingDrawer = (props: { id: number; onCloseApplyingDrawer; applyingDrawerVisible }) => { | 
			
		||||
 | 
				  const [dData, setDdata] = useState<req_CertLogs>({ | 
			
		||||
 | 
				    log_id: props.id, | 
			
		||||
 | 
				    log_type: "cert_apply", | 
			
		||||
 | 
				    websocket: false, | 
			
		||||
 | 
				    log_pos: 0, | 
			
		||||
 | 
				    // 根据实际数据结构添加其他属性的初始值
 | 
			
		||||
 | 
				  }); | 
			
		||||
 | 
				  const [showTextInfo, setShowTextInfo] = useState(""); | 
			
		||||
 | 
				  const [onChangeDT, setOnChangeDT] = useState(0); | 
			
		||||
 | 
				  const { data: certLogsData, isFetching: certLogsFetching } = useAtomValue( | 
			
		||||
 | 
				    useMemo(() => getCertificateLogsAtom(dData), [onChangeDT]), | 
			
		||||
 | 
				  ); | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useEffect(() => { | 
			
		||||
 | 
				    if (certLogsData && (certLogsData as any)?.log) { | 
			
		||||
 | 
				      setShowTextInfo((prev) => prev + (certLogsData as any).log.toString()); | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				  }, [certLogsData]); | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useEffect(() => { | 
			
		||||
 | 
				    if (props.applyingDrawerVisible) { | 
			
		||||
 | 
				      setShowTextInfo(""); | 
			
		||||
 | 
				      setOnChangeDT((prev) => prev + 1); | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				    let timer: number | undefined; | 
			
		||||
 | 
				    if (props.applyingDrawerVisible) { | 
			
		||||
 | 
				      timer = window.setInterval(() => { | 
			
		||||
 | 
				        setDdata((prevState) => ({ | 
			
		||||
 | 
				          ...prevState, | 
			
		||||
 | 
				          log_id: props.id, | 
			
		||||
 | 
				          log_pos: (certLogsData as any)?.log_pos || 0, | 
			
		||||
 | 
				        })); | 
			
		||||
 | 
				        // console.log("update drawer");
 | 
			
		||||
 | 
				        setOnChangeDT((prev) => prev + 1); | 
			
		||||
 | 
				      }, 5000); | 
			
		||||
 | 
				    } else { | 
			
		||||
 | 
				      setDdata((prevState) => ({ | 
			
		||||
 | 
				        ...prevState, | 
			
		||||
 | 
				        log_id: props.id, | 
			
		||||
 | 
				        log_pos: 0, | 
			
		||||
 | 
				      })); | 
			
		||||
 | 
				      window.clearInterval(timer); | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    return () => window.clearInterval(timer); | 
			
		||||
 | 
				  }, [props.applyingDrawerVisible]); | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  return ( | 
			
		||||
 | 
				    <Drawer | 
			
		||||
 | 
				      title="证书申请中..." | 
			
		||||
 | 
				      width={window.innerWidth / 2} | 
			
		||||
 | 
				      placement="right" | 
			
		||||
 | 
				      onClose={props.onCloseApplyingDrawer} | 
			
		||||
 | 
				      visible={props.applyingDrawerVisible} | 
			
		||||
 | 
				      bodyStyle={{ display: "flex", flexDirection: "column", height: "100%" }} | 
			
		||||
 | 
				    > | 
			
		||||
 | 
				      <TextArea value={showTextInfo} readOnly style={{ flex: 1 }} /> | 
			
		||||
 | 
				    </Drawer> | 
			
		||||
 | 
				  ); | 
			
		||||
 | 
				}; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const CertificateManagement: React.FC = () => { | 
			
		||||
 | 
				  const [dlDrawerVisible, setDLDrawerVisible] = useState(false); | 
			
		||||
 | 
				  const [editDrawerVisible, setEditDrawerVisible] = useState(false); | 
			
		||||
 | 
				  const [detailsVisible, setDetailsVisible] = useState(false); | 
			
		||||
 | 
				  const navigate = useNavigate(); | 
			
		||||
 | 
				  const dData: Req_CertList = { order: "", prop: "", page: 1, pageSize: 10 }; | 
			
		||||
 | 
				  const [updataList, setUpdataList] = useState(0); | 
			
		||||
 | 
				  const { data: certListData, isFetching: certListFetching } = useAtomValue( | 
			
		||||
 | 
				    useMemo(() => certListAtom(dData), [updataList]), | 
			
		||||
 | 
				  ); | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const [applyingDrawerVisible, setApplyingDrawerVisible] = useState(false); | 
			
		||||
 | 
				  const [certApplyingId, setCertApplyingId] = useState(0); | 
			
		||||
 | 
				  const [currentDLInfo, setCurrentDLInfo] = useState({}); | 
			
		||||
 | 
				  const { mutate: editCertUpdate } = useAtomValue(editCertificateAtom); | 
			
		||||
 | 
				  const [remakeTextInfo, setRemakeTextInfo] = useState(""); | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useEffect(() => { | 
			
		||||
 | 
				    window.setInterval(() => { | 
			
		||||
 | 
				      // console.log("update drawer");
 | 
			
		||||
 | 
				      setUpdataList((prev) => prev + 1); | 
			
		||||
 | 
				    }, 5000); | 
			
		||||
 | 
				  }, []); | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const showDLDrawer = (record) => { | 
			
		||||
 | 
				    setCurrentDLInfo(record); | 
			
		||||
 | 
				    setDLDrawerVisible(true); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const closeDLDrawer = () => { | 
			
		||||
 | 
				    setDLDrawerVisible(false); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const showEditDrawer = (record) => { | 
			
		||||
 | 
				    setCurrentDLInfo(record); | 
			
		||||
 | 
				    setEditDrawerVisible(true); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				  const enterEditDrawer = (record) => { | 
			
		||||
 | 
				    const data: Req_UpdateCert = { | 
			
		||||
 | 
				      id: record.id, | 
			
		||||
 | 
				      remark: remakeTextInfo, | 
			
		||||
 | 
				    }; | 
			
		||||
 | 
				    editCertUpdate(data); | 
			
		||||
 | 
				    setEditDrawerVisible(false); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				  const closeEditDrawer = () => { | 
			
		||||
 | 
				    setEditDrawerVisible(false); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				  const openDetails = () => { | 
			
		||||
 | 
				    setDetailsVisible(true); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const closeDetails = () => { | 
			
		||||
 | 
				    setDetailsVisible(false); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const showApplyingDrawer = () => { | 
			
		||||
 | 
				    setApplyingDrawerVisible(true); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const onCloseApplyingDrawer = () => { | 
			
		||||
 | 
				    setApplyingDrawerVisible(false); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const applyCertClick = () => { | 
			
		||||
 | 
				    navigate({ to: `/client/cert/apply` }); // 确保路径正确
 | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const clickStateTag = (id: number) => { | 
			
		||||
 | 
				    setCertApplyingId(id); | 
			
		||||
 | 
				    showApplyingDrawer(); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const handleRemarkChange = (e) => { | 
			
		||||
 | 
				    setRemakeTextInfo(e.target.value); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const clickDownLoad = (record: any) => { | 
			
		||||
 | 
				    const cert_format: string = record.certificateFormat.toString().split("/")[0].replace(/\s+/g, ""); | 
			
		||||
 | 
				    // const data: Req_DownloadCert = {
 | 
			
		||||
 | 
				    //   id: (currentDLInfo as any)?.id,
 | 
			
		||||
 | 
				    //   cert_format: cert_format,
 | 
			
		||||
 | 
				    // };
 | 
			
		||||
 | 
				    // websitesServ.cert.downloadCertificate(data);
 | 
			
		||||
 | 
				    // eslint-disable-next-line react-hooks/rules-of-hooks
 | 
			
		||||
 | 
				    const token = getToken(); | 
			
		||||
 | 
				    const certUrl = "http://127.0.0.1:8000/api/v1/cert"; | 
			
		||||
 | 
				    const url = `${certUrl}/apply/download?id=${(currentDLInfo as any)?.id}&cert_format=${cert_format}&token=${token}`; | 
			
		||||
 | 
				    window.open(url, "_blank"); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const clickDelete = (id: number) => { | 
			
		||||
 | 
				    confirm({ | 
			
		||||
 | 
				      title: "提示", | 
			
		||||
 | 
				      content: "是否要删除证书?", | 
			
		||||
 | 
				      okText: "确定", | 
			
		||||
 | 
				      okType: "danger", | 
			
		||||
 | 
				      cancelText: "取消", | 
			
		||||
 | 
				      onOk() { | 
			
		||||
 | 
				        const data: Req_DeletesCert = { ids: [id] }; | 
			
		||||
 | 
				        websitesServ.cert.deletesCertificate(data); | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      onCancel() { | 
			
		||||
 | 
				        console.log("取消删除证书"); | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				    }); | 
			
		||||
 | 
				  }; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const drawerColumns = [ | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "服务器类型", | 
			
		||||
 | 
				      dataIndex: "serverType", | 
			
		||||
 | 
				      key: "serverType", | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "证书格式", | 
			
		||||
 | 
				      dataIndex: "certificateFormat", | 
			
		||||
 | 
				      key: "certificateFormat", | 
			
		||||
 | 
				      render: (text: any) => ( | 
			
		||||
 | 
				        <Space> | 
			
		||||
 | 
				          {text.split("/").map((format: string) => ( | 
			
		||||
 | 
				            <Button key={format} size="small" icon={<CopyOutlined />} iconPosition={"end"}> | 
			
		||||
 | 
				              {format.trim()} | 
			
		||||
 | 
				            </Button> | 
			
		||||
 | 
				          ))} | 
			
		||||
 | 
				        </Space> | 
			
		||||
 | 
				      ), | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "操作", | 
			
		||||
 | 
				      key: "canDownLoad", | 
			
		||||
 | 
				      render: (text: string, record: any) => ( | 
			
		||||
 | 
				        <Space size="middle"> | 
			
		||||
 | 
				          <Button type="link">帮助</Button> | 
			
		||||
 | 
				          <Button type="link" disabled={!record.canDownLoad} onClick={() => clickDownLoad(record)}> | 
			
		||||
 | 
				            下载 | 
			
		||||
 | 
				          </Button> | 
			
		||||
 | 
				        </Space> | 
			
		||||
 | 
				      ), | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				  ]; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const drawerDataSource = [ | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "1", | 
			
		||||
 | 
				      serverType: "Nginx", | 
			
		||||
 | 
				      certificateFormat: "pem / key", | 
			
		||||
 | 
				      canDownLoad: true, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "2", | 
			
		||||
 | 
				      serverType: "Tomcat", | 
			
		||||
 | 
				      certificateFormat: "pfx", | 
			
		||||
 | 
				      canDownLoad: false, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "3", | 
			
		||||
 | 
				      serverType: "Apache", | 
			
		||||
 | 
				      certificateFormat: "crt / key", | 
			
		||||
 | 
				      canDownLoad: true, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "4", | 
			
		||||
 | 
				      serverType: "IIS", | 
			
		||||
 | 
				      certificateFormat: "pfx", | 
			
		||||
 | 
				      canDownLoad: false, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "5", | 
			
		||||
 | 
				      serverType: "JKS", | 
			
		||||
 | 
				      certificateFormat: "jks", | 
			
		||||
 | 
				      canDownLoad: true, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "6", | 
			
		||||
 | 
				      serverType: "宝塔", | 
			
		||||
 | 
				      certificateFormat: "pem / key", | 
			
		||||
 | 
				      canDownLoad: true, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "7", | 
			
		||||
 | 
				      serverType: "其他", | 
			
		||||
 | 
				      certificateFormat: "pem / key", | 
			
		||||
 | 
				      canDownLoad: true, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				  ]; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const columns = [ | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "域名", | 
			
		||||
 | 
				      dataIndex: "primary_domain", | 
			
		||||
 | 
				      key: "primary_domain", | 
			
		||||
 | 
				      render: (text: string) => ( | 
			
		||||
 | 
				        <Tooltip title={`点击查看详情`} color={"orange"}> | 
			
		||||
 | 
				          <Button type="link" onClick={openDetails}> | 
			
		||||
 | 
				            {text} | 
			
		||||
 | 
				          </Button> | 
			
		||||
 | 
				        </Tooltip> | 
			
		||||
 | 
				      ), | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "域名验证", | 
			
		||||
 | 
				      dataIndex: "domainVerification", | 
			
		||||
 | 
				      key: "domainVerification", | 
			
		||||
 | 
				      render: () => <CheckCircleFilled style={{ color: "green" }} />, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "证书品牌", | 
			
		||||
 | 
				      dataIndex: "acme_type", | 
			
		||||
 | 
				      key: "acme_type", | 
			
		||||
 | 
				      render: (text) => ( | 
			
		||||
 | 
				        <Tooltip title={`证书名字`}> | 
			
		||||
 | 
				          <span>{text}</span> | 
			
		||||
 | 
				          {/*<img src="https://placehold.co/20x20?text=Logo" alt="Let's Encrypt Logo" />{" "}*/} | 
			
		||||
 | 
				        </Tooltip> | 
			
		||||
 | 
				      ), | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      /* | 
			
		||||
 | 
				      const ( | 
			
		||||
 | 
				      SSLSuccess    CertStatus = "success"    // 成功
 | 
			
		||||
 | 
				      SSLInit       CertStatus = "init"    // 初始化
 | 
			
		||||
 | 
				      SSLError      CertStatus = "error"   // 错误
 | 
			
		||||
 | 
				      SSLReady      CertStatus = "ready"      // 准备中
 | 
			
		||||
 | 
				      SSLApply      CertStatus = "applying"   // 申请中
 | 
			
		||||
 | 
				      SSLApplyError CertStatus = "applyError" // 申请失败
 | 
			
		||||
 | 
				      ) | 
			
		||||
 | 
				       */ | 
			
		||||
 | 
				      title: "有效期(天)", | 
			
		||||
 | 
				      dataIndex: "validityDays", | 
			
		||||
 | 
				      key: "validityDays", | 
			
		||||
 | 
				      render: (text, record: { created_at: string; updated_at: string; expiredate: string }) => { | 
			
		||||
 | 
				        if (record) { | 
			
		||||
 | 
				          // const twoColors: ProgressProps["strokeColor"] = {
 | 
			
		||||
 | 
				          //   "0%": "#00ff59",
 | 
			
		||||
 | 
				          //   "100%": "#ff0000",
 | 
			
		||||
 | 
				          // };
 | 
			
		||||
 | 
				          record.created_at = !record?.created_at ? format(new Date(), "yyyy-MM-dd HH:mm:ss") : record.created_at; | 
			
		||||
 | 
				          record.expiredate = !record?.expiredate ? format(new Date(), "yyyy-MM-dd HH:mm:ss") : record.expiredate; | 
			
		||||
 | 
				          const createdDate = new Date(record.created_at.replace(/-/g, "/")); | 
			
		||||
 | 
				          const expireDateObj = new Date(record.expiredate.replace(/-/g, "/")); | 
			
		||||
 | 
				          const currentDate = new Date(); | 
			
		||||
 | 
				          const totalValidTime = expireDateObj.getTime() - createdDate.getTime(); | 
			
		||||
 | 
				          const remainingTime = expireDateObj.getTime() - currentDate.getTime(); | 
			
		||||
 | 
				          const percentage = (remainingTime / totalValidTime) * 100; | 
			
		||||
 | 
				          //const percentage = 60;
 | 
			
		||||
 | 
				          let cColor = "#000000"; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				          if (percentage < 50) { | 
			
		||||
 | 
				            cColor = "#00ff59"; | 
			
		||||
 | 
				          } else if (percentage >= 50 && percentage <= 75) { | 
			
		||||
 | 
				            cColor = "#00641a"; | 
			
		||||
 | 
				          } else if (percentage >= 75 && percentage <= 90) { | 
			
		||||
 | 
				            cColor = "#ffdd00"; | 
			
		||||
 | 
				          } else { | 
			
		||||
 | 
				            cColor = "#ff0000"; | 
			
		||||
 | 
				          } | 
			
		||||
 | 
				          return ( | 
			
		||||
 | 
				            <Space> | 
			
		||||
 | 
				              <Tooltip | 
			
		||||
 | 
				                title={ | 
			
		||||
 | 
				                  <div> | 
			
		||||
 | 
				                    创建时间:{record.created_at} | 
			
		||||
 | 
				                    <br /> | 
			
		||||
 | 
				                    到期时间:{record.expiredate} | 
			
		||||
 | 
				                  </div> | 
			
		||||
 | 
				                } | 
			
		||||
 | 
				              > | 
			
		||||
 | 
				                <Progress | 
			
		||||
 | 
				                  style={{ width: 200 }} | 
			
		||||
 | 
				                  percent={percentage} | 
			
		||||
 | 
				                  strokeColor={cColor} | 
			
		||||
 | 
				                  size="small" | 
			
		||||
 | 
				                  showInfo={false} | 
			
		||||
 | 
				                /> | 
			
		||||
 | 
				                <span>{Math.round(percentage)}/100</span> | 
			
		||||
 | 
				              </Tooltip> | 
			
		||||
 | 
				            </Space> | 
			
		||||
 | 
				          ); | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "加密方式", | 
			
		||||
 | 
				      dataIndex: "keytype", | 
			
		||||
 | 
				      key: "keytype", | 
			
		||||
 | 
				      render: (encryptionMethod: string) => <Tag color="green">{encryptionMethod}</Tag>, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "状态", | 
			
		||||
 | 
				      dataIndex: "cert_status", | 
			
		||||
 | 
				      key: "cert_status", | 
			
		||||
 | 
				      render: (state, record: any) => { | 
			
		||||
 | 
				        const { id } = record; | 
			
		||||
 | 
				        if (state === "applying") { | 
			
		||||
 | 
				          return ( | 
			
		||||
 | 
				            <Tag color="blue" onClick={() => clickStateTag(id)} style={{ cursor: "pointer" }}> | 
			
		||||
 | 
				              {state} | 
			
		||||
 | 
				            </Tag> | 
			
		||||
 | 
				          ); | 
			
		||||
 | 
				        } else { | 
			
		||||
 | 
				          return ( | 
			
		||||
 | 
				            <Tag color="green" onClick={() => clickStateTag(id)} style={{ cursor: "pointer" }}> | 
			
		||||
 | 
				              {state} | 
			
		||||
 | 
				            </Tag> | 
			
		||||
 | 
				          ); | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "备注", | 
			
		||||
 | 
				      dataIndex: "remark", | 
			
		||||
 | 
				      key: "remark", | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "操作", | 
			
		||||
 | 
				      key: "action", | 
			
		||||
 | 
				      render: (record: any) => ( | 
			
		||||
 | 
				        <Space size="middle"> | 
			
		||||
 | 
				          <a onClick={() => showEditDrawer(record)}>编辑</a> | 
			
		||||
 | 
				          <a onClick={() => showDLDrawer(record)}>下载</a> | 
			
		||||
 | 
				          <a onClick={() => clickDelete(record.id)}>删除</a> | 
			
		||||
 | 
				        </Space> | 
			
		||||
 | 
				      ), | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				  ]; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const detailsColumns = [ | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "状态", | 
			
		||||
 | 
				      dataIndex: "status", | 
			
		||||
 | 
				      key: "status", | 
			
		||||
 | 
				      render: (text: string) => <span style={{ color: "#52c41a" }}>{text}</span>, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "域名", | 
			
		||||
 | 
				      dataIndex: "domain", | 
			
		||||
 | 
				      key: "domain", | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "服务商", | 
			
		||||
 | 
				      dataIndex: "provider", | 
			
		||||
 | 
				      key: "provider", | 
			
		||||
 | 
				      render: (text: string) => <span style={{ color: "#1890ff" }}>{text}</span>, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "主机记录", | 
			
		||||
 | 
				      dataIndex: "hostRecord", | 
			
		||||
 | 
				      key: "hostRecord", | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "记录类型", | 
			
		||||
 | 
				      dataIndex: "recordType", | 
			
		||||
 | 
				      key: "recordType", | 
			
		||||
 | 
				      render: (text: string) => <span style={{ color: "red" }}>{text}</span>, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "记录值", | 
			
		||||
 | 
				      dataIndex: "recordValue", | 
			
		||||
 | 
				      key: "recordValue", | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      title: "操作", | 
			
		||||
 | 
				      dataIndex: "action", | 
			
		||||
 | 
				      key: "action", | 
			
		||||
 | 
				      render: (text: string) => <Link href="#">{text}</Link>, | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				  ]; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const detailsData = [ | 
			
		||||
 | 
				    { | 
			
		||||
 | 
				      key: "1", | 
			
		||||
 | 
				      status: "通过", | 
			
		||||
 | 
				      domain: "*.bidiy2.com", | 
			
		||||
 | 
				      provider: "Cloudflare", | 
			
		||||
 | 
				      hostRecord: "_acme-challenge", | 
			
		||||
 | 
				      recordType: "CNAME", | 
			
		||||
 | 
				      recordValue: "8cb0d498cecabd92.httpsok.com", | 
			
		||||
 | 
				      action: "检测命令", | 
			
		||||
 | 
				    }, | 
			
		||||
 | 
				  ]; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  return ( | 
			
		||||
 | 
				    <div> | 
			
		||||
 | 
				      <ListPageLayout title={t(`${i18nPrefix}.apply.title`, "证书申请")}> | 
			
		||||
 | 
				        <Space style={{ marginBottom: 16 }}> | 
			
		||||
 | 
				          <Input addonBefore={<SearchOutlined />} placeholder="域名 | 备注" style={{ width: 200 }} /> | 
			
		||||
 | 
				          <Button>搜索</Button> | 
			
		||||
 | 
				          <Button type="primary" onClick={applyCertClick}> | 
			
		||||
 | 
				            免费申请证书 | 
			
		||||
 | 
				          </Button> | 
			
		||||
 | 
				        </Space> | 
			
		||||
 | 
				        <Table columns={columns} dataSource={(certListData as any)?.rows} pagination={false} /> | 
			
		||||
 | 
				      </ListPageLayout> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      <CertApplyingDrawer | 
			
		||||
 | 
				        id={certApplyingId} | 
			
		||||
 | 
				        onCloseApplyingDrawer={onCloseApplyingDrawer} | 
			
		||||
 | 
				        applyingDrawerVisible={applyingDrawerVisible} | 
			
		||||
 | 
				      /> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      <Drawer title="下载证书" placement="right" onClose={closeDLDrawer} visible={dlDrawerVisible} width={600}> | 
			
		||||
 | 
				        <Text>请根据您的服务器类型选择证书下载:</Text> | 
			
		||||
 | 
				        <Table dataSource={drawerDataSource} columns={drawerColumns} pagination={false} style={{ marginTop: 16 }} /> | 
			
		||||
 | 
				        <div style={{ backgroundColor: "#f6ffed", padding: 16, marginTop: 16 }}> | 
			
		||||
 | 
				          <Text>加入SSL证书交流群</Text> | 
			
		||||
 | 
				          <br /> | 
			
		||||
 | 
				          <Text>产品咨询 问题反馈</Text> | 
			
		||||
 | 
				          <br /> | 
			
		||||
 | 
				          <Link href="https://httpsok">请备注 httpsok</Link> | 
			
		||||
 | 
				        </div> | 
			
		||||
 | 
				      </Drawer> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      <Drawer | 
			
		||||
 | 
				        title="编辑" | 
			
		||||
 | 
				        placement="right" | 
			
		||||
 | 
				        closable={false} | 
			
		||||
 | 
				        onClose={closeEditDrawer} | 
			
		||||
 | 
				        visible={editDrawerVisible} | 
			
		||||
 | 
				        style={{ textAlign: "center" }} | 
			
		||||
 | 
				        bodyStyle={{ display: "flex", justifyContent: "center", alignItems: "center" }} | 
			
		||||
 | 
				      > | 
			
		||||
 | 
				        <Form layout="vertical" style={{ maxWidth: "400px", width: "100%" }}> | 
			
		||||
 | 
				          <Form.Item label="域名"> | 
			
		||||
 | 
				            <span>{(currentDLInfo as any)?.primary_domain}</span> | 
			
		||||
 | 
				          </Form.Item> | 
			
		||||
 | 
				          <Form.Item label="证书品牌"> | 
			
		||||
 | 
				            <span>{(currentDLInfo as any)?.acme_type}</span> | 
			
		||||
 | 
				          </Form.Item> | 
			
		||||
 | 
				          <Form.Item label="失效时间"> | 
			
		||||
 | 
				            <span>{(currentDLInfo as any)?.expiredate}</span> | 
			
		||||
 | 
				          </Form.Item> | 
			
		||||
 | 
				          <Form.Item label="创建时间"> | 
			
		||||
 | 
				            <span>{(currentDLInfo as any)?.created_at}</span> | 
			
		||||
 | 
				          </Form.Item> | 
			
		||||
 | 
				          <Form.Item label="备注"> | 
			
		||||
 | 
				            <TextArea | 
			
		||||
 | 
				              rows={4} | 
			
		||||
 | 
				              maxLength={100} | 
			
		||||
 | 
				              defaultValue={(currentDLInfo as any)?.remark} | 
			
		||||
 | 
				              onChange={handleRemarkChange} | 
			
		||||
 | 
				            /> | 
			
		||||
 | 
				          </Form.Item> | 
			
		||||
 | 
				          <Form.Item> | 
			
		||||
 | 
				            <Button onClick={closeEditDrawer} style={{ marginRight: 8 }}> | 
			
		||||
 | 
				              取消 | 
			
		||||
 | 
				            </Button> | 
			
		||||
 | 
				            <Button type="primary" onClick={() => enterEditDrawer(currentDLInfo)}> | 
			
		||||
 | 
				              确定 | 
			
		||||
 | 
				            </Button> | 
			
		||||
 | 
				          </Form.Item> | 
			
		||||
 | 
				        </Form> | 
			
		||||
 | 
				      </Drawer> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      <Modal | 
			
		||||
 | 
				        title="域名验证" | 
			
		||||
 | 
				        visible={detailsVisible} | 
			
		||||
 | 
				        onOk={openDetails} | 
			
		||||
 | 
				        onCancel={closeDetails} | 
			
		||||
 | 
				        width={1200} // 设置宽度为800px
 | 
			
		||||
 | 
				        footer={[ | 
			
		||||
 | 
				          <Button key="ok" type="primary" onClick={openDetails}> | 
			
		||||
 | 
				            确定 | 
			
		||||
 | 
				          </Button>, | 
			
		||||
 | 
				        ]} | 
			
		||||
 | 
				      > | 
			
		||||
 | 
				        <Text> | 
			
		||||
 | 
				          请您添加以下DNS解析记录 <Link href="#">参考文档</Link> | 
			
		||||
 | 
				        </Text> | 
			
		||||
 | 
				        <br /> | 
			
		||||
 | 
				        <Text>1. 只需要添加一次即可,添加后请勿删除记录。</Text> | 
			
		||||
 | 
				        <br /> | 
			
		||||
 | 
				        <Text> | 
			
		||||
 | 
				          2. 需等待1-2分钟。(<Text type="danger">如果长时间没通过,请联系客服解决</Text>) | 
			
		||||
 | 
				        </Text> | 
			
		||||
 | 
				        <Table columns={detailsColumns} dataSource={detailsData} pagination={false} /> | 
			
		||||
 | 
				      </Modal> | 
			
		||||
 | 
				    </div> | 
			
		||||
 | 
				  ); | 
			
		||||
 | 
				}; | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export default CertificateManagement; | 
			
		||||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue