diff --git a/src/components/DeleteModal/reset.less b/src/components/DeleteModal/reset.less index a2c1669..d5a560d 100644 --- a/src/components/DeleteModal/reset.less +++ b/src/components/DeleteModal/reset.less @@ -1,5 +1,9 @@ .queryLogo { svg { fill: #f4ce5d; + + path { + fill: #faad14; + } } } diff --git a/src/components/LabelWithHelp/index.tsx b/src/components/LabelWithHelp/index.tsx new file mode 100644 index 0000000..a3a86fb --- /dev/null +++ b/src/components/LabelWithHelp/index.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import CommonIcon from '../CommonIcon'; +import { ReactComponent as IconHelp } from '../../images/icon/icon_label_query.svg'; +import styled from 'styled-components'; + +const LabelWrap = styled.div` + display: flex; + align-items: center; + position: relative; + + .help { + position: absolute; + display: flex; + align-items: center; + top: 50%; + left: 100%; + transform: translate(12px, -50%); + + svg { + &:hover { + path { + fill: #36435c; + } + } + } + } +`; + +interface IProp { + label: string; + tip: string; +} + +const LabelWithHelp = ({ label, tip }: IProp) => { + return ( + +
{label}
+
+ +
+
+ ); +}; + +export default LabelWithHelp; diff --git a/src/components/MainHeader/index.tsx b/src/components/MainHeader/index.tsx index 09dd854..233a8de 100644 --- a/src/components/MainHeader/index.tsx +++ b/src/components/MainHeader/index.tsx @@ -52,6 +52,8 @@ import { ReactComponent as IconUser } from '../../images/icon/icon_user.svg'; import { ReactComponent as IconTip } from '../../images/icon/icon_label_tips.svg'; import { ReactComponent as IconClose } from '../../images/icon/icon_close.svg'; import { ReactComponent as IconLink } from '../../images/icon/icon_external_link.svg'; +import { ReactComponent as IconSetNormal } from '../../images/icon/icon_set_normal.svg'; +import { ReactComponent as IconSetActive } from '../../images/icon/icon_set_elected.svg'; import ImageVersionInfo from '../../images/icon_logoWords.svg'; import AddCluster from '../../components/AddCluster'; @@ -285,6 +287,15 @@ function MainHeader() { style={{ fontSize: '24px' }} > +
history.push('/dashboard/setting')}> + +
\ No newline at end of file diff --git a/src/images/bg_left_unselect.svg b/src/images/bg_left_unselect.svg new file mode 100644 index 0000000..0bcbabf --- /dev/null +++ b/src/images/bg_left_unselect.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/bg_right_select.svg b/src/images/bg_right_select.svg new file mode 100644 index 0000000..5b37e21 --- /dev/null +++ b/src/images/bg_right_select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/bg_right_unselect.svg b/src/images/bg_right_unselect.svg new file mode 100644 index 0000000..6602696 --- /dev/null +++ b/src/images/bg_right_unselect.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/icon/icon_arrow_right.svg b/src/images/icon/icon_arrow_right.svg new file mode 100644 index 0000000..807d95f --- /dev/null +++ b/src/images/icon/icon_arrow_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/icon/icon_ldap.svg b/src/images/icon/icon_ldap.svg new file mode 100644 index 0000000..0435511 --- /dev/null +++ b/src/images/icon/icon_ldap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/icon/icon_set_elected.svg b/src/images/icon/icon_set_elected.svg new file mode 100644 index 0000000..adb545e --- /dev/null +++ b/src/images/icon/icon_set_elected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/icon/icon_set_normal.svg b/src/images/icon/icon_set_normal.svg new file mode 100644 index 0000000..29ca6d8 --- /dev/null +++ b/src/images/icon/icon_set_normal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/icon_set_elected.svg b/src/images/icon_set_elected.svg new file mode 100644 index 0000000..adb545e --- /dev/null +++ b/src/images/icon_set_elected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/icon_set_normal.svg b/src/images/icon_set_normal.svg new file mode 100644 index 0000000..29ca6d8 --- /dev/null +++ b/src/images/icon_set_normal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/index.css b/src/index.css index 7b6b877..3478695 100644 --- a/src/index.css +++ b/src/index.css @@ -331,3 +331,7 @@ input:-webkit-autofill:active { .ant-tooltip-arrow-content { background-color: #1a2433; } + +.ant-form-item-required { + font-weight: normal; +} diff --git a/src/pages/Application/index.tsx b/src/pages/Application/index.tsx index b95fef6..4e0c581 100644 --- a/src/pages/Application/index.tsx +++ b/src/pages/Application/index.tsx @@ -23,7 +23,6 @@ import DeleteModal from '../../components/DeleteModal'; import LabelSelect from '../../components/LabelSelect'; // import { applictionOptions } from './const'; import { useHistory } from 'react-router-dom'; -import { UserType } from '../User/const'; import CommonIcon from '../../components/CommonIcon'; import { ReactComponent as IconNormalEdit } from '../../images/icon/icon_btn_normal_edit.svg'; import { ReactComponent as IconSelectedEdit } from '../../images/icon/icon_btn_elected_edit.svg'; @@ -42,7 +41,6 @@ import { ReactComponent as IconAdd } from '../../images/icon/icon_add.svg'; function Application() { const [data, setData] = useState([]); const [copyData, setCopyData] = useState([]); - const [userList, setUserList] = useState([]); const [tableLoading, setTableLoading] = useState(false); const [openDialog, setOpenDialog] = useState(false); const [filterValue, setFilterValue] = useState({ name: '', type: 'all' }); @@ -60,12 +58,7 @@ function Application() { const { user } = useContext(UserContext); const history = useHistory(); const { t } = useTranslation(); - const getUser = async () => { - const result = await HTTP.get('users'); - if (result.code === 0) { - setUserList(result.data || []); - } - }; + const getApplication = async () => { setTableLoading(true); const result = await HTTP.get('application', { @@ -81,7 +74,6 @@ function Application() { }; useEffect(() => { getApplication(); - getUser(); }, []); const showTotal = (total: number) => { return `共${total}条`; @@ -230,16 +222,8 @@ function Application() { }, { title: t('resources.application.fields.user'), + dataIndex: 'user_name', key: '5', - // eslint-disable-next-line react/display-name - render: (...args: any) => { - const record = args[1]; - const user_id = record?.user_id; - const item: UserType = userList.find((el: UserType) => el.id === user_id) || { - name: '', - }; - return {item?.name}; - }, }, { title: t('common.operation'), diff --git a/src/pages/Clusters/components/ListItem.tsx b/src/pages/Clusters/components/ListItem.tsx index 465cf31..ee2e1ea 100644 --- a/src/pages/Clusters/components/ListItem.tsx +++ b/src/pages/Clusters/components/ListItem.tsx @@ -496,7 +496,7 @@ const ListItem: FC = ({ data, onSubmit }: IProps) => { component={IconProfile} style={{ fontSize: 24, marginRight: 6 }} /> - {data.userName} + {data.user_name} diff --git a/src/pages/Clusters/index.tsx b/src/pages/Clusters/index.tsx index daf51eb..b7786a2 100644 --- a/src/pages/Clusters/index.tsx +++ b/src/pages/Clusters/index.tsx @@ -6,7 +6,6 @@ import { ContentTitle, ClusterCount, FlexContainer, LoadingBox } from './style-c import { Button, Spin } from 'antd'; import i18n from '../../i18n/i18n'; import AddCluster from '../../components/AddCluster'; -import { queryAllUser } from '../../services'; import { useTranslation } from 'react-i18next'; import NotData from '../../components/NotData'; import Icon from '@ant-design/icons'; @@ -23,17 +22,11 @@ const Clusters: FC<{}> = () => { async function queryClusters() { setClusterLoading(true); - const nameMap = await queryAllUser(); + const response = await HTTP.get('cluster'); setClusterLoading(false); if (response.code === 0) { - const tmpList = response.data.map((item: any) => { - return { - ...item, - userName: nameMap.get(item.user_id), - }; - }); - setClusterList(tmpList); + setClusterList(response.data); } } diff --git a/src/pages/DevSpace/components/DevspaceForm.tsx b/src/pages/DevSpace/components/DevspaceForm.tsx index 7754032..d6f66a3 100644 --- a/src/pages/DevSpace/components/DevspaceForm.tsx +++ b/src/pages/DevSpace/components/DevspaceForm.tsx @@ -10,7 +10,7 @@ import { ReactComponent as IconAdmin } from '../../../images/icon/icon_admin.svg import { ReactComponent as IconResource } from '../../../images/icon/icon_resource.svg'; import { ReactComponent as IconBaseSpace } from '../../../images/icon/icon_switch_baseSpace.svg'; -import { queryAllCluster, queryAllUser } from '../../../services'; +import { queryAllCluster, queryUserList } from '../../../services'; const FormFlexBox = styled(FlexBox)` flex: 1; @@ -112,6 +112,7 @@ const DevspaceForm = ({ const [isAdmin, setIsAdmin] = useState(false); const [userList, setUserList] = useState([]); + const [filterUserList, setFilterUserList] = useState([]); const [clusterList, setClusterList] = useState([]); // 校验相关 @@ -183,15 +184,9 @@ const DevspaceForm = ({ } async function getUsers() { - const userMap = await queryAllUser(); - const tmpList = Array.from(userMap).map((item) => { - return { - value: item[0], - text: item[1], - label: item[1], - }; - }); - setUserList(tmpList); + const list = await queryUserList(); + setUserList(list); + setFilterUserList(list.slice(0, 10)); } const handleSubmit = async (values: any) => { @@ -290,6 +285,11 @@ const DevspaceForm = ({ } }; + const handleSearchUser = (value: string) => { + const list = userList.filter((item: any) => item.label.indexOf(value) > -1); + setFilterUserList(list.slice(0, 10)); + }; + return ( <>
diff --git a/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx b/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx index 34d70d5..210ea31 100644 --- a/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx +++ b/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx @@ -20,9 +20,7 @@ import { ReactComponent as WayLine } from '../../../../images/mesh-icon/way1.svg import { ReactComponent as BlueArrow } from '../../../../images/mesh-icon/arrow.svg'; import { ReactComponent as IconTracingHeader } from '../../../../images/icon/icon_normal_tracingHeaders.svg'; import { ReactComponent as IconActiveTracingHeader } from '../../../../images/icon/icon_active_tracingHeaders.svg'; - import CommonIcon from '../../../../components/CommonIcon'; - import { ReactComponent as IconPath } from '../../../../images/mesh-icon/icon_path.svg'; import { windowAnimationStartHandle, diff --git a/src/pages/EnvList/components/KubeConfig.tsx b/src/pages/EnvList/components/KubeConfig.tsx index 9478912..fb6a7ea 100644 --- a/src/pages/EnvList/components/KubeConfig.tsx +++ b/src/pages/EnvList/components/KubeConfig.tsx @@ -69,7 +69,9 @@ const KubeConfig = (props: PropParam) => { const handleDownload = () => { // download - const blob = new Blob([kubeConfig]); + const blob = new Blob([kubeConfig], { + type: 'application/octet-stream', + }); const aLink = document.createElement('a'); aLink.style.display = 'none'; aLink.setAttribute('download', 'config'); diff --git a/src/pages/EnvList/index.tsx b/src/pages/EnvList/index.tsx index 0f54ca7..0bfdf74 100644 --- a/src/pages/EnvList/index.tsx +++ b/src/pages/EnvList/index.tsx @@ -252,12 +252,12 @@ const EnvList = () => { return record?.cooper_user?.length > 0 || record?.viewer_user?.length > 0 ? ( }> - {record.user_name} + {record?.owner?.name} ) : ( - {record.user_name} + {record?.owner?.name} ); }, }, @@ -609,13 +609,6 @@ const EnvList = () => { /> - {/* - - */} {!id && ( <> {!!user.is_admin && ( diff --git a/src/pages/Home.tsx/routes.tsx b/src/pages/Home.tsx/routes.tsx index abe359a..c4ed855 100644 --- a/src/pages/Home.tsx/routes.tsx +++ b/src/pages/Home.tsx/routes.tsx @@ -7,6 +7,7 @@ import EnvList from '../EnvList'; import DevSpace from '../DevSpace'; import DevSpaceOperation from '../DevSpace/components/DevspaceOperation'; import MeshSpace from '../DevSpace/components/MeshSpace'; +import Setting from '../Setting'; import ImportUser from '../User/Import'; import ImportDevSpace from '../DevSpace/import'; @@ -64,4 +65,8 @@ export const routes = [ path: '/dashboard/devspace/mesh-space', component: MeshSpace, }, + { + path: '/dashboard/setting', + component: Setting, + }, ]; diff --git a/src/pages/Setting/components/ConfigService.tsx b/src/pages/Setting/components/ConfigService.tsx new file mode 100644 index 0000000..9cf01b2 --- /dev/null +++ b/src/pages/Setting/components/ConfigService.tsx @@ -0,0 +1,380 @@ +import React, { useEffect, useState } from 'react'; + +import { ConfigServiceWrap } from '../styled-component'; +import { useTranslation } from 'react-i18next'; +import { Form, Input, Button, Checkbox } from 'antd'; +import { ConfigInfo } from '../../../types'; +import LabelWithHelp from '../../../components/LabelWithHelp'; +import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; +import { ReactComponent as IconArrowRight } from '../../../images/icon/icon_arrow_right.svg'; + +import HTTP from '../../../api/fetch'; + +interface IProp { + configData: ConfigInfo | null; + onClose: () => void; + handleSyncData: () => void; + closeAndSync: () => void; +} + +interface SearchData { + bind_dn: string; + email: string; + user_name: string; +} + +const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IProp) => { + const { t } = useTranslation(); + const [currentStep, setCurrentStep] = useState(configData?.enable ? 2 : 1); + const [folded, setFolded] = useState(true); + const [form] = Form.useForm(); + const [isLoading, setIsLoading] = useState(false); + // eslint-disable-next-line no-undef + const [step1Info, setStep1Info] = useState | null>(configData); + const [searchData, setSearchData] = useState(null); + const [advancedSearchData, setAdvancedSearchData] = useState(null); + + useEffect(() => { + if (configData) { + const security = []; + if (configData.tls) { + security.push('tls'); + } + if (configData?.md5) { + security.push('md5'); + } + form.setFieldsValue({ + server: configData?.server, + bind_dn: configData?.bind_dn, + password: configData?.password, + security, + base_dn: configData?.base_dn, + filter: configData?.filter, + email_attr: configData?.email_attr, + user_name_attr: configData?.user_name_attr, + admin_base_dn: configData?.admin_base_dn, + admin_filter: configData?.admin_filter, + }); + validateSearch(); + advanceSearch(); + } + }, []); + + const handleNextStep = async () => { + form.validateFields().then(async () => { + const { bind_dn, password, server, security } = form.getFieldsValue(); + setIsLoading(true); + setStep1Info({ + ...configData, + bind_dn, + password, + server, + tls: security.indexOf('tls') > -1 ? 1 : 0, + md5: security.indexOf('md5') ? 1 : 0, + }); + const response = await HTTP.put('ldap/bind', { + bind_dn, + password, + server, + }); + setIsLoading(false); + if (response.code === 0) { + setCurrentStep(2); + } + }); + }; + + const validateSearch = async () => { + const { + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + } = form.getFieldsValue(); + if (email_attr && base_dn) { + const resp = await HTTP.put('ldap/search', { + ...step1Info, + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + }); + if (resp.code === 0) { + setSearchData(resp.data); + } + return resp; + } + }; + + const advanceSearch = async () => { + const { + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + } = form.getFieldsValue(); + if (email_attr && admin_base_dn) { + const resp = await HTTP.put('ldap/search', { + ...step1Info, + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + }); + if (resp.code === 0) { + setAdvancedSearchData(resp.data); + } + return resp; + } + }; + + const saveConfig = async () => { + const { + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + } = form.getFieldsValue(); + return await HTTP.put('ldap/config/set', { + ...step1Info, + email_attr, + user_name_attr, + base_dn, + filter, + admin_base_dn, + admin_filter, + }); + }; + + const handleSubmit = async () => { + form.validateFields().then(async () => { + setIsLoading(true); + const saveInfo = await saveConfig(); + if (saveInfo.code === 0) { + await handleSyncData(); + setIsLoading(false); + closeAndSync(); + } else { + setIsLoading(false); + } + }); + }; + + return ( + +
{t('settings.configDesc')}
+
+
+
1
+
+
{t('settings.serverConfig')}
+
{t('settings.serverConfigTip')}
+
+
+
+
2
+
+
{t('settings.ruleConfig')}
+
{t('settings.ruleConfigTip')}
+
+
+
+
+ + {currentStep === 1 && ( + <> + + + + + + + + + + + + Enable TLS + Enable Digest MD5 Bind + + + + )} + + {currentStep === 2 && ( + <> + + } + name="email_attr" + rules={[ + { + required: true, + }, + ]} + required={true} + > + + + + } + name="user_name_attr" + > + + + +
+
{t('settings.memberRule')}
+ + + + + + +
+
{`Example: ${t('settings.exampleTip')}`}
+
DN: {searchData?.bind_dn ?? '--'}
+
Email: {searchData?.email ?? '--'}
+
Username: {searchData?.user_name ?? '--'}
+
+
+ +
+
setFolded((curr) => !curr)} + > + {folded ? : } + +
+ {!folded && ( +
+
{t('settings.adminRule')}
+ + + + + + +
+
{`Example: ${t('settings.exampleTip')}`}
+
DN: {advancedSearchData?.bind_dn ?? '- -'}
+
Email: {advancedSearchData?.email ?? '- -'}
+
+ Username: {advancedSearchData?.user_name ?? '- -'} +
+
+
+ )} +
+ + )} + +
+
+ + {currentStep === 1 && ( + + )} + {currentStep === 2 && ( + <> + + + + )} +
+
+ ); +}; + +export default ConfigService; diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx new file mode 100644 index 0000000..8962691 --- /dev/null +++ b/src/pages/Setting/components/ThirdAccount.tsx @@ -0,0 +1,136 @@ +import React, { useState } from 'react'; +import { ThirdAccountWrap, PopupWrap } from '../styled-component'; +import { Button, Popover } from 'antd'; +import { useTranslation } from 'react-i18next'; + +import { ReactComponent as IconLdap } from '../../../images/icon/icon_ldap.svg'; +import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg'; +import { ReactComponent as IconExplain } from '../../../images/icon/icon_label_explain.svg'; +import Icon from '@ant-design/icons'; +import DeleteModal from '../../../components/DeleteModal'; +import CommonIcon from '../../../components/CommonIcon'; + +interface ThirdAccountProp { + status: 'unallocated' | 'configured'; + showConfig: () => void; + handleSyncData: () => any; + handleDeleteConfig: () => void; + configData: any; +} + +const ThirdAccount = ({ + status, + showConfig, + handleSyncData, + handleDeleteConfig, + configData, +}: ThirdAccountProp) => { + const { t } = useTranslation(); + const [visible, setVisible] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [showDelete, setShowDelete] = useState(false); + const [showSyncTip, setShowSyncTip] = useState(Boolean(configData?.last_sync_err_msg)); + const [syncTip, setSyncTip] = useState(configData?.last_sync_err_msg ?? ''); + + const handleEditConfig = () => { + setVisible(false); + showConfig(); + }; + + const handleDelete = () => { + setVisible(false); + handleDeleteConfig(); + }; + + const handleClick = async () => { + setIsLoading(true); + const resp = await handleSyncData(); + if (resp?.data?.last_sync_err_msg) { + setSyncTip(resp?.data?.last_sync_err_msg ?? ''); + setShowSyncTip(true); + } + + setIsLoading(false); + }; + + const onClickDelete = () => { + setVisible(false); + setShowDelete(true); + }; + + const PopupContent = () => { + return ( + +
  • + {t('settings.modifyConfig')} +
  • +
  • + {t('settings.deleteConfig')} +
  • +
    + ); + }; + + return ( + +
    +
    + +
    +
    +
    + LDAP +
    {t(`settings.${status}`)}
    +
    +
    + {t('settings.thirdAccountDesc')} + {t('settings.helpDocs')} +
    +
    +
    +
    + {showSyncTip && ( + + )} + {status === 'unallocated' && ( + + )} + {status === 'configured' && ( + <> + + } + visible={visible} + onVisibleChange={(curr: boolean) => setVisible(curr)} + trigger="click" + > + + + + )} +
    + setShowDelete(false)} + onConfirm={handleDelete} + /> +
    + ); +}; + +export default ThirdAccount; diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx new file mode 100644 index 0000000..3456c08 --- /dev/null +++ b/src/pages/Setting/index.tsx @@ -0,0 +1,112 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { SettingWrap } from './styled-component'; +import ThirdAccount from './components/ThirdAccount'; +import ConfigService from './components/ConfigService'; +import { message, Modal } from 'antd'; +import HTTP from '../../api/fetch'; +import { ConfigInfo } from '../../types'; + +const CONFIG_MENU_LIST = ['thirdAccount']; + +const Settings = () => { + const { t } = useTranslation(); + const [currentIndex, setCurrentIndex] = useState(0); + const [showModal, setShowModal] = useState(false); + const [status, setStatus] = useState<'unallocated' | 'configured'>('unallocated'); + const [configData, setConfigData] = useState(null); + + const showConfig = () => { + setShowModal(true); + }; + + const handleSyncData = async () => { + const response = await HTTP.post('ldap/trigger', {}); + if (response.code === 0) { + message.success(t('common.message.success')); + } + + return response; + }; + + // delete config + const handleDeleteConfig = async () => { + const resp = await HTTP.put('ldap/config/disable', configData); + if (resp.code === 0) { + await getConfig(); + } + }; + + const getConfig = async () => { + const response = await HTTP.get('ldap/config'); + if (response.code === 0) { + const { data } = response; + if (data) { + setStatus(data?.enable ? 'configured' : 'unallocated'); + setConfigData(data); + } + } + }; + + const closeAndSync = async () => { + setShowModal(false); + await getConfig(); + }; + + useEffect(() => { + getConfig(); + }, []); + + return ( + <> + +
    +
    {t('settings.settingCenter')}
    +
      + {CONFIG_MENU_LIST.map((item, index) => { + return ( +
    • setCurrentIndex(index)} + key={index} + className={`menu-list-item${ + currentIndex === index ? ' active' : '' + }`} + > + {t(`settings.${item}`)} +
    • + ); + })} +
    +
    +
    +
    {t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
    + +
    +
    + setShowModal(false)} + footer={null} + bodyStyle={{ paddingTop: 0 }} + > + setShowModal(false)} + closeAndSync={closeAndSync} + handleSyncData={handleSyncData} + /> + + + ); +}; + +export default Settings; diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx new file mode 100644 index 0000000..e6247af --- /dev/null +++ b/src/pages/Setting/styled-component.tsx @@ -0,0 +1,326 @@ +import styled from 'styled-components'; +import step1active from '../../images/bg_left_select.svg'; +import step1normal from '../../images/bg_left_unselect.svg'; +import step2active from '../../images/bg_right_select.svg'; +import step2normal from '../../images/bg_right_unselect.svg'; + +export const SettingWrap = styled.div` + display: flex; + min-height: 604px; + background: #ffffff; + + .menu { + width: 200px; + border-right: 1px solid #dae1e8; + font-family: PingFangSC-Semibold; + font-size: 16px; + font-weight: 600; + color: #36435c; + + .title { + padding: 16px 0 16px 20px; + } + + &-list { + font-size: 14px; + + &-item { + height: 40px; + padding: 0 20px; + display: flex; + align-items: center; + cursor: pointer; + + &.active, + &:hover { + color: #0066ff; + border-right: 2px solid #0066ff; + background: #f3f6fa; + } + } + } + } + + .content { + flex: 1; + padding: 0 20px; + + .title { + padding: 16px 0; + font-family: PingFangSC-Semibold; + font-size: 16px; + font-weight: 600; + color: #36435c; + } + } +`; + +interface IThirdAccount { + status: string; +} + +interface IConfigService { + step: number; +} + +export const ThirdAccountWrap = styled.div` + display: flex; + padding: 30px 20px; + border: 1px solid #dae1e8; + border-radius: 4px; + justify-content: space-between; + align-items: center; + + .left { + display: flex; + align-items: center; + + .logo { + width: 40px; + height: 40px; + } + + .content { + .sub-title { + color: #202d40; + display: flex; + align-items: center; + font-family: PingFangSC-Semibold; + font-weight: 600; + font-size: 16px; + + .status { + height: 18px; + padding: 0 4px; + display: flex; + align-items: center; + margin-left: 8px; + font-size: 12px; + color: #ffffff; + border-radius: 2px; + background: ${(props) => + props.status === 'unallocated' ? '#ffac52' : '#4ac285'}; + } + } + + .desc { + margin-top: 10px; + font-size: 12px; + color: #79879c; + } + } + } + + .btn-box { + display: flex; + align-items: center; + + .popup-btn { + display: flex; + align-items: center; + } + + svg { + &:hover { + path { + fill: #faad14; + } + } + } + } +`; + +export const ConfigServiceWrap = styled.div` + display: block; + overflow: hidden; + + .desc { + font-family: PingFangSC-Regular; + font-size: 14px; + font-weight: normal; + color: #79879c; + } + + .progress { + height: 68px; + margin-top: 20px; + display: flex; + align-items: center; + + .step { + height: 68px; + width: 50%; + padding: 14px; + flex-basis: 50%; + display: flex; + position: relative; + background-size: cover; + + &::after { + content: ''; + width: 0; + height: 0; + display: block; + position: absolute; + top: 100%; + left: 50%; + border: 10px solid transparent; + border-bottom: 10px solid #f9fbfd; + transform: translate(-10px, -10px); + } + + &:nth-child(1) { + background: url(${(props) => (props.step === 1 ? step1active : step1normal)}) + no-repeat; + position: absolute; + z-index: 10; + + &::after { + visibility: ${(props) => (props.step === 1 ? 'visible' : 'hidden')}; + } + } + + &:nth-child(2) { + width: 314px; + position: absolute; + padding-left: 40px; + padding-right: 0; + left: 50%; + background: url(${(props) => (props.step === 2 ? step2active : step2normal)}) + no-repeat; + + &::after { + visibility: ${(props) => (props.step === 2 ? 'visible' : 'hidden')}; + } + } + + .icon { + height: 24px; + width: 24px; + display: flex; + justify-content: center; + align-items: center; + background: rgb(239, 244, 249); + border: 2px solid rgb(255, 255, 255); + box-shadow: 0px 3px 8px 0px rgba(54, 67, 92, 0.2); + border-radius: 50%; + font-family: PingFangSC-Semibold; + font-size: 12px; + font-weight: 600; + color: #b6c2cd; + + &.active { + background: rgb(0, 128, 255); + border: 2px solid rgb(255, 255, 255); + box-shadow: 0px 3px 8px 0px rgba(0, 128, 255, 0.3); + color: #ffffff; + } + } + + .content { + margin-left: 6px; + + .title { + color: rgb(32, 45, 64); + font-family: PingFangSC-Semibold; + font-size: 16px; + font-weight: 600; + } + + .desc { + color: rgb(121, 135, 156); + font-family: PingFangSC-Regular; + font-size: 12px; + } + } + } + } + + .form-wrap { + margin-top: 10px; + padding: 20px 10px; + background: #f9fbfd; + font-size: 14px; + + .member-rule { + padding: 20px 10px 20px 0; + position: relative; + min-height: 184px; + border-radius: 4px; + border: 1px solid rgb(218, 225, 232); + + .rule-label { + padding: 0 4px; + position: absolute; + background: #f9fbfd; + top: 0; + left: 0; + transform: translate(10px, -50%); + font-family: PingFangSC-Semibold; + font-size: 14px; + font-weight: 600; + } + + .rule-tip { + padding: 0 10px; + font-size: 12px; + color: #79879c; + } + } + + .advance { + &-setting { + margin-top: 20px; + } + + &-label { + margin-bottom: 20px; + display: flex; + align-items: center; + cursor: pointer; + } + } + + .ant-form-item-label { + padding-left: 10px; + } + } + + .btn-box { + margin-top: 24px; + display: flex; + justify-content: flex-end; + align-items: center; + } + + .ant-form-item-control-input { + box-shadow: none; + } +`; + +export const PopupWrap = styled.ul` + .list-item { + display: flex; + align-items: center; + justify-content: center; + width: 106px; + height: 32px; + cursor: pointer; + + &:hover { + background: #f3f6fa; + } + + font-family: PingFangSC-Regular; + font-size: 14px; + font-weight: normal; + + &.modify { + color: rgb(32, 45, 64); + } + + &.del { + color: #ff3f3f; + } + } +`; diff --git a/src/services/index.ts b/src/services/index.ts index e97db9b..6d77dad 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -12,6 +12,20 @@ export const queryAllUser = async () => { return nameMap; }; +export const queryUserList = async () => { + const response = await HTTP.get('users'); + let list = []; + if (response.code === 0) { + list = response?.data?.map((item: any) => { + return { + label: item.name, + value: item.id, + text: item.name, + }; + }); + } + return list; +}; export interface UserType { id: number; name: string; diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 947567a..0000000 --- a/src/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface LoginToken { - email: string; - exp: number; - iat: number; - is_admin: number; - nbf: number; - user_id: number; - username: string; - uuid: string; -} diff --git a/src/types/index.ts b/src/types/index.ts index 062aa0c..37ba4b8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,13 @@ +export interface LoginToken { + email: string; + exp: number; + iat: number; + is_admin: number; + nbf: number; + user_id: number; + username: string; + uuid: string; +} export interface ClusterItemInfo { id: number; info: string; @@ -8,6 +18,7 @@ export interface ClusterItemInfo { users_count: number; created_at: string; user_id: number; + user_name: string; userName: string; modifiable: boolean; resources: { @@ -18,3 +29,29 @@ export interface ClusterItemInfo { used: number; }[]; } + +export interface ConfigInfo { + admin_base_dn: string; + admin_filter: string; + base_dn: string; + bind_dn: string; + costs: number; + created_at: string; + deletes: number; + email_attr: string; + enable: number; + entries: number; + fails: number; + filter: string; + id: number; + inserts: number; + last_sync_err_msg: string; + ldap_gen: number; + md5: number; + password: string; + server: string; + sync_protection_ts: number; + tls: number; + updates: number; + user_name_attr: string; +}