From 1886b894c5a65b206af97bc9af830a7ffccde2b9 Mon Sep 17 00:00:00 2001 From: freyaLo Date: Wed, 12 Oct 2022 15:59:02 +0800 Subject: [PATCH 1/4] =?UTF-8?q?:art:=20=E4=BB=93=E5=BA=93=E7=99=BB?= =?UTF-8?q?=E8=AE=B0=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/component/authority/constants.ts | 6 + .../shared/component/authority/index.tsx | 17 +- web/packages/shared/component/route/index.ts | 1 + .../shared/component/route/listener/index.tsx | 110 ++++++ web/packages/shared/global.d.ts | 9 + web/packages/shared/hooks/useFetch.ts | 6 +- web/packages/shared/tca/user-auth/README.md | 7 + web/packages/shared/tca/user-auth/api.ts | 58 +++ .../tca/user-auth/auth-form-item/index.tsx | 165 +++++++++ .../auth-form-item/tdesign-index.tsx | 168 +++++++++ .../shared/tca/user-auth/auth-modal.tsx | 169 +++++++++ .../shared/tca/user-auth/auth-table.tsx | 100 +++++ .../tca/user-auth/common-constant/default.ts | 3 + .../tca/user-auth/common-constant/index.ts | 1 + web/packages/shared/tca/user-auth/constant.ts | 116 ++++++ .../tca/user-auth}/git-oauth/index.tsx | 26 +- web/packages/shared/tca/user-auth/index.tsx | 143 ++++++++ web/packages/shared/tca/user-auth/types.ts | 30 ++ .../tdesign-component/authority/index.tsx | 18 +- web/packages/shared/util/fetch.ts | 39 +- web/packages/shared/util/index.ts | 2 +- web/packages/shared/util/route.ts | 17 + web/packages/shared/util/window.ts | 4 +- web/packages/tca-analysis/global.d.ts | 10 +- web/packages/tca-analysis/package.json | 1 + web/packages/tca-analysis/public/index.html | 3 + .../public/locales/en-US/translation.json | 31 +- .../public/locales/zh-CN/translation.json | 31 +- .../src/components/authority/constants.ts | 48 +++ .../src/components/authority/index.tsx | 163 +++++++++ .../src/components/repos/index.tsx | 88 +++-- .../tca-analysis/src/constant/index.ts | 4 +- web/packages/tca-analysis/src/index.tsx | 3 +- .../modules/project-team/overview/index.tsx | 16 +- .../src/modules/projects/index.tsx | 9 +- .../modules/projects/issues/rule-detail.tsx | 11 +- .../modules/projects/metric/ccfiles/index.tsx | 26 +- .../projects/metric/ccissues/detail.tsx | 102 +++--- .../projects/metric/ccissues/issue-modal.tsx | 19 +- .../projects/metric/ccissues/style.scss | 9 + .../projects/metric/dupfiles/index.tsx | 28 +- .../tca-analysis/src/modules/projects/nav.tsx | 12 +- .../src/modules/projects/overview/utils.ts | 160 ++++++-- .../modules/projects/project/first-modal.tsx | 8 +- .../modules/projects/project/project-list.tsx | 34 +- .../modules/projects/scan-history/detail.tsx | 4 +- .../src/modules/repos/authority-config.tsx | 114 ++++++ .../src/modules/repos/constant.tsx | 7 + .../src/modules/repos/constants.ts | 104 ------ .../src/modules/repos/create-repo.tsx | 163 +++++++++ .../tca-analysis/src/modules/repos/create.tsx | 344 ------------------ .../src/modules/repos/edit-modal.tsx | 102 ++++++ .../tca-analysis/src/modules/repos/hooks.ts | 46 --- .../tca-analysis/src/modules/repos/index.tsx | 180 ++++++--- .../tca-analysis/src/modules/repos/list.tsx | 333 +++++++++++++++++ .../src/modules/repos/member-config.tsx | 125 +++++++ .../src/modules/repos/repo-list/index.tsx | 180 --------- .../src/modules/repos/repo-list/left-list.tsx | 125 ------- .../src/modules/repos/repo-list/style.scss | 98 ----- .../repos/repo-list/tabs/authority.tsx | 269 -------------- .../modules/repos/repo-list/tabs/members.tsx | 147 -------- .../modules/repos/repo-list/tabs/overview.tsx | 124 ------- .../src/modules/repos/repo/index.tsx | 179 +++++++++ .../tca-analysis/src/modules/repos/routes.ts | 21 -- .../src/modules/repos/style.module.scss | 86 +++++ .../tca-analysis/src/modules/repos/style.scss | 19 - .../schemes/code-lint/all-rules-search.tsx | 14 +- .../src/modules/schemes/code-lint/index.tsx | 32 +- .../src/modules/schemes/index.tsx | 239 ++++++------ .../template/code-lint/all-rules-search.tsx | 20 +- .../modules/template/code-lint/all-rules.tsx | 2 +- .../src/modules/template/code-lint/index.tsx | 40 +- .../src/modules/template/permission/index.tsx | 11 +- .../src/modules/template/schemes/index.tsx | 63 ++-- .../src/modules/template/sync-modal.tsx | 100 ++--- .../src/modules/welcome/index.tsx | 2 +- .../tca-analysis/src/plat/common/api.ts | 18 +- .../src/plat/common/hooks/index.ts | 13 + .../src/plat/common/modules/index.tsx | 28 ++ .../src/plat/common/{routes.ts => routes.tsx} | 21 +- .../tca-analysis/src/plat/open/hooks.ts | 1 + .../src/plat/open/modules/index.tsx | 1 + .../src/plat/open/{routes.ts => routes.tsx} | 2 + web/packages/tca-analysis/src/root.tsx | 28 +- .../tca-analysis/src/routes/index.tsx | 105 +++--- .../tca-analysis/src/services/common.ts | 11 +- .../tca-analysis/src/services/projects.ts | 4 +- .../tca-analysis/src/services/repos.ts | 41 ++- .../tca-analysis/src/services/schemes.ts | 4 +- .../tca-analysis/src/services/template.tsx | 3 +- web/packages/tca-analysis/src/services/tmf.ts | 51 +++ web/packages/tca-analysis/src/types/index.ts | 41 +++ .../tca-analysis/src/utils/getRoutePath.ts | 55 ++- web/packages/tca-analysis/src/utils/index.ts | 25 +- web/packages/tca-analysis/webpack.config.ts | 21 ++ .../public/locales/en-US/translation.json | 33 -- .../public/locales/zh-CN/translation.json | 33 -- web/packages/tca-layout/src/constant/auth.ts | 37 -- .../modules/layout/user/auth/auth-modal.tsx | 190 ---------- .../modules/layout/user/auth/auth-table.tsx | 97 ----- .../modules/layout/user/auth/git-oauth.tsx | 11 + .../src/modules/layout/user/auth/index.tsx | 182 +-------- .../src/modules/tool-libs/create-libs.tsx | 18 +- .../src/modules/tool-libs/index.tsx | 3 +- .../src/modules/tools/create-tool.tsx | 15 +- .../detail-components/baseinfo/index.tsx | 14 +- .../tca-layout/src/plat/common/api/user.ts | 49 +-- .../tca-layout/src/plat/open/routes.ts | 2 +- .../modules/components/node-tasks-modal.tsx | 10 +- .../tca-manage/src/modules/jobs/constants.ts | 40 +- .../tca-manage/src/modules/jobs/job-table.tsx | 6 +- .../tca-manage/src/modules/style.scss | 16 +- web/packages/webpack/src/config-webpack.ts | 2 +- web/packages/webpack/src/env.ts | 2 +- web/yarn.lock | 19 + 115 files changed, 3711 insertions(+), 2865 deletions(-) create mode 100644 web/packages/shared/component/route/index.ts create mode 100644 web/packages/shared/component/route/listener/index.tsx create mode 100644 web/packages/shared/tca/user-auth/README.md create mode 100644 web/packages/shared/tca/user-auth/api.ts create mode 100644 web/packages/shared/tca/user-auth/auth-form-item/index.tsx create mode 100644 web/packages/shared/tca/user-auth/auth-form-item/tdesign-index.tsx create mode 100644 web/packages/shared/tca/user-auth/auth-modal.tsx create mode 100644 web/packages/shared/tca/user-auth/auth-table.tsx create mode 100644 web/packages/shared/tca/user-auth/common-constant/default.ts create mode 100644 web/packages/shared/tca/user-auth/common-constant/index.ts create mode 100644 web/packages/shared/tca/user-auth/constant.ts rename web/packages/{tca-layout/src/modules => shared/tca/user-auth}/git-oauth/index.tsx (62%) create mode 100644 web/packages/shared/tca/user-auth/index.tsx create mode 100644 web/packages/shared/tca/user-auth/types.ts create mode 100644 web/packages/tca-analysis/src/components/authority/constants.ts create mode 100644 web/packages/tca-analysis/src/components/authority/index.tsx create mode 100644 web/packages/tca-analysis/src/modules/repos/authority-config.tsx create mode 100644 web/packages/tca-analysis/src/modules/repos/constant.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/constants.ts create mode 100644 web/packages/tca-analysis/src/modules/repos/create-repo.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/create.tsx create mode 100644 web/packages/tca-analysis/src/modules/repos/edit-modal.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/hooks.ts create mode 100644 web/packages/tca-analysis/src/modules/repos/list.tsx create mode 100644 web/packages/tca-analysis/src/modules/repos/member-config.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/repo-list/index.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/repo-list/left-list.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/repo-list/style.scss delete mode 100644 web/packages/tca-analysis/src/modules/repos/repo-list/tabs/authority.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/repo-list/tabs/members.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/repo-list/tabs/overview.tsx create mode 100644 web/packages/tca-analysis/src/modules/repos/repo/index.tsx delete mode 100644 web/packages/tca-analysis/src/modules/repos/routes.ts create mode 100644 web/packages/tca-analysis/src/modules/repos/style.module.scss delete mode 100644 web/packages/tca-analysis/src/modules/repos/style.scss create mode 100644 web/packages/tca-analysis/src/plat/common/hooks/index.ts create mode 100644 web/packages/tca-analysis/src/plat/common/modules/index.tsx rename web/packages/tca-analysis/src/plat/common/{routes.ts => routes.tsx} (64%) create mode 100644 web/packages/tca-analysis/src/plat/open/hooks.ts create mode 100644 web/packages/tca-analysis/src/plat/open/modules/index.tsx rename web/packages/tca-analysis/src/plat/open/{routes.ts => routes.tsx} (63%) create mode 100644 web/packages/tca-analysis/src/services/tmf.ts create mode 100644 web/packages/tca-analysis/src/types/index.ts delete mode 100644 web/packages/tca-layout/src/modules/layout/user/auth/auth-modal.tsx delete mode 100644 web/packages/tca-layout/src/modules/layout/user/auth/auth-table.tsx create mode 100644 web/packages/tca-layout/src/modules/layout/user/auth/git-oauth.tsx diff --git a/web/packages/shared/component/authority/constants.ts b/web/packages/shared/component/authority/constants.ts index ec64a8b82..a54023673 100644 --- a/web/packages/shared/component/authority/constants.ts +++ b/web/packages/shared/component/authority/constants.ts @@ -34,6 +34,12 @@ export const AUTH_TYPE_TXT = { OAUTH: 'OAuth', }; +export const AUTH_TYPE_CHOICES = { + [AUTH_TYPE.HTTP]: AUTH_TYPE_TXT.HTTP, + [AUTH_TYPE.SSH]: AUTH_TYPE_TXT.SSH, + [AUTH_TYPE.OAUTH]: AUTH_TYPE_TXT.OAUTH, +}; + // 凭证映射,对应 api 返回的字段名 export const SCM_MAP = { [AUTH_TYPE.HTTP]: 'scm_account', diff --git a/web/packages/shared/component/authority/index.tsx b/web/packages/shared/component/authority/index.tsx index 430f79213..4030b6a3c 100644 --- a/web/packages/shared/component/authority/index.tsx +++ b/web/packages/shared/component/authority/index.tsx @@ -8,8 +8,8 @@ import PlusIcon from 'coding-oa-uikit/lib/icon/Plus'; import RefreshIcon from 'coding-oa-uikit/lib/icon/Refresh'; import { AUTH_TYPE, AUTH_TYPE_TXT, SCM_MAP, SCM_PLATFORM_CHOICES } from './constants'; +import { FormInstance } from 'coding-oa-uikit/lib/form'; -import { FormInstance } from 'rc-field-form'; const { Option, OptGroup } = Select; export interface RestfulListAPIParams { @@ -30,10 +30,14 @@ interface AuthorityProps { placeholder?: string; required?: boolean; allowClear?: boolean; + formLayout?: any; + /** 新增凭证路由 */ + addAuthRouter?: string } const Authority = (props: AuthorityProps) => { - const { form, name, label, initAuth, getAuthList, selectStyle = {}, placeholder, required, allowClear } = props; + const { form, name, label, initAuth, getAuthList, selectStyle = {}, + placeholder, required, allowClear, formLayout, addAuthRouter } = props; const [sshAuthList, setSshAuthList] = useState([]); const [httpAuthList, setHttpAuthList] = useState([]); const [oauthAuthList, setOauthAuthList] = useState([]); @@ -105,7 +109,7 @@ const Authority = (props: AuthorityProps) => { }, [initAuth, authLoading]); return ( - + document.body} + optionLabelProp="label" + allowClear={allowClear} + > + {/* oauth */} + {!isEmpty(authData.oauthList) && ( + + {authData.oauthList.map((auth: any) => ( + + ))} + + )} + {/* ssh */} + {!isEmpty(authData.sshList) && ( + + {authData.sshList.map((auth: any) => ( + + ))} + + )} + {/* http */} + {!isEmpty(authData.accountList) && ( + + {authData.accountList.map((auth: any) => ( + + ))} + + )} + + +
+ document.body}> + + + document.body}> + + +
+
; +}; + +export default UserAuthFormItem; diff --git a/web/packages/shared/tca/user-auth/auth-form-item/tdesign-index.tsx b/web/packages/shared/tca/user-auth/auth-form-item/tdesign-index.tsx new file mode 100644 index 000000000..8f47ea0ca --- /dev/null +++ b/web/packages/shared/tca/user-auth/auth-form-item/tdesign-index.tsx @@ -0,0 +1,168 @@ +import React, { useEffect, useState } from 'react'; +import { isEmpty, get } from 'lodash'; +import { Button, Form, Select, Tooltip, FormInstanceFunctions } from 'tdesign-react'; +import { AddIcon, RefreshIcon } from 'tdesign-icons-react'; + +import { AuthTypeEnum, AuthTypeTxtEnum, SCM_PLATFORM_CHOICES, SCM_MAP } from '../constant'; +import { UserAuthAPI } from '../types'; +import { openURL } from '../../../util'; + +const { FormItem } = Form; + +const { Option, OptionGroup } = Select; + +const authInitData = { + sshList: [], + accountList: [], + oauthList: [], +}; + +interface UserAuthFormItemProps { + /** form 实例 */ + form: FormInstanceFunctions; + /** form item name */ + name: string; + /** form item label */ + label: string | React.ReactNode; + /** form item required */ + required?: boolean; + /** form item layout */ + formLayout?: any; + /** select placeholder */ + placeholder?: string; + /** select allowClear */ + allowClear?: boolean; + /** select style */ + selectStyle?: React.CSSProperties; + /** auth api */ + userAuthAPI: UserAuthAPI; + /** 传入的 scm auth 信息 */ + authinfo?: any; + /** 新增凭证路由 */ + addAuthRouter?: string; +} + +const UserAuthFormItem = ({ + form, name, label, required, placeholder, allowClear, selectStyle, + userAuthAPI, authinfo, addAuthRouter, +}: UserAuthFormItemProps) => { + const [initAuthData, setInitAuthData] = useState<{ + accountList: any[], sshList: any[], oauthList: any[] + }>(authInitData); + const [authData, setAuthData] = useState<{ accountList: any[], sshList: any[], oauthList: any[] }>(authInitData); + const [loading, setLoading] = useState(false); + const [reload, setReload] = useState(false); + + useEffect(() => { + // 获取凭证列表,记录记录当前用户个人凭证 + setLoading(true); + userAuthAPI.getAuthInfos().then(setInitAuthData) + .finally(() => setLoading(false)); + }, [reload]); + + useEffect(() => { + // 判断是否存在authinfo,如果存在则配置auth列表,且setFieldsValue + if (authinfo) { + const { scm_oauth: scmOAuth, scm_ssh: scmSsh, scm_account: scmAccount, auth_type: authType } = authinfo; + const { oauthList, sshList, accountList } = initAuthData; + if (scmOAuth && authType === AuthTypeEnum.OAUTH && !oauthList.filter(i => i.id === scmOAuth.id).length) { + setAuthData({ oauthList: [scmOAuth, ...oauthList], sshList, accountList }); + } else if (scmSsh && authType === AuthTypeEnum.SSH && !sshList.filter(i => i.id === scmSsh.id).length) { + setAuthData({ oauthList, sshList: [scmSsh, ...sshList], accountList }); + } else if (scmAccount && authType === AuthTypeEnum.HTTP + && !accountList.filter(i => i.id === scmAccount.id).length) { + setAuthData({ oauthList, sshList, accountList: [scmAccount, ...accountList] }); + } else { + setAuthData(initAuthData); + } + const auth = authinfo?.[SCM_MAP[authinfo.auth_type as AuthTypeEnum]]; + if (auth?.id) { + form?.setFieldsValue?.({ [name]: `${authinfo.auth_type}#${auth.id}` }); + } + } else { + // 默认配置auth列表 + form?.setFieldsValue?.({ [name]: undefined }); + setAuthData(initAuthData); + } + }, [authinfo, form, name, initAuthData]); + + return + + + +
+ + + + + + +
+
; +}; + +export default UserAuthFormItem; diff --git a/web/packages/shared/tca/user-auth/auth-modal.tsx b/web/packages/shared/tca/user-auth/auth-modal.tsx new file mode 100644 index 000000000..ff28f9919 --- /dev/null +++ b/web/packages/shared/tca/user-auth/auth-modal.tsx @@ -0,0 +1,169 @@ +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Modal, Form, Input, Select, message } from 'coding-oa-uikit'; +import { UserAuthAPI } from './types'; + +// 项目内 +import { AuthTypeEnum, AUTH_TYPE_OPTIONS, ScmPlatformEnum, SCM_PLATFORM_OPTIONS } from './constant'; + +interface AuthModalProps { + /** 弹框开关 */ + visible: boolean; + /** auth 接口模块 */ + userAuthAPI: UserAuthAPI; + /** 凭证信息 */ + authinfo?: any; + /** 确认操作 */ + onOk: () => void; + /** 取消操作 */ + onCancel: () => void; +} + +const AuthModal = ({ visible, authinfo, userAuthAPI, onOk, onCancel }: AuthModalProps) => { + const [form] = Form.useForm(); + const { t } = useTranslation(); + + const isUpdate = !!authinfo; + + useEffect(() => { + if (visible) { + form.resetFields(); + } + }, [visible]); + + /** 凭证创建/更新操作 */ + const onAuthHandler = (formData: any, type: 'ssh' | 'account') => { + if (authinfo) { + return { promise: userAuthAPI[type].update(authinfo.id, formData), created: false }; + } + return { promise: userAuthAPI[type].create(formData), created: true }; + }; + + /** 表单保存操作 */ + const onSubmitHandle = () => { + form.validateFields().then((formData) => { + const { promise, created } = formData.auth_type === AuthTypeEnum.HTTP + ? onAuthHandler(formData, 'account') + : onAuthHandler({ ...formData, password: null, git_token: null }, 'ssh'); + promise.then(() => { + created ? message.success(t('已录入一条新凭证')) : message.success(t('已更新凭证')); + form.resetFields(); + onOk(); + }); + }); + }; + + return +
+ + + + + + + + ) : ( + <> + + + + + + + + )) + } + + { + !isUpdate && ( + <> + + + + )} + + + ) + } + +
+
; +}; + +export default AuthModal; diff --git a/web/packages/shared/tca/user-auth/auth-table.tsx b/web/packages/shared/tca/user-auth/auth-table.tsx new file mode 100644 index 000000000..e6e8d516b --- /dev/null +++ b/web/packages/shared/tca/user-auth/auth-table.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Table, Button, Tag, Tooltip, Space, Popconfirm } from 'coding-oa-uikit'; +import Edit from 'coding-oa-uikit/lib/icon/Edit'; +import Trash from 'coding-oa-uikit/lib/icon/Trash'; +import LinkIcon from 'coding-oa-uikit/lib/icon/Link'; +import RefreshIcon from 'coding-oa-uikit/lib/icon/Refresh'; +import Close2Icon from 'coding-oa-uikit/lib/icon/Close2'; +import Success from 'coding-oa-uikit/lib/icon/Success'; +import Aborted2 from 'coding-oa-uikit/lib/icon/Aborted2'; + +// 项目内 +import { AuthTypeEnum, AUTH_TYPE_CHOICES, ScmPlatformEnum, SCM_PLATFORM_CHOICES, SCM_PLATFORM_OPTIONS } from './constant'; + +const { Column } = Table; + +interface AuthTableProps { + /** 数据源 */ + dataSource: Array; + /** 编辑操作 */ + onEdit: (authinfo: any) => void; + /** 删除操作 */ + onDel: (authinfo: any) => void; + /** 是否展示凭证创建渠道,默认不展示 */ + showOrigin?: boolean; +} + +const AuthTable = ({ dataSource, onEdit, onDel, showOrigin }: AuthTableProps) => { + const { t } = useTranslation(); + return `${item.auth_type}#${item.id}`} + dataSource={dataSource} + pagination={{ + hideOnSinglePage: true, + pageSize: 30, + showTotal: (total: number, range: [number, number]) => `${range[0]} - ${range[1]} 条,共 ${total} 条`, + }} + > + { + let txt: React.ReactNode = value; + if (record.auth_type === AuthTypeEnum.OAUTH) { + txt = record.oauth_status ? <>已认证 : <>未认证 ; + } else if (record.auth_type === AuthTypeEnum.SSH) { + txt = record.name; + } + return {AUTH_TYPE_CHOICES[record.auth_type as AuthTypeEnum]}:{txt}; + } + } + /> + record.scm_platform === value} + render={(value: ScmPlatformEnum) => {SCM_PLATFORM_CHOICES[value]}} + /> + {showOrigin && value || 'CodeDog'} + />} + { + if (record.auth_type === AuthTypeEnum.OAUTH) { + return !record.oauth_status ?
; +}; + +export default AuthTable; diff --git a/web/packages/shared/tca/user-auth/common-constant/default.ts b/web/packages/shared/tca/user-auth/common-constant/default.ts new file mode 100644 index 000000000..b378057fb --- /dev/null +++ b/web/packages/shared/tca/user-auth/common-constant/default.ts @@ -0,0 +1,3 @@ +export const PLATFORM_CHOICES_DEFAULT = '默认'; + +export const PLATFORM_NAME_ENUM_DEFAULT = 'default'; diff --git a/web/packages/shared/tca/user-auth/common-constant/index.ts b/web/packages/shared/tca/user-auth/common-constant/index.ts new file mode 100644 index 000000000..acced897e --- /dev/null +++ b/web/packages/shared/tca/user-auth/common-constant/index.ts @@ -0,0 +1 @@ +export * from './default'; diff --git a/web/packages/shared/tca/user-auth/constant.ts b/web/packages/shared/tca/user-auth/constant.ts new file mode 100644 index 000000000..2a6a1994f --- /dev/null +++ b/web/packages/shared/tca/user-auth/constant.ts @@ -0,0 +1,116 @@ +import { generateOptions } from '../../util'; +import { PLATFORM_CHOICES_DEFAULT, PLATFORM_NAME_ENUM_DEFAULT } from './common-constant'; + +/** 凭证类型枚举 */ +export enum AuthTypeEnum { + HTTP = 'password', + SSH = 'ssh_token', + OAUTH = 'oauth', +} + +/** 凭证类型文本枚举 */ +export enum AuthTypeTxtEnum { + HTTP = '用户名 + 密码', + SSH = 'SSH', + OAUTH = 'OAuth', +} + +/** 凭证类型 kv */ +export const AUTH_TYPE_CHOICES = { + [AuthTypeEnum.HTTP]: AuthTypeTxtEnum.HTTP, + [AuthTypeEnum.SSH]: AuthTypeTxtEnum.SSH, + [AuthTypeEnum.OAUTH]: AuthTypeTxtEnum.OAUTH, +}; + +/** 凭证类型 options,排除 oauth */ +export const AUTH_TYPE_OPTIONS = [{ + label: AUTH_TYPE_CHOICES[AuthTypeEnum.HTTP], + value: AuthTypeEnum.HTTP, +}, { + label: AUTH_TYPE_CHOICES[AuthTypeEnum.SSH], + value: AuthTypeEnum.SSH, +}]; + +/** 凭证映射,对应 api 返回的字段名*/ +export const SCM_MAP = { + [AuthTypeEnum.HTTP]: 'scm_account', + [AuthTypeEnum.SSH]: 'scm_ssh', + [AuthTypeEnum.OAUTH]: 'scm_oauth', +}; + +/** OAuth 所属平台类型枚举 */ +export enum ScmPlatformEnum { + DEFAULT = 1, + GIT_TENCENT, + CODING, + GITHUB, + GITEE, + GITLAB, + OTHER, +} + + +/** OAuth 所属平台类型 kv */ +export const SCM_PLATFORM_CHOICES = { + // 暂时定制兼容 + [ScmPlatformEnum.DEFAULT]: PLATFORM_CHOICES_DEFAULT, + [ScmPlatformEnum.GIT_TENCENT]: '腾讯工蜂', + [ScmPlatformEnum.CODING]: 'Coding', + [ScmPlatformEnum.GITHUB]: 'GitHub', + [ScmPlatformEnum.GITEE]: 'Gitee', + [ScmPlatformEnum.GITLAB]: 'GitLab', + [ScmPlatformEnum.OTHER]: '其它平台', +}; + +/** OAuth 所属平台类型 options */ +export const SCM_PLATFORM_OPTIONS = generateOptions(SCM_PLATFORM_CHOICES, true); + +/** OAuth 所属平台类型名称 */ +export const ScmPlatformNameEnum = { + DEFAULT: PLATFORM_NAME_ENUM_DEFAULT, + GIT_TENCENT: 'tgitsaas', + CODING: 'coding', + GITHUB: 'github', + GITEE: 'gitee', + GITLAB: 'gitlab', +}; + +/** OAuth 所属平台类型名称 kv */ +export const SCM_PLATFORM_NAME_CHOICES = { + [ScmPlatformNameEnum.DEFAULT]: { + id: ScmPlatformEnum.DEFAULT, + scm_platform: ScmPlatformEnum.DEFAULT, + scm_platform_name: ScmPlatformNameEnum.DEFAULT, + auth_type: AuthTypeEnum.OAUTH, + }, + [ScmPlatformNameEnum.GIT_TENCENT]: { + id: ScmPlatformEnum.GIT_TENCENT, + scm_platform: ScmPlatformEnum.GIT_TENCENT, + scm_platform_name: ScmPlatformNameEnum.GIT_TENCENT, + auth_type: AuthTypeEnum.OAUTH, + }, + [ScmPlatformNameEnum.CODING]: { + id: ScmPlatformEnum.CODING, + scm_platform: ScmPlatformEnum.CODING, + scm_platform_name: ScmPlatformNameEnum.CODING, + auth_type: AuthTypeEnum.OAUTH, + }, + [ScmPlatformNameEnum.GITHUB]: { + id: ScmPlatformEnum.GITHUB, + scm_platform: ScmPlatformEnum.GITHUB, + scm_platform_name: ScmPlatformNameEnum.GITHUB, + auth_type: AuthTypeEnum.OAUTH, + }, + [ScmPlatformNameEnum.GITEE]: { + id: ScmPlatformEnum.GITEE, + scm_platform: ScmPlatformEnum.GITEE, + scm_platform_name: ScmPlatformNameEnum.GITEE, + auth_type: AuthTypeEnum.OAUTH, + }, + [ScmPlatformNameEnum.GITLAB]: { + id: ScmPlatformEnum.GITLAB, + scm_platform: ScmPlatformEnum.GITLAB, + scm_platform_name: ScmPlatformNameEnum.GITLAB, + auth_type: AuthTypeEnum.OAUTH, + }, +}; diff --git a/web/packages/tca-layout/src/modules/git-oauth/index.tsx b/web/packages/shared/tca/user-auth/git-oauth/index.tsx similarity index 62% rename from web/packages/tca-layout/src/modules/git-oauth/index.tsx rename to web/packages/shared/tca/user-auth/git-oauth/index.tsx index b40c243c2..c01798f94 100644 --- a/web/packages/tca-layout/src/modules/git-oauth/index.tsx +++ b/web/packages/shared/tca/user-auth/git-oauth/index.tsx @@ -1,23 +1,27 @@ import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import Loading from '@tencent/micro-frontend-shared/component/loading'; -import { getURLSearch } from '@tencent/micro-frontend-shared/util/route'; -import { PostMessageType, PostMessageCode, postMessageToTarget } from '@tencent/micro-frontend-shared/util/window'; - import { message } from 'coding-oa-uikit'; -import Container from '@src/component/container'; -import { UserAPI } from '@plat/api'; +// 项目内 +import Loading from '../../../component/loading'; +import { getURLSearch } from '../../../util/route'; +import { FetchAPIManager } from '../../../util/fetch'; +import { PostMessageType, PostMessageCode, postMessageToTarget } from '../../../util/window'; + +interface GitOAuthCallbackProps { + /** auth 接口模块 */ + oauthCallback: FetchAPIManager +} -const GitOAuth = () => { +const GitOAuthCallback = ({ oauthCallback }: GitOAuthCallbackProps) => { const query = getURLSearch(); const { scmPlatformName }: any = useParams(); const { t } = useTranslation(); useEffect(() => { const { opener } = window; - UserAPI.getOAuthCallback(scmPlatformName, query).then(() => { + oauthCallback.getDetail(scmPlatformName, query).then(() => { message.success(t('OAuth授权成功')); postMessageToTarget(opener, { code: PostMessageCode.SUCCESS, type: PostMessageType.GIT_OAUTH }); }) @@ -31,10 +35,8 @@ const GitOAuth = () => { }, []); return ( - - - + ); }; -export default GitOAuth; +export default GitOAuthCallback; diff --git a/web/packages/shared/tca/user-auth/index.tsx b/web/packages/shared/tca/user-auth/index.tsx new file mode 100644 index 000000000..66869f314 --- /dev/null +++ b/web/packages/shared/tca/user-auth/index.tsx @@ -0,0 +1,143 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, Row, Col, message } from 'coding-oa-uikit'; +import Plus from 'coding-oa-uikit/lib/icon/Plus'; + +// 项目内 +import { openWindow, PostMessageType, PostMessageCode } from '../../util/window'; +import AuthTable from './auth-table'; +import AuthModal from './auth-modal'; +import { AuthTypeEnum } from './constant'; +import { UserAuthAPI } from './types'; + +export * from './constant'; +export * from './types'; +export * from './api'; + +/** auth modal 初始值 */ +const authConfInitData = { + visible: false, + authinfo: undefined, +}; + +/** UserAuth 入参数结构 */ +interface UserAuthProps { + /** auth 接口模块 */ + userAuthAPI: UserAuthAPI; + /** 展示凭证创建渠道,默认false */ + showOrigin?: boolean; +} + +const UserAuth = ({ userAuthAPI, showOrigin }: UserAuthProps) => { + const [dataSource, setDataSource] = useState([]); + const [reload, setReload] = useState(false); + const [authConf, setAuthConf] = useState(authConfInitData); + const { t } = useTranslation(); + + useEffect(() => { + // 获取凭证列表 + userAuthAPI.getAuths().then(setDataSource); + }, [reload]); + + /** + * 监听OAuth事件 + */ + useEffect(() => { + const receiveOAuthHandler = (event: MessageEvent) => { + const curLocation = window.location; + if (event.origin === curLocation.origin) { + if (event.data?.type === PostMessageType.GIT_OAUTH && event.data?.code === PostMessageCode.SUCCESS) { + message.success(t('OAuth授权成功')); + setReload(!reload); + } else if (event.data?.type === PostMessageType.GIT_OAUTH && event.data?.code === PostMessageCode.FAIL) { + message.error(t('OAuth授权失败')); + } + } + }; + + window.addEventListener('message', receiveOAuthHandler, false); + return () => { + window.removeEventListener('message', receiveOAuthHandler); + }; + }, [reload]); + + /** 创建/更新凭证信息 */ + const onUpdateOrCreateHandler = (authinfo?: any) => { + if (authinfo && authinfo.auth_type === AuthTypeEnum.OAUTH) { + userAuthAPI.oauthStatus.get({ scm_platform_name: authinfo.scm_platform_name }).then((res: any) => { + openWindow(res.git_auth_url || '', 'Git OAuth'); + }) + .catch(() => { + message.error(t('平台暂未配置OAuth应用,无法去授权,请联系管理员。')); + }); + } else { + setAuthConf({ + visible: true, + authinfo, + }); + } + }; + + /** 删除当前凭证 */ + const onDeleteHandler = (authinfo: any) => { + let promise = null; + let msg = t('已删除凭证'); + switch (authinfo.auth_type) { + case AuthTypeEnum.HTTP: + promise = userAuthAPI.account.delete(authinfo.id); + break; + case AuthTypeEnum.SSH: + promise = userAuthAPI.ssh.delete(authinfo.id); + break; + case AuthTypeEnum.OAUTH: + msg = t('已取消授权'); + promise = userAuthAPI.oauthStatus.del({ scm_platform_name: authinfo.scm_platform_name }); + break; + } + promise?.then(() => { + message.success(msg); + setReload(!reload); + }); + }; + + return <>
+ + +

{t('个人凭证管理')}

+ + + + +
+

+ {t('录入后,仓库登记、分支项目等模块可直接选择凭证,无需重复填写。')} +

+
+ + { + setAuthConf(authConfInitData); + setReload(!reload); + }} + onCancel={() => { + setAuthConf(authConfInitData); + }} + /> + ; +}; + +export default UserAuth; diff --git a/web/packages/shared/tca/user-auth/types.ts b/web/packages/shared/tca/user-auth/types.ts new file mode 100644 index 000000000..75721238f --- /dev/null +++ b/web/packages/shared/tca/user-auth/types.ts @@ -0,0 +1,30 @@ +import { FetchAPIManager } from '../../util/fetch'; + +/** OAuth 凭证相关返回结果数据结构 */ +export type OAuthResult = { + [key: string]: string +}; + +/** user auth api 数据结构 */ +export type UserAuthAPI = { + /** 个人凭证列表 */ + getAuths: () => Promise, + /** 个人凭证信息列表 */ + getAuthInfos: () => Promise<{ + oauthList: any[], + sshList: any[], + accountList: any[] + }>, + /** ssh 凭证接口 */ + ssh: FetchAPIManager, + /** account 凭证接口 */ + account: FetchAPIManager, + /** oauth 凭证接口 */ + oauth: FetchAPIManager, + /** oauth 凭证后台配置项接口 */ + oauthSetting: FetchAPIManager, + /** oauth 凭证个人认证项接口 */ + oauthStatus: FetchAPIManager, + /** oauth 授权回调接口 */ + oauthCallback: FetchAPIManager +}; diff --git a/web/packages/shared/tdesign-component/authority/index.tsx b/web/packages/shared/tdesign-component/authority/index.tsx index 69763cce4..9a6279d48 100644 --- a/web/packages/shared/tdesign-component/authority/index.tsx +++ b/web/packages/shared/tdesign-component/authority/index.tsx @@ -30,17 +30,18 @@ interface AuthorityProps { selectStyle?: any; placeholder?: string; required?: boolean; + allowClear?: boolean; } const Authority = (props: AuthorityProps) => { - const { form, name, label, initAuth, getAuthList, selectStyle = {}, placeholder, required } = props; + const { form, name, label, initAuth, getAuthList, selectStyle = {}, placeholder, required, allowClear } = props; const [sshAuthList, setSshAuthList] = useState([]); const [httpAuthList, setHttpAuthList] = useState([]); const [oauthAuthList, setOauthAuthList] = useState([]); const [authLoading, setAuthLoading] = useState(false); const history = useHistory(); - const setCurAuth = (sshList = sshAuthList, httpList = httpAuthList) => { + const setCurAuth = (sshList = sshAuthList, httpList = httpAuthList, oauthList = oauthAuthList) => { // 设置初始值 if (initAuth[SCM_MAP[initAuth.auth_type]]?.id) { form?.setFieldsValue?.({ [name]: `${initAuth.auth_type}#${initAuth[SCM_MAP[initAuth.auth_type]]?.id}` }); @@ -61,6 +62,13 @@ const Authority = (props: AuthorityProps) => { ) { setHttpAuthList([initAuth.scm_account, ...httpList]); } + if ( + initAuth.scm_oauth + && initAuth.auth_type === AUTH_TYPE.OAUTH + && !find(oauthAuthList, { id: initAuth.scm_oauth?.id }) + ) { + setOauthAuthList([initAuth.scm_oauth, ...oauthList]); + } }; const getAuth = () => { @@ -104,6 +112,7 @@ const Authority = (props: AuthorityProps) => { document.body} + optionLabelProp="label" + > + {!isEmpty(oauthAuthList) && ( + + {oauthAuthList.map((auth: any) => ( + + ))} + + )} + {!isEmpty(sshAuthList) && ( + + {sshAuthList.map((auth: any) => ( + + ))} + + )} + {!isEmpty(httpAuthList) && ( + + {httpAuthList.map((auth: any) => ( + + ))} + + )} + +
+
+ document.body}> + + + document.body}> + + +
+ + ); +}; + +export default Authority; diff --git a/web/packages/tca-analysis/src/components/repos/index.tsx b/web/packages/tca-analysis/src/components/repos/index.tsx index 4f6a1744d..ae2783f8c 100644 --- a/web/packages/tca-analysis/src/components/repos/index.tsx +++ b/web/packages/tca-analysis/src/components/repos/index.tsx @@ -8,19 +8,23 @@ * 代码库列表组件 */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import cn from 'classnames'; +import { useParams, Link } from 'react-router-dom'; + import { isEmpty, find, toNumber } from 'lodash'; -import { Dropdown, Menu, Input } from 'coding-oa-uikit'; +import { Dropdown, Menu, Input, Tooltip } from 'coding-oa-uikit'; import RepoIcon from 'coding-oa-uikit/lib/icon/Repos'; import CaretDown from 'coding-oa-uikit/lib/icon/CaretDown'; -import Link from 'coding-oa-uikit/lib/icon/Link'; - +import LinkIcon from 'coding-oa-uikit/lib/icon/Link'; +import ConfigIcon from 'coding-oa-uikit/lib/icon/Cog'; -import Copy from '@src/components/copy'; +import { getRepos } from '@src/services/common'; +import { getRepoName } from '@src/utils'; +import { getReposRouter } from '@src/utils/getRoutePath'; import { useStateStore, useDispatchStore } from '@src/context/store'; - +import { SET_CUR_REPO } from '@src/context/constant'; import style from './style.scss'; interface IProps { @@ -30,12 +34,28 @@ interface IProps { } const Repos = (props: IProps) => { - const { callback } = props; + const { repoId } = useParams(); + const { orgSid, teamName, callback } = props; const { curRepo, repos } = useStateStore(); const dispatch = useDispatchStore(); const [visible, setVisible] = useState(false); const [searchValue, onChangeSearchValue] = useState(''); + const [searchRepos, setSearchRepos] = useState([]); + + useEffect(() => { + if (repoId && toNumber(repoId) !== curRepo.id) { + const repo = find(repos, { id: toNumber(repoId) }); + if (repo) { + dispatch({ + type: SET_CUR_REPO, + payload: repo, + }); + } else { + // message.error('代码库不存在'); + } + } + }, [repoId]); const handleVisibleChange = (flag: boolean) => { setVisible(flag); @@ -46,12 +66,24 @@ const Repos = (props: IProps) => { setVisible(false); } - const repo = find(repos, { id: toNumber(e.key) }); + let repo = find(repos, { id: toNumber(e.key) }); + + if (!repo) { + repo = find(searchRepos, { id: toNumber(e.key) }); + } onChangeRepo(repo); }; const onSearch = (value: string) => { onChangeSearchValue(value); + getRepos(orgSid, teamName, { + scope: 'related_me', + scm_url_or_name: value, + limit: 10, + offset: 0, + }).then((res) => { + setSearchRepos(res.results || []); + }); }; const onChangeRepo = (repo: any) => { @@ -64,6 +96,16 @@ const Repos = (props: IProps) => { } }; + const renderRepoItem = () => { + const repoList = searchValue ? searchRepos : repos; + return repoList.map(item => ( + + + {item.name || getRepoName(item.scm_url)} + + )); + }; + return (
@@ -80,21 +122,12 @@ const Repos = (props: IProps) => { size='middle' allowClear placeholder='代码库筛选' - onChange={(e: any) => { - onSearch(e.target.value); + onSearch={(value: string) => { + onSearch(value); }} /> - { - repos - .filter((item: any) => item.name?.toLowerCase().includes(searchValue.toLowerCase())) - .map((item: any) => ( - - - {item.name} - - )) - } + {renderRepoItem()} }>
@@ -103,10 +136,17 @@ const Repos = (props: IProps) => {
- - - - + {/* */} + + + + + + + + + +
); diff --git a/web/packages/tca-analysis/src/constant/index.ts b/web/packages/tca-analysis/src/constant/index.ts index 389fd2ae8..dc3d23d90 100644 --- a/web/packages/tca-analysis/src/constant/index.ts +++ b/web/packages/tca-analysis/src/constant/index.ts @@ -11,10 +11,10 @@ export const BASE_ROUTE_PREFIX = '/t/:orgSid/p/:teamName'; export const REPOS_ROUTE_PREFIX = `${BASE_ROUTE_PREFIX}/repos`; /** 分析项目路由前缀 */ -export const PROJECT_ROUTE_PREFIX = `${BASE_ROUTE_PREFIX}/code-analysis/repos/:repoId/projects/:projectId`; +export const PROJECT_ROUTE_PREFIX = `${BASE_ROUTE_PREFIX}/code-analysis/(project)?/repos/:repoId/projects/:projectId`; /** 分析方案路由前缀 */ -export const SCHEMES_ROUTE_PREFIX = `${BASE_ROUTE_PREFIX}/code-analysis/repos/:repoId/schemes/:schemeId`; +export const SCHEMES_ROUTE_PREFIX = `${BASE_ROUTE_PREFIX}/code-analysis/(scheme)?/repos/:repoId/schemes/:schemeId`; /** 分析方案模板路由前缀 */ export const TMPL_ROUTE_PREFIX = `${BASE_ROUTE_PREFIX}/template`; diff --git a/web/packages/tca-analysis/src/index.tsx b/web/packages/tca-analysis/src/index.tsx index 469ed9bf1..cec5d18f6 100644 --- a/web/packages/tca-analysis/src/index.tsx +++ b/web/packages/tca-analysis/src/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import MicroInit from '@tencent/micro-frontend-shared/component/micro-init'; import MicroLayout from '@tencent/micro-frontend-shared/component/micro-layout'; import initI18next from '@tencent/micro-frontend-shared/i18n'; +import { isTrue } from '@tencent/micro-frontend-shared/util'; import { initReactI18next } from 'react-i18next'; // import { StoreProvider } from './context/store'; @@ -18,7 +19,7 @@ MicroInit({ id: 'container', name: pkg.name, container: ( - + ), diff --git a/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx b/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx index 52bbfc0fe..343b74e31 100644 --- a/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx +++ b/web/packages/tca-analysis/src/modules/project-team/overview/index.tsx @@ -4,14 +4,14 @@ // See LICENSE for details // ============================================================================== -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { useParams, useHistory } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Form, Button, Input, message } from 'coding-oa-uikit'; -import { pick, get, find } from 'lodash'; -import { useSelector } from 'react-redux'; +import { pick } from 'lodash'; // 项目内 +import { useLoginUserIsAdmin } from '@plat/hooks'; import { getProjectListRouter, getProjectOverviewRouter } from '@src/utils/getRoutePath'; import { formatDateTime, getUserName } from '@src/utils'; import { getProjectTeam, putProjectTeam, disableProject } from '@src/services/common'; @@ -28,14 +28,12 @@ const Overview = () => { const { orgSid, teamName }: any = useParams(); // 判断是否有权限删除团队项目 const history: any = useHistory(); - const APP = useSelector((state: any) => state.APP); - const isSuperuser = get(APP, 'user.is_superuser', false); // 当前用户是否是超级管理员 - const userName = get(APP, 'user.username', null); - const isAdmin = !!find(team?.admins, { username: userName }); // 当前用户是否是项目管理员 - const deletable = isAdmin || isSuperuser; // 删除权限 const [deleteVisible, setDeleteVisible] = useState(false); const { t } = useTranslation(); + const admins = useMemo(() => team?.admins?.map((userinfo: any) => userinfo.username) || [], [team]); + const isAdmin = useLoginUserIsAdmin(admins); + // 重置 const onReset = () => { setEdit(false); @@ -144,7 +142,7 @@ const Overview = () => { {t('编辑')} )} - {deletable && } diff --git a/web/packages/tca-analysis/src/modules/projects/index.tsx b/web/packages/tca-analysis/src/modules/projects/index.tsx index a0669ccc0..9f1d4c3fa 100644 --- a/web/packages/tca-analysis/src/modules/projects/index.tsx +++ b/web/packages/tca-analysis/src/modules/projects/index.tsx @@ -9,7 +9,7 @@ */ import React, { useEffect, useState } from 'react'; -import { Switch, Route, useHistory, useParams } from 'react-router-dom'; +import { Switch, Route, useHistory, useParams, Redirect } from 'react-router-dom'; import { isEmpty, get } from 'lodash'; import { useStateStore } from '@src/context/store'; @@ -56,8 +56,8 @@ const Projects = () => { return (
history.push(getProjectRouter(orgSid, teamName, repo.id)) } /> @@ -72,7 +72,7 @@ const Projects = () => { } /> { // path={`${BASE_ROUTE_PREFIX}/code-analysis/repos/:repoId?/projects/:projectId?/:tabs`} render={() =>
); diff --git a/web/packages/tca-analysis/src/modules/projects/issues/rule-detail.tsx b/web/packages/tca-analysis/src/modules/projects/issues/rule-detail.tsx index 697619773..8256fa109 100644 --- a/web/packages/tca-analysis/src/modules/projects/issues/rule-detail.tsx +++ b/web/packages/tca-analysis/src/modules/projects/issues/rule-detail.tsx @@ -9,12 +9,12 @@ */ import React from 'react'; import cn from 'classnames'; -import { Link, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import ReactMarkdown from 'react-markdown'; import { get } from 'lodash'; import { Drawer } from 'coding-oa-uikit'; -import { getToolsRouter } from '@src/utils/getRoutePath'; +import { ToolInfoLink } from '@plat/modules'; import style from './style.scss'; @@ -48,12 +48,7 @@ const RuleDetail = (props: RuleDetailProps) => {
所属工具 {data?.checktool?.display_name ? ( - - {data?.checktool?.display_name} - + ) : ( -- )} diff --git a/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/index.tsx b/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/index.tsx index a58d44a80..b648e39ca 100644 --- a/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/index.tsx +++ b/web/packages/tca-analysis/src/modules/projects/metric/ccfiles/index.tsx @@ -9,7 +9,7 @@ * */ import React, { useEffect, useState } from 'react'; -import { Link, useHistory } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { cloneDeep, toNumber, omit, omitBy } from 'lodash'; import qs from 'qs'; @@ -18,7 +18,7 @@ import UserIcon from 'coding-oa-uikit/lib/icon/User'; import { getQuery } from '@src/utils'; import { DEFAULT_PAGER } from '@src/constant'; -import { getProjectRouter } from '@src/utils/getRoutePath'; +import { getProjectRouter, getProjectBlankRouter } from '@src/utils/getRoutePath'; import { getCCFilesIssues } from '@src/services/projects'; import { CC_CHANGE_TYPE_CHOICES } from '../../constants'; import Search from './search'; @@ -111,20 +111,20 @@ const CCFiles = (props: CCFilesProps) => { title="所属文件" dataIndex="file_path" render={(file_path, data: any) => ( -

{file_path.split('/').pop()}

{file_path}

- + )} /> { align='center' render={change_type => ( // todo: remove - - {CC_CHANGE_TYPE_CHOICES[change_type].label} - + + {CC_CHANGE_TYPE_CHOICES[change_type].label} + )} /> {

缺陷位置 -

+

{loadingIssueDetail ? ( ) : ( - <> - - - - - + <> + + - + + )}
{/* todo: remove Row Col */} @@ -165,10 +165,10 @@ const CcIssueDetail = () => { ) : ( - <> - - 未分配 - + <> + + 未分配 + )}
)} @@ -182,7 +182,7 @@ const CcIssueDetail = () => {

数量上表现为独立执行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。 -

+

@@ -193,7 +193,7 @@ const CcIssueDetail = () => {

圈复杂度大说明程序代码可能质量低且难于测试和维护, 根据经验,程序的可能错误和高的圈复杂度有着很大关系。 -

+

@@ -256,27 +256,27 @@ const CcIssueDetail = () => { > - 返回上一级 - + 返回上一级 +

路径: - {issueDetail.file_path - ? issueDetail.file_path.split('/').join(' / ') - : ''} + {issueDetail.file_path + ? issueDetail.file_path.split('/').join(' / ') + : ''}

仓库地址: - {`${curRepo.scm_url}.${curRepo.scm_type}`} + {`${curRepo.scm_url}.${curRepo.scm_type}`}

) : ( - + )} ); @@ -334,18 +334,18 @@ const CcIssueDetail = () => {

1. 代码库账号密码问题 -- 前往 - + 分支项目凭证管理页面 - - 查看 -

+ + 查看 +

2. 文件格式无法展示 -- 不支持展示.jar等二进制文件 -

+

3. 代码文件不存在 -- 可能为本地分析中间代码,请在本地环境下查看代码或联系管理员定位问题 -

+

)} @@ -367,17 +367,17 @@ const CcIssueDetail = () => { {loadingCodeContents ? ( renderLoading() ) : ( - <> - {renderCodeContent()} - {getCodeFileDetail !== '' && ( - - - -

{getCodeFileDetail}

- - - )} - + <> + {renderCodeContent()} + {getCodeFileDetail !== '' && ( + + + +

{getCodeFileDetail}

+ + + )} + )} ); diff --git a/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx b/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx index bfa34e162..aa811d5fe 100644 --- a/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx +++ b/web/packages/tca-analysis/src/modules/projects/metric/ccissues/issue-modal.tsx @@ -8,15 +8,14 @@ * 圈复杂度 - 方法列表详情 */ import React, { useEffect, useState, useRef } from 'react'; -import { Link } from 'react-router-dom'; import cn from 'classnames'; import AutoSizer from 'react-virtualized-auto-sizer'; import Highlight from 'react-highlight'; import { VariableSizeList as List } from 'react-window'; import { Modal, Button, Tooltip } from 'coding-oa-uikit'; import InfoCircle from 'coding-oa-uikit/lib/icon/InfoCircle'; -import LinkIcon from 'coding-oa-uikit/lib/icon/Link'; +import Copy from '@src/components/copy'; import PositionIcon from '@src/images/position.svg'; import { getCodeFile, getCCIssueDetail } from '@src/services/projects'; import Loading from '@src/components/loading'; @@ -127,11 +126,17 @@ const IssueModal = (props: IssueModalProps) => { centered title={
-

{detail.func_name}

- +

{detail.func_name} + + 文件路径:{detail.file_path} + + + +

+ {/* - -
+ */} + } width={1000} visible={visible} @@ -201,7 +206,7 @@ const IssueModal = (props: IssueModalProps) => { - + ); }; diff --git a/web/packages/tca-analysis/src/modules/projects/metric/ccissues/style.scss b/web/packages/tca-analysis/src/modules/projects/metric/ccissues/style.scss index 8ba122233..32b9d3045 100644 --- a/web/packages/tca-analysis/src/modules/projects/metric/ccissues/style.scss +++ b/web/packages/tca-analysis/src/modules/projects/metric/ccissues/style.scss @@ -468,6 +468,15 @@ vertical-align: inherit !important; } } + + .modal-desc{ + margin-left: 8px; + } + + .modal-desc, .copy-icon{ + font-size: 12px; + color: #606c80; + } } .wrapper { diff --git a/web/packages/tca-analysis/src/modules/projects/metric/dupfiles/index.tsx b/web/packages/tca-analysis/src/modules/projects/metric/dupfiles/index.tsx index e9be2de55..bfbd20520 100644 --- a/web/packages/tca-analysis/src/modules/projects/metric/dupfiles/index.tsx +++ b/web/packages/tca-analysis/src/modules/projects/metric/dupfiles/index.tsx @@ -8,7 +8,7 @@ * 度量结果 - 重复代码 - 列表页 */ import React, { useEffect, useState } from 'react'; -import { Link, useHistory } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { cloneDeep, toNumber, omit, omitBy, isString } from 'lodash'; import qs from 'qs'; import { Table, Avatar } from 'coding-oa-uikit'; @@ -17,7 +17,7 @@ import UserIcon from 'coding-oa-uikit/lib/icon/User'; import { getQuery } from '@src/utils'; import { getDupIssues } from '@src/services/projects'; import { DEFAULT_PAGER } from '@src/constant'; -import { getProjectRouter } from '@src/utils/getRoutePath'; +import { getProjectBlankRouter } from '@src/utils/getRoutePath'; import { DUP_FILE_STATE_OPTIONS } from '../../constants'; import Search from './search'; @@ -137,20 +137,20 @@ const DupFiles = (props: DupFilesProps) => { sorter sortOrder={sort.key === 'file_name' ? sort.order : undefined} render={(file_name, data: any) => ( -

{file_name}

{data.file_path}

- + )} /> { // color: 'uncompleted', // }, // ]; -
- {/* todo: remove */} - {/* + {/* todo: remove */} + {/* */} - - {' '} - {data.duplicate_rate}% - -
+ + {' '} + {data.duplicate_rate}% + + ) } /> diff --git a/web/packages/tca-analysis/src/modules/projects/nav.tsx b/web/packages/tca-analysis/src/modules/projects/nav.tsx index 012e72568..c3b1dfdcf 100644 --- a/web/packages/tca-analysis/src/modules/projects/nav.tsx +++ b/web/packages/tca-analysis/src/modules/projects/nav.tsx @@ -8,7 +8,7 @@ * 分支项目公共导航栏 */ import React, { useEffect, useState } from 'react'; -import { useParams, useHistory, Route, Link } from 'react-router-dom'; +import { useParams, useHistory, Route } from 'react-router-dom'; import { toNumber, get, find } from 'lodash'; import { Tabs, Button } from 'coding-oa-uikit'; @@ -16,7 +16,7 @@ import LinkIcon from 'coding-oa-uikit/lib/icon/Link'; import SelectDropdown from '@src/components/select-dropdown'; import { useStateStore } from '@src/context/store'; -import { getProjectRouter, getSchemeRouter } from '@src/utils/getRoutePath'; +import { getProjectRouter, getSchemeBlankRouter } from '@src/utils/getRoutePath'; import { PROJECT_ROUTE_PREFIX } from '@src/constant'; import { getProjectDetail, @@ -134,17 +134,17 @@ const Nav = ({ allSchemes, templates }: NavProps) => { history.push(`${getProjectRouter(orgSid, teamName, repoId, id)}/overview`); }} /> - 查看分析方案 - +
diff --git a/web/packages/tca-analysis/src/modules/projects/overview/utils.ts b/web/packages/tca-analysis/src/modules/projects/overview/utils.ts index 1d3c86e83..6f48cc62f 100644 --- a/web/packages/tca-analysis/src/modules/projects/overview/utils.ts +++ b/web/packages/tca-analysis/src/modules/projects/overview/utils.ts @@ -7,11 +7,20 @@ import get from 'lodash/get'; // 项目内 -import { SEVERITY_TYPE, SEVERITY_TYPE_TXT, STANDARD_TYPE, RISK_TYPE, RISK_TYPE_TXT, CATEGORY_TYPE_TXT, LINT_STATE_TYPE_TXT } from '@src/modules/projects/constants'; +import { + SEVERITY_TYPE, + SEVERITY_TYPE_TXT, + STANDARD_TYPE, + RISK_TYPE, + RISK_TYPE_TXT, + CATEGORY_TYPE_TXT, + LINT_STATE_TYPE_TXT, +} from '@src/modules/projects/constants'; import { formatDate } from '@src/utils'; import { CLOC_TYPE, CLOC_TYPE_TXT } from './codecloc'; import { CYC_TYPE, CYC_TYPE_TXT } from './codecc'; import { LINT_HISTORY_TYPE, LINT_HISTORY_TYPE_TXT } from './codelint'; +import { isEmpty } from 'lodash'; /** * 格式化时间,获取表格时间格式 @@ -60,10 +69,9 @@ export const formatLatestLintData = (latestLintData: any) => { * 获取圈复杂度简要数据,用于分支概览 * @param cycScans 圈复杂度历史详情数据 */ -export const getBriefCycData = (cycScans: any) => ( - cycScans.length > 0 - ? cycScans[0].custom_summary || cycScans[0].default_summary - : null); +export const getBriefCycData = (cycScans: any) => (cycScans.length > 0 + ? cycScans[0].custom_summary || cycScans[0].default_summary + : null); /** * 获取重复代码简要数据,用于分支概览 @@ -115,7 +123,10 @@ export const getLintLineChartData = (scans: any) => { }); } }); - return { [LINT_HISTORY_TYPE.TOTAL]: rowsTotal.reverse(), [LINT_HISTORY_TYPE.UNDEAL]: rowsPending }; + return { + [LINT_HISTORY_TYPE.TOTAL]: rowsTotal.reverse(), + [LINT_HISTORY_TYPE.UNDEAL]: rowsPending, + }; } return { [LINT_HISTORY_TYPE.TOTAL]: [], [LINT_HISTORY_TYPE.UNDEAL]: [] }; }; @@ -130,15 +141,26 @@ export const getLintPieChartData = (scans: any) => { totalData: {}, }; if (scans && scans.length > 0) { - let scanFlag = false; let totalFlag = false; + let scanFlag = false; + let totalFlag = false; Object.values(scans).forEach((value: any) => { - const { scan_summary: scanSummary, total_summary: totalSummary, status } = value; + const { + scan_summary: scanSummary, + total_summary: totalSummary, + status, + } = value; // 如果成功,则取成功那次结果,否则取最近一次成功有数据的 - if (!scanFlag && ((status >= 0 && status < 100) || JSON.stringify(scanSummary) !== '{}')) { + if ( + !scanFlag + && ((status >= 0 && status < 100) || JSON.stringify(scanSummary) !== '{}') + ) { summaryDict.undealData = scanSummary; scanFlag = true; } - if (!totalFlag && ((status >= 0 && status < 100) || JSON.stringify(totalSummary) !== '{}')) { + if ( + !totalFlag + && ((status >= 0 && status < 100) || JSON.stringify(totalSummary) !== '{}') + ) { summaryDict.totalData = totalSummary; totalFlag = true; } @@ -217,7 +239,7 @@ export const getCyCLineChartData = (scans: any, standard: string) => { const rowsTotal: Array = []; const rowsExcess: Array = []; scans.forEach((scan: any) => { - const summary = standard === STANDARD_TYPE.CUSTOM && scan.custom_summary + const summary = standard === STANDARD_TYPE.CUSTOM && scan.custom_summary ? scan.custom_summary : scan.default_summary; if (summary) { @@ -228,10 +250,17 @@ export const getCyCLineChartData = (scans: any, standard: string) => { num: summary.under_cc_func_count + summary.over_cc_func_count, type: CYC_TYPE_TXT.TOTAL, }); - rowsExcess.push({ date, num: summary.over_cc_func_count, type: CYC_TYPE_TXT.OVER }); + rowsExcess.push({ + date, + num: summary.over_cc_func_count, + type: CYC_TYPE_TXT.OVER, + }); } }); - return { [CYC_TYPE.TOTAL]: rowsTotal.reverse(), [CYC_TYPE.OVER]: rowsExcess }; + return { + [CYC_TYPE.TOTAL]: rowsTotal.reverse(), + [CYC_TYPE.OVER]: rowsExcess, + }; } return { [CYC_TYPE.TOTAL]: [], [CYC_TYPE.OVER]: [] }; }; @@ -242,7 +271,7 @@ export const getCyCLineChartData = (scans: any, standard: string) => { * @param standard 标准 */ export const getCycPieChartData = (scans: any, standard: string) => { - Object.values(scans).forEach((data: any) => { + for (const data of scans) { if (data.default_summary) { const summary = standard === STANDARD_TYPE.CUSTOM && data.custom_summary ? data.custom_summary @@ -252,7 +281,7 @@ export const getCycPieChartData = (scans: any, standard: string) => { { type: CYC_TYPE_TXT.EXCESS, value: summary.under_cc_func_count }, ]; } - }); + } return []; }; @@ -273,10 +302,26 @@ export const getDupLineChartData = (scans: any, standard: string) => { : scan.default_summary; if (summary) { const date = formatChartDate(scan.scan_time); - rowsExhi.push({ date, num: get(summary, 'exhi_risk.file_count', 0), type: RISK_TYPE_TXT.EXHI }); - rowsHigh.push({ date, num: get(summary, 'high_risk.file_count', 0), type: RISK_TYPE_TXT.HIGH }); - rowsMidd.push({ date, num: get(summary, 'midd_risk.file_count', 0), type: RISK_TYPE_TXT.MIDD }); - rowsLow.push({ date, num: get(summary, 'low_risk.file_count', 0), type: RISK_TYPE_TXT.LOW }); + rowsExhi.push({ + date, + num: get(summary, 'exhi_risk.file_count', 0), + type: RISK_TYPE_TXT.EXHI, + }); + rowsHigh.push({ + date, + num: get(summary, 'high_risk.file_count', 0), + type: RISK_TYPE_TXT.HIGH, + }); + rowsMidd.push({ + date, + num: get(summary, 'midd_risk.file_count', 0), + type: RISK_TYPE_TXT.MIDD, + }); + rowsLow.push({ + date, + num: get(summary, 'low_risk.file_count', 0), + type: RISK_TYPE_TXT.LOW, + }); } }); return { @@ -286,7 +331,12 @@ export const getDupLineChartData = (scans: any, standard: string) => { [RISK_TYPE.LOW]: rowsLow.reverse(), }; } - return { [RISK_TYPE.EXHI]: [], [RISK_TYPE.HIGH]: [], [RISK_TYPE.MIDD]: [], [RISK_TYPE.LOW]: [] }; + return { + [RISK_TYPE.EXHI]: [], + [RISK_TYPE.HIGH]: [], + [RISK_TYPE.MIDD]: [], + [RISK_TYPE.LOW]: [], + }; }; /** @@ -295,7 +345,12 @@ export const getDupLineChartData = (scans: any, standard: string) => { * @param standard 标准 */ export const getDupBarChartData = (scans: any, standard: string) => { - Object.values(scans).forEach((data: any) => { + if (isEmpty(scans)) { + return []; + } + + // for (const data of scans) { + for (const data of scans) { if (data.default_summary) { const summary = standard === STANDARD_TYPE.CUSTOM && data.custom_summary ? data.custom_summary @@ -319,11 +374,11 @@ export const getDupBarChartData = (scans: any, standard: string) => { }, ]; } - }); - return []; + return []; + } + // }); }; - /** * 获取代码统计表格数据 * @param scans 代码统计数据 @@ -337,10 +392,26 @@ export const getClocLineChartData = (scans: any) => { scans.forEach((item: any) => { if (item) { const date = formatChartDate(item.scan_time); - rowsTotal.push({ date, num: item.total_line_num, type: CLOC_TYPE_TXT.TOTAL }); - rowsCode.push({ date, num: item.code_line_num, type: CLOC_TYPE_TXT.CODE }); - rowsComment.push({ date, num: item.comment_line_num, type: CLOC_TYPE_TXT.COMMENT }); - rowsBlank.push({ date, num: item.blank_line_num, type: CLOC_TYPE_TXT.BLANK }); + rowsTotal.push({ + date, + num: item.total_line_num, + type: CLOC_TYPE_TXT.TOTAL, + }); + rowsCode.push({ + date, + num: item.code_line_num, + type: CLOC_TYPE_TXT.CODE, + }); + rowsComment.push({ + date, + num: item.comment_line_num, + type: CLOC_TYPE_TXT.COMMENT, + }); + rowsBlank.push({ + date, + num: item.blank_line_num, + type: CLOC_TYPE_TXT.BLANK, + }); } }); return { @@ -350,7 +421,12 @@ export const getClocLineChartData = (scans: any) => { [CLOC_TYPE.BLANK]: rowsBlank.reverse(), }; } - return { [CLOC_TYPE.TOTAL]: [], [CLOC_TYPE.CODE]: [], [CLOC_TYPE.COMMENT]: [], [CLOC_TYPE.BLANK]: [] }; + return { + [CLOC_TYPE.TOTAL]: [], + [CLOC_TYPE.CODE]: [], + [CLOC_TYPE.COMMENT]: [], + [CLOC_TYPE.BLANK]: [], + }; }; /** @@ -359,16 +435,20 @@ export const getClocLineChartData = (scans: any) => { */ export const getClocPieChartData = (scans: any) => { if (scans && scans.length > 0) { - return [{ - type: CLOC_TYPE_TXT.CODE, - value: scans[0].code_line_num, - }, { - type: CLOC_TYPE_TXT.COMMENT, - value: scans[0].comment_line_num, - }, { - type: CLOC_TYPE_TXT.BLANK, - value: scans[0].blank_line_num, - }]; + return [ + { + type: CLOC_TYPE_TXT.CODE, + value: scans[0].code_line_num, + }, + { + type: CLOC_TYPE_TXT.COMMENT, + value: scans[0].comment_line_num, + }, + { + type: CLOC_TYPE_TXT.BLANK, + value: scans[0].blank_line_num, + }, + ]; } return []; }; @@ -380,7 +460,7 @@ export const getClocPieChartData = (scans: any) => { */ export const getMineFormatData = (count: any, total: any) => { let progress = 0; - if (typeof (count) === 'number' && typeof (total) === 'number') { + if (typeof count === 'number' && typeof total === 'number') { progress = total === 0 ? 0 : count / total; } else { count = '-'; diff --git a/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx b/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx index cff31b394..b11914667 100644 --- a/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx +++ b/web/packages/tca-analysis/src/modules/projects/project/first-modal.tsx @@ -50,10 +50,12 @@ const FirstModal = (props: FirstModalProps) => { useEffect(() => { (async () => { - setTags(get(await getTags(orgSid), 'results', [])); - setLanguages(get(await getLanguages(), 'results', [])); + if (visible) { + setTags(get(await getTags(orgSid), 'results', [])); + setLanguages(get(await getLanguages(), 'results', [])); + } })(); - }, []); + }, [visible]); const onFinish = (data: any) => { const { funcList = [] } = data; diff --git a/web/packages/tca-analysis/src/modules/projects/project/project-list.tsx b/web/packages/tca-analysis/src/modules/projects/project/project-list.tsx index 581d7d35a..ec4621faa 100644 --- a/web/packages/tca-analysis/src/modules/projects/project/project-list.tsx +++ b/web/packages/tca-analysis/src/modules/projects/project/project-list.tsx @@ -11,18 +11,18 @@ import React, { useEffect, useState } from 'react'; import { Link, useHistory, useParams } from 'react-router-dom'; import { Table, Button, Input, message } from 'coding-oa-uikit'; -import { pickBy, isNumber, get, toNumber, find } from 'lodash'; +import { pickBy, isNumber, get, toNumber } from 'lodash'; import qs from 'qs'; -import { useSelector } from 'react-redux'; +import { useLoginUserIsAdmin } from '@plat/hooks'; import SelectDropdown from '../../../components/select-dropdown'; import Tips from '@src/components/tips'; import { useStateStore } from '@src/context/store'; import { DEFAULT_PAGER } from '@src/constant'; import { useQuery } from '@src/utils/hooks'; -import { getProjectRouter, getSchemeRouter } from '@src/utils/getRoutePath'; +import { getProjectRouter, getSchemeBlankRouter } from '@src/utils/getRoutePath'; import { getProjects, delProject } from '@src/services/projects'; -import { getMembers } from '@src/services/common'; +import { getRepoMembers } from '@src/services/repos'; import ScanModal from './scan-modal'; import NewProjectModal from './new-project-modal'; @@ -54,14 +54,6 @@ const ProjectList = (props: ProjectListProps) => { const [curProjId, setCurProjId] = useState(null); const [reload, setReload] = useState(false); const [hoverRowId, setHoverRowId] = useState(undefined); - - // 判断是否有权限删除分支项目 - const APP = useSelector((state: any) => state.APP); - const isSuperuser = get(APP, 'user.is_superuser', false); // 当前用户是否是超级管理员 - const userName = get(APP, 'user.username', null); - const [admins, setAdmins] = useState([]); - const isAdmin = !!find(admins, { username: userName }); // 当前用户是否是代码库管理员 - const deletable = isAdmin || isSuperuser; // 删除权限 const [deleteVisible, setDeleteVisible] = useState(false); const [searchParams, setSearchParams] = useState({ @@ -70,6 +62,8 @@ const ProjectList = (props: ProjectListProps) => { }); const { pageSize, pageStart, count } = pager; + const [admins, setAdmins] = useState([]); + const isAdmin = useLoginUserIsAdmin(admins); useEffect(() => { repoId && getListData(); @@ -85,8 +79,8 @@ const ProjectList = (props: ProjectListProps) => { useEffect(() => { // 获取代码库成员 if (repoId) { - getMembers(orgSid, teamName, repoId).then((response) => { - setAdmins(response.admins); + getRepoMembers(orgSid, teamName, repoId).then((res: any) => { + setAdmins(res.admins?.map((userinfo: any) => userinfo.username) || []); }); } }, [repoId]); @@ -107,7 +101,7 @@ const ProjectList = (props: ProjectListProps) => { ...params, scan_scheme__status: 1, }).then((response) => { - history.push(`${location.pathname}?${qs.stringify(params)}`); + history.replace(`${location.pathname}?${qs.stringify(params)}`); setList(response.results); setPager({ ...pager, count: response.count }); }); @@ -229,18 +223,18 @@ const ProjectList = (props: ProjectListProps) => { title="分析方案" dataIndex={['scan_scheme', 'name']} render={(name, data: any) => ( - {name} - + )} /> @@ -282,7 +276,7 @@ const ProjectList = (props: ProjectListProps) => { > 分析历史 - {hoverRowId === id && deletable && ( + {hoverRowId === id && isAdmin && ( onDeleteProject(id)} diff --git a/web/packages/tca-analysis/src/modules/projects/scan-history/detail.tsx b/web/packages/tca-analysis/src/modules/projects/scan-history/detail.tsx index 84a968e14..704d0de60 100644 --- a/web/packages/tca-analysis/src/modules/projects/scan-history/detail.tsx +++ b/web/packages/tca-analysis/src/modules/projects/scan-history/detail.tsx @@ -19,7 +19,7 @@ import AttentionRed from 'coding-oa-uikit/lib/icon/AttentionRed'; import Attention from 'coding-oa-uikit/lib/icon/Attention'; import CloudDownload from 'coding-oa-uikit/lib/icon/CloudDownload'; -import { getProjectRouter, getSchemeRouter } from '@src/utils/getRoutePath'; +import { getProjectRouter, getSchemeBlankRouter } from '@src/utils/getRoutePath'; import { getScanDetail, getTaskDetail, getLog } from '@src/services/projects'; import { getQuery, formatDateTime } from '@src/utils'; @@ -271,7 +271,7 @@ const ScanDetail = () => { { + const { auth_type: authType, scm_oauth: scmOAuth, scm_ssh: scmSSH, scm_account: scmAccount } = scmAuth; + + let txt = ''; + if (authType === AuthTypeEnum.OAUTH && scmOAuth) { + txt = `${AuthTypeTxtEnum.OAUTH}: ${scmOAuth.user} (由 ${scmOAuth.user?.nickname} 在 ${scmOAuth.auth_origin} 创建)`; + } else if (authType === AuthTypeEnum.SSH && scmSSH) { + txt = `${AuthTypeTxtEnum.SSH}: ${scmSSH.name} (由 ${scmSSH.user?.nickname} 在 ${scmSSH.auth_origin} 创建)`; + } else if (authType === AuthTypeEnum.HTTP && scmAccount) { + txt = `${AuthTypeTxtEnum.HTTP}: ${scmAccount.scm_username} (由 ${scmAccount.user?.nickname} 在 ${scmAccount.auth_origin} 创建)`; + } + if (txt === '') { + return }>无凭证,请尽快添加授权凭证; + } + return {txt}; +}; + +interface AuthorityConfigProps { + repoInfo: any; + visible: boolean; + orgSid: string; + teamName: string; + onCancel: () => void; + callback?: () => void; +} + +const AuthorityConfig = ({ orgSid, teamName, repoInfo, visible, onCancel, callback }: AuthorityConfigProps) => { + const [form] = Form.useForm(); + + const onFinish = ({ scm }: any) => { + if (scm) { + const [authType, id] = scm.split('#') ?? []; + const scmAuth = SCM_MAP[authType as AuthTypeEnum]; + putRepoAuth(orgSid, teamName, repoInfo?.id, { + auth_type: authType, + [scmAuth]: id, + }).then(() => { + message.success('代码库凭证切换成功'); + callback?.(); + onReset(); + }); + } else { + onCancel(); + } + }; + + const onReset = () => { + form.resetFields(); + onCancel(); + }; + + + return ( + { + form.validateFields().then(onFinish); + }} + > +
+ {repoInfo?.scm_auth && + + } + {/* */} + + +
+ ); +}; + +export default AuthorityConfig; + diff --git a/web/packages/tca-analysis/src/modules/repos/constant.tsx b/web/packages/tca-analysis/src/modules/repos/constant.tsx new file mode 100644 index 000000000..a3068cb6d --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/constant.tsx @@ -0,0 +1,7 @@ + +// 仓库类型 +export const REPO_TYPE = { + GIT: 'git', + SVN: 'svn', +}; + diff --git a/web/packages/tca-analysis/src/modules/repos/constants.ts b/web/packages/tca-analysis/src/modules/repos/constants.ts deleted file mode 100644 index 0a0c6254b..000000000 --- a/web/packages/tca-analysis/src/modules/repos/constants.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - - -/** - * 代码库页面的tab 切换 - */ -export const REPO_TAB_TYPE = { - MEMBER: 'member', - AUTH: 'auth', - OVERVIEW: 'overview', -}; - -export const REPO_TAB_TYPE_TXT = { - MEMBER: '成员权限', - AUTH: '认证方式', - OVERVIEW: '仓库信息', -}; - -export const REPO_TAB_OPTIONS = [ - { - label: REPO_TAB_TYPE_TXT.MEMBER, - value: REPO_TAB_TYPE.MEMBER, - }, - { - label: REPO_TAB_TYPE_TXT.AUTH, - value: REPO_TAB_TYPE.AUTH, - }, - { - label: REPO_TAB_TYPE_TXT.OVERVIEW, - value: REPO_TAB_TYPE.OVERVIEW, - }, -]; - -/** - * 凭证类型 - */ -export const AUTH_TYPE = { - HTTP: 'password', - SSH: 'ssh_token', - OAUTH: 'oauth', -}; - -export const AUTH_TYPE_TXT = { - HTTP: '用户名 + 密码', - SSH: 'ssh', - OAUTH: 'OAuth', -}; - -export const AUTH_TYPE_OPTIONS = [ - { - label: AUTH_TYPE_TXT.HTTP, - value: AUTH_TYPE.HTTP, - }, - { - label: AUTH_TYPE_TXT.SSH, - value: AUTH_TYPE.SSH, - }, -]; - -export const AUTH_TYPE_CHOICES = { - [AUTH_TYPE.HTTP]: AUTH_TYPE_TXT.HTTP, -}; - -/** - * 仓库类型 - */ -export const REPO_TYPE = { - GIT: 'git', - SVN: 'svn', -}; - -export const REPO_TYPE_OPTIONS = [REPO_TYPE.GIT, REPO_TYPE.SVN]; - -export const DEFAULT_SCM_PLATFORM = [ - { - scm_platform: 2, - scm_platform_name: 'tgitsaas', - }, - { - scm_platform: 4, - scm_platform_name: 'github', - }, - { - scm_platform: 5, - scm_platform_name: 'gitee', - }, - { - scm_platform: 6, - scm_platform_name: 'gitlab', - }, -]; - -export const SCM_PLATFORM = { - 1: '其他', - 2: '腾讯工蜂', - 3: 'CODING', - 4: 'GitHub', - 5: 'Gitee', - 6: 'GitLab', -}; diff --git a/web/packages/tca-analysis/src/modules/repos/create-repo.tsx b/web/packages/tca-analysis/src/modules/repos/create-repo.tsx new file mode 100644 index 000000000..b6372d4a4 --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/create-repo.tsx @@ -0,0 +1,163 @@ +/** + * 代码库登记-弹框 + */ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { Modal, Form, Input, Select, message } from 'coding-oa-uikit'; + +import AuthFormItem from '@tencent/micro-frontend-shared/tca/user-auth/auth-form-item'; +import { AuthTypeEnum, SCM_MAP } from '@tencent/micro-frontend-shared/tca/user-auth/constant'; +import { userAuthAPI } from '@plat/api'; +// import Authority from '@src/components/authority'; +// import { SCM_MAP } from '@src/components/authority/constants'; +import { postRepo } from '@src/services/repos'; +import { getRepos } from '@src/services/common'; +import { getAnalysisBaseRouter, getUserAuthBlankRouter } from '@src/utils/getRoutePath'; +import { RestfulListAPIParams } from '@src/types'; +import { getRepoName } from '@src/utils'; +import { REPO_TYPE } from './constant'; + +const { Option } = Select; + +interface CreateRepoProps { + orgSid: string; + teamName: string; + visible: boolean; + onCancel: () => void; + callback: () => void; +} + +const CreateRepo = (props: CreateRepoProps) => { + const [form] = Form.useForm(); + const { orgSid, teamName, visible, onCancel, callback } = props; + + const history = useHistory(); + + const onRepoNameFocus = () => { + const url = form.getFieldValue('scm_url'); + if (url) { + form.setFieldsValue({ + name: getRepoName(url), + }); + } + }; + + const onFinish = (formData: any) => { + const data = { + ...formData, + created_from: 'codedog_web', + }; + if (data.scm) { + const [authType, id] = data?.scm?.split('#') ?? []; + delete data.scm; + data.scm_auth = { auth_type: authType }; + if (SCM_MAP[authType as AuthTypeEnum]) { + data.scm_auth[SCM_MAP[authType as AuthTypeEnum]] = id; + } + } + + /** 判断代码库是否已存在,存在则提供跳转 */ + getRepos(orgSid, teamName, { limit: 1, scm_url: data.scm_url }).then(({ results }: RestfulListAPIParams) => { + const [repo] = results; + if (repo) { + Modal.confirm({ + title: '代码库已登记', + content: '该代码库已在腾讯代码分析平台完成过登记,可点击直接访问', + okText: '直接访问', + cancelText: '返回修改', + onOk() { + history.push(`${getAnalysisBaseRouter(orgSid, teamName)}/repos/${repo.id}/projects`); + }, + }); + } else { + postRepo(orgSid, teamName, data).then(() => { + message.success('代码库登记成功'); + callback?.(); + onReset(); + }); + } + }); + }; + + const onReset = () => { + form.resetFields(); + onCancel(); + }; + + return ( + { + form.validateFields().then(onFinish); + }} + // confirmLoading={loading} + > +
+ + + + + + + + + + + + + + {/* */} + + +
+ ); +}; + +export default CreateRepo; diff --git a/web/packages/tca-analysis/src/modules/repos/create.tsx b/web/packages/tca-analysis/src/modules/repos/create.tsx deleted file mode 100644 index 7d7753ba9..000000000 --- a/web/packages/tca-analysis/src/modules/repos/create.tsx +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import React, { useEffect, useState } from 'react'; -import { useHistory, useParams } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import { Form, Select, Input, Button, message } from 'coding-oa-uikit'; -// Radio -import { get, isEmpty, filter } from 'lodash'; -import PlusIcon from 'coding-oa-uikit/lib/icon/Plus'; -import RefreshIcon from 'coding-oa-uikit/lib/icon/Refresh'; - -// 项目内 -import { useStateStore, useDispatchStore } from '@src/context/store'; -import { SET_CUR_REPO, SET_REPOS } from '@src/context/constant'; -import { getScmAccounts, postRepo, getSSHInfo, getOAuthInfo, getPlatformStatus } from '@src/services/repos'; -import { getPCAuthRouter, getRepoRouter } from '@src/modules/repos/routes'; -import { AUTH_TYPE, AUTH_TYPE_TXT, REPO_TYPE, REPO_TYPE_OPTIONS, SCM_PLATFORM } from './constants'; -import s from './style.scss'; - -const { Option, OptGroup } = Select; - -// TODO: 暂时不做组织处理 -// const REPO_ORG_TYPE = { -// PERSONAL: 1, -// ORG: 2, -// }; - -// const REPO_ORG_TYPE_OPTIONS = [ -// { -// label: t('个人'), -// value: REPO_ORG_TYPE.PERSONAL, -// }, -// { -// label: t('组织'), -// value: REPO_ORG_TYPE.ORG, -// }, -// ]; - -const layout = { - labelCol: { span: 3 }, -}; - -const Create = () => { - const [form] = Form.useForm(); - const history = useHistory(); - const { repos } = useStateStore(); - const dispatch = useDispatchStore(); - const { t } = useTranslation(); - - const { orgSid, teamName }: any = useParams(); - // const [allAuthList, setAllAuthList] = useState>([]); - const [sshAuthList, setSshAuthList] = useState([]); - const [httpAuthList, setHttpAuthList] = useState([]); - const [oauthAuthList, setOauthAuthList] = useState([]); - const [submitLoading, setSubmitLoading] = useState(false); - const [reload, setReload] = useState(false); - const [authLoading, setAuthLoading] = useState(false); - - useEffect(() => { - getAuth(); - }, [reload]); - - const getAuth = () => { - setAuthLoading(true); - Promise.all([ - getSSHInfo().then(r => r.results || []), - getScmAccounts().then(r => r.results || []), - getOAuthInfo().then(r => r.results || []), - getPlatformStatus().then(r => r || []), - ]) - .then((result) => { - const activeOauth = filter( - result[2].map((item: any) => ({ - ...item, - platform_status: get(result[3], item.scm_platform_name, [false]), - })), - 'platform_status', - ); - // HTTP 和 SSH ID可能重复 - setSshAuthList(result[0]?.map((item: any) => ({ - ...item, - authId: `${AUTH_TYPE.SSH}#${item.id}`, - }))); - setHttpAuthList(result[1].map((item: any) => ({ - ...item, - authId: `${AUTH_TYPE.HTTP}#${item.id}`, - }))); - setOauthAuthList(activeOauth.map((item: any) => ({ - ...item, - authId: `${AUTH_TYPE.OAUTH}#${item.id}`, - }))); - }) - .finally(() => { - setAuthLoading(false); - }); - }; - - const onFinish = (values: any) => { - const { name, scm_auth_id: scmAuthId, address, symbol } = values; - const [authType, id] = scmAuthId?.split('#') ?? []; - const data = { - name, - create_from: 'codedog_web', - repo_type: values.repo_type, - scm_auth: { - auth_type: authType, - }, - ...address, - }; - - switch (data.scm_auth.auth_type) { - case AUTH_TYPE.HTTP: - data.scm_auth.scm_account = id; - break; - case AUTH_TYPE.SSH: - data.scm_auth.scm_ssh = id; - break; - case AUTH_TYPE.OAUTH: - data.scm_auth.scm_oauth = id; - break; - } - - if (symbol) { - data.symbol = symbol; - } - setSubmitLoading(true); - postRepo(orgSid, teamName, data) - .then((response) => { - message.success(t('登记代码库成功')); - history.replace(getRepoRouter(orgSid, teamName, response.id)); - dispatch({ - type: SET_REPOS, - payload: [...repos, response], - }); - dispatch({ - type: SET_CUR_REPO, - payload: response, - }); - }) - .finally(() => { - setSubmitLoading(false); - }); - }; - - const onReset = () => { - form.resetFields(); - }; - - const initialValues = { - address: { - scm_type: REPO_TYPE.GIT, - }, - // repo_type: REPO_ORG_TYPE.PERSONAL, - }; - - /** - * 仓库别名获取焦点时将代码库地址后缀赋予仓库别名 - */ - const onRepoNameFocus = () => { - const address = form.getFieldValue('address'); - const scmUrl = get(address, 'scm_url', null); - if (scmUrl) { - const name = scmUrl.split('/'); - form.setFieldsValue({ - name: name[name.length - 1], - }); - } - }; - - return ( -
-
{t('代码库登记')}
-
- {/* - - {REPO_ORG_TYPE_OPTIONS.map(item => ( - - {item.label} - - ))} - - */} - - - - - - - - - -

- 由于境外代码托管平台可能存在网络问题,建议使用境内托管平台的代码库 -

-
-
- prevValues.scm_auth_id !== curValues.scm_auth_id} - > - {({ getFieldValue }) => { - const scmAuth = getFieldValue('scm_auth_id'); - return scmAuth?.startsWith('ssh_token') && ( - <> - - - - - ); - }} - - - - - - - - - - - -
- - - -
-
-
- ); -}; - -export default Create; diff --git a/web/packages/tca-analysis/src/modules/repos/edit-modal.tsx b/web/packages/tca-analysis/src/modules/repos/edit-modal.tsx new file mode 100644 index 000000000..82adca4de --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/edit-modal.tsx @@ -0,0 +1,102 @@ +/** + * 编辑仓库信息 + */ +import React, { useEffect, useState } from 'react'; +import { Modal, Form, Input, Space, Button, message } from 'coding-oa-uikit'; +import { useLoginUserIsAdmin } from '@plat/hooks'; +import { getRepoName } from '@src/utils'; +import { putRepo, getRepoMembers, delRepo } from '@src/services/repos'; + +interface EditModalProps { + orgSid: string; + teamName: string; + visible: boolean; + repoInfo: any; + onCancel: () => void; + callback: (deletedRepo: boolean) => void; +} + +const EditModal = (props: EditModalProps) => { + const { orgSid, teamName, visible, repoInfo, onCancel, callback } = props; + const [form] = Form.useForm(); + const [admins, setAdmins] = useState([]); + const isAdmin = useLoginUserIsAdmin(admins); + + useEffect(() => { + if (visible) { + form.resetFields(); + getRepoMembers(orgSid, teamName, repoInfo.id).then((res: any) => { + setAdmins(res.admins?.map((userinfo: any) => userinfo.username) || []); + }); + } + }, [visible]); + + const onConfirm = () => { + form.validateFields().then((formData) => { + putRepo(orgSid, teamName, repoInfo.id, { + ...repoInfo, + ...formData, + }).then(() => { + message.success('修改成功'); + callback?.(false); + onCancel(); + }); + }); + }; + + const onDel = () => { + Modal.confirm({ + title: `是否确认删除代码库【${repoInfo.name || getRepoName(repoInfo.scm_url)}】?`, + onOk() { + delRepo(orgSid, teamName, repoInfo.id).then(() => { + message.success('已删除代码库'); + callback?.(true); + onCancel(); + }); + }, + }); + }; + + return ( + + + + { + isAdmin && ( + + ) + } + + )} + > +
+ + + + + + +
+
+ ); +}; + +export default EditModal; diff --git a/web/packages/tca-analysis/src/modules/repos/hooks.ts b/web/packages/tca-analysis/src/modules/repos/hooks.ts deleted file mode 100644 index 15e56df0b..000000000 --- a/web/packages/tca-analysis/src/modules/repos/hooks.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import { useEffect } from 'react'; -// 项目内 -import { getRepo } from '@src/services/repos'; -import { getMembers } from '@src/services/common'; -import { useStateStore, useDispatchStore } from '@src/context/store'; -import { SET_CUR_REPO, SET_CUR_REPO_MEMBER } from '@src/context/constant'; - -/** - * 初始化代码库相关信息,用户store设置代码库信息和成员等初始化配置 - * @param repoId 代码库ID - */ -export const useInitRepo = (orgSid: string, teamName: string, repoId: number | string) => { - const { curRepo, curRepoMember } = useStateStore(); - const dispatch = useDispatchStore(); - - useEffect(() => { - if (repoId) { - // 获取代码库信息 - getRepo(orgSid, teamName, repoId).then((response) => { - dispatch({ - type: SET_CUR_REPO, - payload: response, - }); - }); - // 获取代码库成员 - getMembers(orgSid, teamName, repoId).then((response) => { - const { admins = [], users = [] } = response; - dispatch({ - type: SET_CUR_REPO_MEMBER, - payload: { - admins, - users, - }, - }); - }); - } - }, [repoId]); - - return { curRepo, curRepoMember }; -}; diff --git a/web/packages/tca-analysis/src/modules/repos/index.tsx b/web/packages/tca-analysis/src/modules/repos/index.tsx index ee436855a..eda0098b3 100644 --- a/web/packages/tca-analysis/src/modules/repos/index.tsx +++ b/web/packages/tca-analysis/src/modules/repos/index.tsx @@ -1,70 +1,136 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== +/** + * 仓库登记入口文件 + */ +import React, { useState, useEffect } from 'react'; +import { useHistory, useParams } from 'react-router-dom'; +import qs from 'qs'; +import { toNumber, omit } from 'lodash'; -import React, { useEffect } from 'react'; -import { useParams, useHistory, useRouteMatch } from 'react-router-dom'; -import { get } from 'lodash'; +// import { Button, Form, Input, Checkbox } from 'coding-oa-uikit'; +import { Button, Form, Input } from 'coding-oa-uikit'; +import Filter from '@src/components/filter'; -// 项目内 -import { useStateStore } from '@src/context/store'; -import { getReposRouter } from '@src/utils/getRoutePath'; +import { getQuery } from '@src/utils'; +import { getRepos } from '@src/services/common'; +import { DEFAULT_PAGER } from '@src/constant'; +import { CLOSE_REPO_MEMBER_CONF } from '@plat/modules'; -// 模块内 -import ReposList from './repo-list'; -import { getRepoRouter } from './routes'; +import List from './list'; +import CreateRepoModal from './create-repo'; +import style from './style.module.scss'; const Repos = () => { + const [form] = Form.useForm(); const history = useHistory(); const { orgSid, teamName }: any = useParams(); - const { url } = useRouteMatch(); - const { curRepo, repos } = useStateStore(); + const query = getQuery() as any; + const [list, setList] = useState([]); + const [loading, setLoading] = useState(false); + const [count, setCount] = useState(DEFAULT_PAGER.count); + const [visible, setVisible] = useState(false); - /** - * 重定向到对应代码库。如果store中存在代码库且在代码库列表中,则跳转到对应代码库,否则选取第一个代码库跳转 - * @param repoList 代码库列表 - */ - // const replaceToRepo = () => { - // if (reposLoading) { - // if (repos.length > 0) { - // // 存在登记的代码库,且repoId不存在或不在repos内则重定向到第一项 - // if (!repoId || !repos.some((item: any) => item.id === repoId)) { - // if (!repos.some((item: any) => item.id === curRepo.id)) { - // history.replace(getRepoRouter(orgSid, teamName, repos[0].id)); - // } else { - // history.replace(getRepoRouter(orgSid, teamName, curRepo.id)); - // } - // } else { - // // 存在repoId则直接采用该路由 - // } - // } else { - // // 待移除 - // // 未登记代码库,则重定向到欢迎页 - // history.replace(`${getReposRouter(orgSid, teamName)}/welcome`); - // } - // } - // }; - - // useEffect(() => { - // // 当处于xxx/repos路由时,进行重定向到对应代码库 - // if (!reposLoading && url === getReposRouter(orgSid, teamName)) { - // replaceToRepo(); - // } - // }); + const pageSize = toNumber(query.limit) || DEFAULT_PAGER.pageSize; + const pageStart = toNumber(query.offset) || DEFAULT_PAGER.pageStart; + const searchParams: any = omit(query, ['offset', 'limit']); useEffect(() => { - // 当处于xxx/repos路由时,且当前repo存在repos中,且当前路由的项目标识与当前代码库项目标识相同,则进行重定向到对应代码库 - if ( - url === getReposRouter(orgSid, teamName) - && repos.some((item: any) => item.id === curRepo.id) - && get(curRepo, 'project_team.name') === teamName - ) { - history.replace(`${getRepoRouter(orgSid, teamName, curRepo.id)}`); - } - }); + getListData(); + }, [orgSid, teamName]); + + const getListData = (offset = pageStart, limit = pageSize, otherParams = searchParams) => { + const params = { + limit, + offset, + scope: 'related_me', // 默认展示有权限的代码库 + ...otherParams, + }; + setLoading(true); + getRepos(orgSid, teamName, params).then((response) => { + history.replace(`?${qs.stringify(params)}`); + setCount(response.count); + setList(response.results || []); + }) + .finally(() => { + setLoading(false); + form.resetFields(); + }); + }; + + const onChangePageSize = (page: number, pageSize: number) => { + getListData((page - 1) * pageSize, pageSize); + }; - return ; + const onChangeSearchParams = (type: string, value: any) => { + getListData(DEFAULT_PAGER.pageStart, pageSize, { + ...searchParams, + [type]: value, + }); + }; + + return ( +
+
+ 仓库登记 +
+ +
+
+
+ + + { + onChangeSearchParams('scm_url_or_name', value); + }} + /> + + {/* + { + onChangeSearchParams('scope', e.target.checked ? 'subscribed' : 'related_me'); + }} + >我关注的 + */} + +
+
+ +
+ setVisible(false)} + callback={() => getListData(DEFAULT_PAGER.pageStart)} + /> +
+ ); }; + export default Repos; diff --git a/web/packages/tca-analysis/src/modules/repos/list.tsx b/web/packages/tca-analysis/src/modules/repos/list.tsx new file mode 100644 index 000000000..383e2be0b --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/list.tsx @@ -0,0 +1,333 @@ +/** + * 代码库列表 + */ +import React, { useState } from 'react'; +import cn from 'classnames'; +import { Link, useHistory } from 'react-router-dom'; +import Highlighter from 'react-highlight-words'; +import { Table, Tooltip, Popover, Input, Button, message, Space } from 'coding-oa-uikit'; +// import StarIcon from 'coding-oa-uikit/lib/icon/Star'; +// import CancelStarIcon from 'coding-oa-uikit/lib/icon/CancelStar'; +import PencilIcon from 'coding-oa-uikit/lib/icon/Pencil'; +import ScanIcon from 'coding-oa-uikit/lib/icon/Scan'; +import ShieldIcon from 'coding-oa-uikit/lib/icon/Shield'; +import MemberSettingsIcon from 'coding-oa-uikit/lib/icon/MemberSettings'; +import ClockIcon from 'coding-oa-uikit/lib/icon/Clock'; +import AlignLeftIcon from 'coding-oa-uikit/lib/icon/AlignLeft'; + +import { putRepo } from '@src/services/repos'; +// import { subscribedRepo, cancelSubscribedRepo, putRepo } from '@src/services/repos'; +import { formatDateTime, getRepoName } from '@src/utils'; +import { getProjectRouter } from '@src/utils/getRoutePath'; + +import MemberConfig from './member-config'; +import AuthorityConfig from './authority-config'; +import EditModal from './edit-modal'; +import style from './style.module.scss'; + +const { Column } = Table; + +interface RepoListProps { + orgSid: string; + teamName: string; + searchWords: string; // 搜索关键字 + loading: boolean; + list: Array; + count: number; + pageStart: number; + pageSize: number; + onChangePageSize: (page: number, pageSize: number) => void; + callback: () => void; + /** 关闭仓库成员配置 */ + closeMemberConf?: boolean; +} + +const RepoList = (props: RepoListProps) => { + const { + orgSid, teamName, searchWords, loading, list, count, pageStart, pageSize, + onChangePageSize, callback, closeMemberConf, + } = props; + const history = useHistory(); + const [visible, setVisible] = useState(false); // 修改别名 + const [name, setName] = useState(''); + const [editVsb, setEditVsb] = useState({ + visible: false, + repoInfo: null, + }); + const [hoverRowId, setHoverRowId] = useState(undefined); + const [clickRowIds, setClickRowIds] = useState([]); + const [memberConf, setMemberConf] = useState({ + visible: false, + repoId: undefined, + }); + const [authorityConf, setAuthorityConf] = useState({ + visible: false, + repoInfo: null, + }); + + const updataName = (data: any) => { + if (name && name !== data.name) { + putRepo(orgSid, teamName, data.id, { + ...data, + name, + }).then(() => { + message.success('别名修改成功'); + callback?.(); + setVisible(false); + setName(''); + }); + } else { + setVisible(false); + } + }; + + // const subscribedHandle = (repoId: number, subscribed: boolean) => { + // const func = subscribed ? subscribedRepo : cancelSubscribedRepo; + // func(orgSid, teamName, repoId).then(() => { + // message.success(`已${subscribed ? '关注' : '取消关注'}该项目`); + // callback?.(); + // }); + // }; + + return ( + <> + item.id} + loading={loading} + dataSource={list} + onRow={record => ({ + onClick: () => { + setClickRowIds([record.id]); + }, // 点击行 + onMouseEnter: () => { + setHoverRowId(record.id); + }, // 鼠标移入行 + onMouseLeave: () => { + setHoverRowId(undefined); + }, // 鼠标移出行 + })} + pagination={{ + current: Math.floor(pageStart / pageSize) + 1, + total: count, + pageSize, + showSizeChanger: false, + showTotal: (total: any, range: any) => `${range[0]} - ${range[1]} 条数据,共 ${total} 条`, + onChange: onChangePageSize, + }} + scroll={{ x: true }} + > + ( + <> + + + + {/* todo: 待接口完善 */} + {/* { + subscribedHandle(data.id, !data.subscribed); + }}> + { // 关注的图标一直显示 + (hoverRowId === data.id || data.subscribed) && ( + + { + data.subscribed ? : + } + + ) + } + */} + + { + hoverRowId === data.id && ( + + document.body} + onVisibleChange={(visible) => { + setVisible(visible); + if (!visible) { + setName(''); + } + }} + content={( +
+ setName(e.target.value)} + /> +
+ + +
+
+ )} + > + +
+
+ ) + } +
+ + )} + /> + ( + + + + )} + /> + time &&
{formatDateTime(time)}
} + /> + ( +
+ + {data?.branch_name} + + { + data?.active_time && ( +
+ {formatDateTime(data.active_time)}   + {data.total_line_num && {data.total_line_num} 行 } +
+ ) + } +
+ )} + /> + {/* */} + ( + + +
+ { + setMemberConf({ + visible: false, + repoId: undefined, + }); + }} + /> + { + setAuthorityConf({ + visible: false, + repoInfo: null, + }); + }} + callback={() => callback?.()} + /> + { + setEditVsb({ + visible: false, + repoInfo: null, + }); + }} + callback={() => callback?.()} + /> + + ); +}; + +export default RepoList; diff --git a/web/packages/tca-analysis/src/modules/repos/member-config.tsx b/web/packages/tca-analysis/src/modules/repos/member-config.tsx new file mode 100644 index 000000000..6fa126cd1 --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/member-config.tsx @@ -0,0 +1,125 @@ +/** + * 项目成员设置 + */ +import React, { useEffect, useState } from 'react'; +import { uniqBy } from 'lodash'; +import { Modal, Select, message, Button, Tag } from 'coding-oa-uikit'; + +import { getRepoMembers, postRepoMembers, delRepoMembers } from '@src/services/repos'; +import { getProjectMembers } from '@src/utils'; + +import style from './style.module.scss'; + + +interface MemberConfigProps { + teamName: string; + visible: boolean; + orgSid: string; + repoId: number; + onCancel: (refresh?: boolean) => void; +} + +const MemberConfig = (props: MemberConfigProps) => { + const { orgSid, teamName, repoId, visible, onCancel } = props; + const projectMembers = getProjectMembers(); + const [selectMembers, setSelectMembers] = useState([]); + const [members, setMembers] = useState([]); + + useEffect(() => { + if (visible) { + getMembers(); + } + }, [visible, repoId]); + + const getMembers = () => { + getRepoMembers(orgSid, teamName, repoId).then((res) => { + const { admins = [], users = [] } = res; + const arrs = uniqBy([...admins, ...users], 'username'); + setMembers(arrs); + }); + }; + + const onAdd = () => { + if (selectMembers?.length > 0) { + postRepoMembers(orgSid, teamName, repoId, { + role: 1, + users: selectMembers, + }).then(() => { + message.success('仓库成员配置成功'); + getMembers(); + setSelectMembers([]); + }); + } else { + message.warning('请选择成员'); + } + }; + + const onDel = (user: any) => { + Modal.confirm({ + title: `确认删除成员【${user.nickname}】?`, + onOk() { + delRepoMembers(orgSid, teamName, repoId, user.username).then(() => { + message.success('删除成功'); + getMembers(); + }); + }, + }); + }; + + const onReset = () => { + onCancel(); + setSelectMembers([]); + setMembers([]); + }; + + + return ( + { + setSelectMembers([]); + }} + footer={null} + className={style.memberConfigModal} + > + { + members?.length > 0 && ( + + ) + } + { + members.map(item => ( + { + e.preventDefault(); + onDel(item); + }} + >{item.nickname} + )) + } +
+ + +
+
+ ); +}; + +export default MemberConfig; diff --git a/web/packages/tca-analysis/src/modules/repos/repo-list/index.tsx b/web/packages/tca-analysis/src/modules/repos/repo-list/index.tsx deleted file mode 100644 index 23ffc0247..000000000 --- a/web/packages/tca-analysis/src/modules/repos/repo-list/index.tsx +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import React, { useState } from 'react'; -import { useParams, useHistory } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import classnames from 'classnames'; -import { Row, Col, Tabs, Button, message } from 'coding-oa-uikit'; -import LoadingIcon from 'coding-oa-uikit/lib/icon/Loading'; -import ExternalLinkIcon from 'coding-oa-uikit/lib/icon/ExternalLink'; -import Copy from '@src/components/copy'; -import { toNumber, get, find, remove, isEmpty } from 'lodash'; -import { useSelector } from 'react-redux'; - -// 项目内 -import { SET_REPOS } from '@src/context/constant'; -import { useDispatchStore } from '@src/context/store'; -import { getProjectRouter, getReposRouter } from '@src/utils/getRoutePath'; -import { delRepo } from '@src/services/repos'; -import { useInitRepo } from '@src/modules/repos/hooks'; -import LeftList from './left-list'; -import Members from './tabs/members'; -import Authority from './tabs/authority'; -import Overview from './tabs/overview'; -import s from './style.scss'; -import { REPO_TAB_TYPE, REPO_TAB_TYPE_TXT } from '../constants'; -import { getRepoRouter } from '../routes'; -import DeleteModal from '@src/components/delete-modal'; - -interface IProps { - repos: Array; -} - -/** - * 根据路由获取tab - */ -const getTabValue = () => { - const url = window.location.href; - if (url.indexOf(REPO_TAB_TYPE.MEMBER) > -1) { - return REPO_TAB_TYPE.MEMBER; - } - if (url.indexOf(REPO_TAB_TYPE.AUTH) > -1) { - return REPO_TAB_TYPE.AUTH; - } - if (url.indexOf(REPO_TAB_TYPE.OVERVIEW) > -1) { - return REPO_TAB_TYPE.OVERVIEW; - } - return ''; -}; - -const RepoList = ({ repos }: IProps) => { - const params: any = useParams(); - const { t } = useTranslation(); - const repoId = toNumber(params.repoId); - const { orgSid, teamName }: any = params; - const history = useHistory(); - const dispatch = useDispatchStore(); - const { curRepo, curRepoMember } = useInitRepo(orgSid, teamName, repoId); - const { admins = [], users = [] } = curRepoMember; - const tabValue = getTabValue(); - // 判断是否有权限删除代码库 - const APP = useSelector((state: any) => state.APP); - const isSuperuser = get(APP, 'user.is_superuser', false); // 当前用户是否是超级管理员 - const userName = get(APP, 'user.username', null); - const isAdmin = !!find(admins, { username: userName }); // 当前用户是否是代码库管理员 - const deletable = isAdmin || isSuperuser; // 删除权限 - const [deleteVisible, setDeleteVisible] = useState(false); - - // tab 切换跳转路由 - const onTabChange = (key: string) => { - history.push(getRepoRouter(orgSid, teamName, repoId, key)); - }; - - const onDeleteRepo = () => { - setDeleteVisible(true); - }; - - const handleDeleteRepo = () => { - delRepo(orgSid, teamName, repoId).then(() => { - message.success('已删除代码库'); - remove(repos, (item: any) => item?.id === repoId); - const firstRepoId = isEmpty(repos) ? null : repos[0].id; - dispatch({ - type: SET_REPOS, - payload: repos, - }); - if (firstRepoId) { - history.push(getRepoRouter(orgSid, teamName, firstRepoId)); - } else { - history.push(getReposRouter(orgSid, teamName)); - } - }) - .finally(() => { - setDeleteVisible(false); - }); - }; - - return ( - - setDeleteVisible(false)} - onOk={handleDeleteRepo} - /> - - - {curRepo ? ( - <> -
-
- {curRepo.name} - -
-
- {curRepo.scm_url} - -
-
- onTabChange(key)} - className={s.tabs} - > - - - - - - - - - - - - ) : ( -
- - {t('正在加载')}... -
- )} - -
- ); -}; - -export default RepoList; diff --git a/web/packages/tca-analysis/src/modules/repos/repo-list/left-list.tsx b/web/packages/tca-analysis/src/modules/repos/repo-list/left-list.tsx deleted file mode 100644 index 92d0de88c..000000000 --- a/web/packages/tca-analysis/src/modules/repos/repo-list/left-list.tsx +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import React, { useState } from 'react'; -import { Link, useParams, useHistory } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import classnames from 'classnames'; -import { toNumber } from 'lodash'; - -import { Col, Input, Tag, Tooltip } from 'coding-oa-uikit'; -import SearchIcon from 'coding-oa-uikit/lib/icon/Search'; -import QuestionCircleIcon from 'coding-oa-uikit/lib/icon/QuestionCircle'; -import PlusIcon from 'coding-oa-uikit/lib/icon/Plus'; -import GitBranchIcon from 'coding-oa-uikit/lib/icon/GitBranch'; -import partnerIcon from '@src/images/partner.svg'; -// 项目内 -import { getReposRouter } from '@src/utils/getRoutePath'; -// 模块内 -import s from './style.scss'; -import { getRepoRouter } from '../routes'; - -interface IProps { - repos: Array; -} - -const LeftList = ({ repos }: IProps) => { - const { t } = useTranslation(); - const [searchValue, setSearchValue] = useState(''); - const params: any = useParams(); - const { orgSid, teamName }: any = params; - const repoId = toNumber(params.repoId); - const history = useHistory(); - - // 模糊匹配 - const searchRepos = repos.filter(item => item.scm_url.indexOf(searchValue) > -1 - || (item.name && item.name.indexOf(searchValue) > -1)); - - return ( - -
- {t('仓库登记')} - - - {t('登记代码库是使用代码分析的前置步骤。建议您录入仓库权限高(如Owner、Master)的凭据完成登记。此外还可以控制用户权限等。')} -
- } - > - - - - - - - setSearchValue(e.target.value)} - value={searchValue} - placeholder={t('搜索代码库')} - suffix={} - /> -
-
- {searchRepos.length === 0 && ( -
- {t('未匹配到任何代码仓库')} -
- )} - {searchRepos.map(item => ( -
repoId !== item.id - && history.push(getRepoRouter(orgSid, teamName, item.id)) - } - title={item.scm_url} - > - {/* -
{item.name}
-
{item.scm_url}
- - } - >
*/} -
-
- {item.name} - {item.symbol === 1 && ( - - - - )} -
-
- - {item.branch_count} 个分支 - - {/* 暂时隐藏 */} - {false && ( - - {t('审核中')} - - )} -
-
-
- ))} -
- - ); -}; -export default LeftList; diff --git a/web/packages/tca-analysis/src/modules/repos/repo-list/style.scss b/web/packages/tca-analysis/src/modules/repos/repo-list/style.scss deleted file mode 100644 index e6ac12f1d..000000000 --- a/web/packages/tca-analysis/src/modules/repos/repo-list/style.scss +++ /dev/null @@ -1,98 +0,0 @@ -@import '@tencent/micro-frontend-shared/style/color.scss'; - -.left-container { - width: 300px; - border-right: 1px solid #eceff2; - height: 100%; - - .header { - padding: 20px; - border-bottom: 1px solid #eceff2; - overflow: hidden; - - .icon { - margin-left: 8px; - color: $grey-6; - } - - .icon:hover { - margin-left: 8px; - color: $grey-8; - } - } - - .content { - padding-bottom: 20px; - height: calc(100% - 125px); - height: -webkit-calc(100% - 125px); - height: -moz-calc(100% - 125px); - overflow-y: auto; - - .list-item { - cursor: pointer; - padding: 14px 18px 0 20px; - border-right: 2px solid transparent; - - &:hover, - &.active { - border-right-color: $blue-5; - background-color: #f2f4f6; - } - - .item-body { - padding-bottom: 14px; - border-bottom: 1px solid #eceff2; - - .partner-icon { - color: $green-5; - margin-left: 10px; - } - } - } - } -} - -.icon-plus-button { - float: right; - text-align: center; - border-radius: 50%; - width: 28px; - height: 28px; - background: rgb(0, 102, 255); - // box-shadow: rgba(19, 111, 220, 0.1) 0 6px 9px 0, rgba(31, 32, 32, 0.1) 0 4px 18px 0; - cursor: pointer; - transition: background-color 0.3s ease 0s; - - &:hover { - background: rgb(1, 81, 202); - } - - .icon-plus { - color: #fff; - margin-top: 7px; - } -} - -.right-container { - width: calc(100% - 300px); - width: -webkit-calc(100% - 300px); - width: -moz-calc(100% - 300px); - padding: 30px; - height: 100%; -} - -.member-item { - margin-bottom: 30px; - - .tit { - margin-bottom: 16px; - font-size: 16px; - font-weight: 500; - } - - .group { - padding: 8px 16px; - background: $grey-1; - border-radius: 2px; - } -} \ No newline at end of file diff --git a/web/packages/tca-analysis/src/modules/repos/repo-list/tabs/authority.tsx b/web/packages/tca-analysis/src/modules/repos/repo-list/tabs/authority.tsx deleted file mode 100644 index 76a7bad55..000000000 --- a/web/packages/tca-analysis/src/modules/repos/repo-list/tabs/authority.tsx +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -/** - * 仓库登记入口文件 - */ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { find, isEmpty, get, filter } from 'lodash'; -import { Button, Form, Select, message } from 'coding-oa-uikit'; -import PlusIcon from 'coding-oa-uikit/lib/icon/Plus'; -import RefreshIcon from 'coding-oa-uikit/lib/icon/Refresh'; - -// 项目内 -import { AUTH_TYPE, AUTH_TYPE_TXT, SCM_PLATFORM } from '@src/modules/repos/constants'; -import { getPCAuthRouter } from '@src/modules/repos/routes'; -import { getSSHInfo, getScmAccounts, putRepoAuth, getOAuthInfo, getPlatformStatus } from '@src/services/repos'; - -const { Option, OptGroup } = Select; - -interface IProps { - curRepo: any; - repoId: number; - orgSid: string; - teamName: string; -} - -const layout = { - labelCol: { span: 3 }, -}; - -/** - * Todo: 目前仅存在http凭证,后续可能会添加各个oauth以及ssh等,后续再重新做其余认证模式处理 - */ -const Authority = ({ curRepo, orgSid, teamName, repoId }: IProps) => { - const { t } = useTranslation(); - const [selectedAuth, setSelectedAuth] = useState({}); - const [sshAuthList, setSshAuthList] = useState([]); - const [httpAuthList, setHttpAuthList] = useState([]); - const [oauthAuthList, setOauthAuthList] = useState([]); - // const [allAuthList, setAllAuthList] = useState>([]); - const [authLoading, setAuthLoading] = useState(false); - - const scmAuth: any = curRepo?.scm_auth ?? {}; - let curAuth: any = {}; - if (scmAuth.auth_type) { - switch (scmAuth?.auth_type) { - case AUTH_TYPE.HTTP: - curAuth = scmAuth.scm_account; - break; - case AUTH_TYPE.SSH: - curAuth = scmAuth.scm_ssh; - break; - case AUTH_TYPE.OAUTH: - curAuth = scmAuth.scm_oauth; - break; - } - if (curAuth) { - curAuth.auth_type = curRepo?.scm_auth?.auth_type; - } else { - curAuth = {}; - } - } - - const setCurAuth = (sshList = sshAuthList, httpList = httpAuthList, oauthList = oauthAuthList) => { - // 确保当前凭证在select数据内 - if ( - curAuth.id - && curAuth.auth_type === AUTH_TYPE.SSH - && !find(sshList, { id: curAuth.id }) - ) { - setSshAuthList([curAuth, ...sshList]); - } - if ( - curAuth.id - && curAuth.auth_type === AUTH_TYPE.HTTP - && !find(httpList, { id: curAuth.id }) - ) { - setHttpAuthList([curAuth, ...httpList]); - } - if ( - curAuth.id - && curAuth.auth_type === AUTH_TYPE.OAUTH - && !find(oauthList, { id: curAuth.id }) - ) { - setOauthAuthList([curAuth, ...oauthList]); - } - }; - - const getAuth = () => { - setAuthLoading(true); - Promise.all([ - getSSHInfo().then(r => r.results || []), - getScmAccounts().then(r => r.results || []), - getOAuthInfo().then(r => r.results || []), - getPlatformStatus().then(r => r || []), - ]).then((result) => { - const activeOauth = filter( - result[2].map((item: any) => ({ - ...item, - platform_status: get(result[3], item.scm_platform_name, [false]), - })), - 'platform_status', - ); - setSshAuthList(result[0]); - setHttpAuthList(result[1]); - setOauthAuthList(activeOauth); - setCurAuth(result[0], result[1], result[2]); - setAuthLoading(false); - }); - }; - - const onFinish = () => { - if (selectedAuth) { - if (selectedAuth.id === curAuth.id) { - message.info('认证未发生变更'); - } else { - const scmAuth: any = { - auth_type: selectedAuth.auth_type, - scm_auth: null, - }; - - if (selectedAuth.auth_type === AUTH_TYPE.HTTP) { - scmAuth.scm_account = selectedAuth.id; - scmAuth.scm_ssh = null; - scmAuth.scm_oauth = null; - } - - if (selectedAuth.auth_type === AUTH_TYPE.SSH) { - scmAuth.scm_ssh = selectedAuth.id; - scmAuth.scm_account = null; - scmAuth.scm_oauth = null; - } - - if (selectedAuth.auth_type === AUTH_TYPE.OAUTH) { - scmAuth.scm_oauth = selectedAuth.id; - scmAuth.scm_account = null; - scmAuth.scm_ssh = null; - } - - putRepoAuth(orgSid, teamName, repoId, { scm_auth: scmAuth }).then(() => { - message.success('已切换认证方式'); - }); - } - } else { - message.warning(t('请选择一条凭证用于代码库认证')); - } - }; - - const onReset = () => { - setSelectedAuth(curAuth.id - ? { - id: curAuth.id, - auth_type: curAuth.auth_type, - } - : {}); - }; - - useEffect(() => { - getAuth(); - }, []); - - useEffect(() => { - setSelectedAuth(curAuth.id - ? { - id: curAuth.id, - auth_type: curAuth.auth_type, - } - : {}); - setCurAuth(); - }, [curRepo.id, curAuth.id]); - - return ( -
- - <> - - - - - -
- - -
-
- ); -}; - -export default Authority; diff --git a/web/packages/tca-analysis/src/modules/repos/repo-list/tabs/members.tsx b/web/packages/tca-analysis/src/modules/repos/repo-list/tabs/members.tsx deleted file mode 100644 index 4ca0ba6f0..000000000 --- a/web/packages/tca-analysis/src/modules/repos/repo-list/tabs/members.tsx +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import React, { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { unionWith, isEqual } from 'lodash'; -import { Row, Col, Button, Avatar, Modal, Select, message } from 'coding-oa-uikit'; -import PlusCircleIcon from 'coding-oa-uikit/lib/icon/PlusCircle'; -import UserIcon from 'coding-oa-uikit/lib/icon/User'; - -// 项目内 -import { useStateStore, useDispatchStore } from '@src/context/store'; -import { SET_CUR_REPO_MEMBER } from '@src/context/constant'; -import { getUserImgUrl } from '@src/utils'; -import { getMembers } from '@src/services/common'; -import { postRepoMembers } from '@src/services/repos'; - -// 模块内 -import s from '../style.scss'; - -const PERM_ENUM = { - ADMIN: 1, - USER: 2, -}; - -interface IMemberItem { - list: Array; - title: string; - onAddMemberClick: () => void; -} - -const MemberItem = ({ list, title, onAddMemberClick }: IMemberItem) => ( -
-
{title}
- - - - - {list.map((userinfo: any) => ( - - } - /> - {userinfo.nickname} - - ))} - -
-); - -interface IProps { - orgSid: string; - teamName: string; - repoId: any; - admins: Array; - users: Array; -} - -const Members = ({ orgSid, teamName, repoId, admins }: IProps) => { - const dispatch = useDispatchStore(); - const { t } = useTranslation(); - const [inviteVisb, setInviteVisb] = useState(false); - const { projectMembers } = useStateStore(); - const userOptions = unionWith(projectMembers.admins, projectMembers.users, isEqual).map(user => ({ - ...user, - label: user.nickname, - value: user.username, - })); - - const [form, setForm] = useState({ - role: PERM_ENUM.USER, - users: [], - }); - - const onAddMemberHandle = (role: number) => { - setForm({ - ...form, - role, - }); - setInviteVisb(true); - }; - - const onSelectUsersHandle = (value: any) => { - setForm({ - ...form, - users: value, - }); - }; - - const onOkHandle = () => { - if (form.users.length > 0) { - postRepoMembers(orgSid, teamName, repoId, form).then(() => { - message.success(t('成员添加成功')); - // 获取代码库成员 - getMembers(orgSid, teamName, repoId).then((response) => { - dispatch({ - type: SET_CUR_REPO_MEMBER, - payload: response, - }); - }); - setInviteVisb(false); - }); - } else { - message.warning(t('成员为必选项')); - } - }; - - return ( -
- setInviteVisb(false)} - > - : {curRepo.ssh_url}} - - - {edit ? : {curRepo.name}} - - - <> - } - /> - - {creatorInfo.nickname} - - - - - - {formatDateTime(curRepo.created_time)} - - -
- {edit ? ( - <> - - - - ) : ( - - )} - {deletable && } -
- - ); -}; -export default Overview; diff --git a/web/packages/tca-analysis/src/modules/repos/repo/index.tsx b/web/packages/tca-analysis/src/modules/repos/repo/index.tsx new file mode 100644 index 000000000..ea0a9501f --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/repo/index.tsx @@ -0,0 +1,179 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { useParams, Link, useHistory } from 'react-router-dom'; +import cn from 'classnames'; +import { Skeleton, Descriptions, Tooltip, message, Popover, Button, Input, Space } from 'coding-oa-uikit'; +import PencilIcon from 'coding-oa-uikit/lib/icon/Pencil'; +import ScanIcon from 'coding-oa-uikit/lib/icon/Scan'; +import ShieldIcon from 'coding-oa-uikit/lib/icon/Shield'; +import ClockIcon from 'coding-oa-uikit/lib/icon/Clock'; +import AlignLeftIcon from 'coding-oa-uikit/lib/icon/AlignLeft'; +import MemberSettingsIcon from 'coding-oa-uikit/lib/icon/MemberSettings'; + +import { getRepo, putRepo } from '@src/services/repos'; +import { getProjectRouter, getReposRouter } from '@src/utils/getRoutePath'; +import { formatDateTime, getRepoName } from '@src/utils'; +import { CLOSE_REPO_MEMBER_CONF } from '@plat/modules'; + +import MemberConfig from '../member-config'; +import AuthorityConfig from '../authority-config'; +import EditModal from '../edit-modal'; +import style from '../style.module.scss'; + +const Repo = () => { + const [repoDetailData, setRepoDetailData] = useState({ + loading: false, + repoInfo: null, + }); + const [authVisible, setAuthVisible] = useState(false); + const [memberVisible, setMemberVisible] = useState(false); + const [editVisible, setEditVisible] = useState(false); + const [aliasVisible, setAliasVisible] = useState(false); + const [name, setName] = useState(''); + const { orgSid, teamName, repoId }: any = useParams(); + const history = useHistory(); + + const { loading, repoInfo } = repoDetailData; + const { recent_active: recentActive = null } = repoInfo || {}; + + const init = useCallback(async (showLoading = false) => { + if (showLoading) { + setRepoDetailData(pre => ({ ...pre, loading: true })); + } + const repoInfo: any = await getRepo(orgSid, teamName, repoId); + setRepoDetailData({ + loading: false, + repoInfo, + }); + setName(repoInfo.name || getRepoName(repoInfo.scm_url)); + }, [orgSid, teamName, repoId]); + + useEffect(() => { + if (repoId) { + init(true); + } + }, [repoId, init]); + + const updataName = useCallback((repoInfo: any, name: string) => { + if (name && name !== repoInfo.name) { + putRepo(orgSid, teamName, repoInfo.id, { + ...repoInfo, + name, + }).then(() => { + message.success('别名修改成功'); + setAliasVisible(false); + init(); + }); + } else { + setAliasVisible(false); + } + }, [init]); + + return <> + + {repoInfo + && + + {name} +
+ + + setName(e.target.value)} + /> +
+ + +
+
+ )} + > +
+ + } layout="vertical" bordered> + {repoInfo.scm_url} + {formatDateTime(repoInfo.created_time)} + {repoInfo.created_from} + + {recentActive && <> + + {recentActive.branch_name} + + { + recentActive.active_time && ( +
+ {formatDateTime(recentActive.active_time)}   + {recentActive.total_line_num && {recentActive.total_line_num} 行 } +
+ ) + } + } +
+ + + + + {!CLOSE_REPO_MEMBER_CONF && } + + + + + } + + setMemberVisible(false)} + /> + setAuthVisible(false)} + /> + setEditVisible(false)} + callback={(deletedRepo: boolean) => { + if (deletedRepo) { + history.replace(getReposRouter(orgSid, teamName)); + } else { + init(); + } + }} + /> + ; +}; + +export default Repo; diff --git a/web/packages/tca-analysis/src/modules/repos/routes.ts b/web/packages/tca-analysis/src/modules/repos/routes.ts deleted file mode 100644 index 2d8db1ad2..000000000 --- a/web/packages/tca-analysis/src/modules/repos/routes.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2021-2022 THL A29 Limited -// -// This source code file is made available under MIT License -// See LICENSE for details -// ============================================================================== - -import { getBaseRouter } from '@src/utils/getRoutePath'; -import { REPO_TAB_TYPE } from './constants'; - -/** - * 获取代码库路由, 默认路由到成员页面 - * @param repoId 代码库ID - */ -export const getRepoRouter = ( - orgSid: string, - teamName: string, - repoId: number | string, - tabKey: string = REPO_TAB_TYPE.MEMBER, -) => `${getBaseRouter(orgSid, teamName)}/repos/${repoId}/${tabKey}`; - -export const getPCAuthRouter = () => '/user/auth'; diff --git a/web/packages/tca-analysis/src/modules/repos/style.module.scss b/web/packages/tca-analysis/src/modules/repos/style.module.scss new file mode 100644 index 000000000..f04fe77bf --- /dev/null +++ b/web/packages/tca-analysis/src/modules/repos/style.module.scss @@ -0,0 +1,86 @@ + +.repos { + .repo-header { + display: flex; + align-items: center; + justify-content: space-between; + height: 48px; + font-size: 16px; + font-weight: 500; + line-height: 48px; + padding: 0 24px; + box-shadow: inset 0 -1px 0 0 #dadfe6; + } + + .search { + display: flex; + align-items: center; + min-height: 48px; + padding: 0 24px; + border-bottom: 1px solid #dadfe6; + background-color: #fcfcfa; + } + + .list { + padding: 0 24px; + + :global(.ant-table-thead > tr > th) { + border-top: none; + } + + .pencil-icon { + display: inline-block; + width: 18px; + text-align: right; + margin-left: 10px; + } + + .star-icon { + display: inline-block; + width: 26px; + text-align: right; + color: #edb807; + cursor: pointer; + } + + .desc { + font-size: 13px; + color: #606c80; + } + + .scm-url { + color: #202d40; + + &.hover { + color: #0066ff; + } + } + } +} + +.repo { + padding: 24px; + .desc { + font-size: 12px; + color: #606c80; + } +} + +.member-config-modal { + .label { + font-size: 13px; + color: #606c80; + } + + .add-members { + display: flex; + justify-content: space-between; + margin-top: 20px; + padding-bottom: 30px; + + .select-members { + margin-right: 10px; + width: 100%; + } + } +} \ No newline at end of file diff --git a/web/packages/tca-analysis/src/modules/repos/style.scss b/web/packages/tca-analysis/src/modules/repos/style.scss deleted file mode 100644 index c38a9474e..000000000 --- a/web/packages/tca-analysis/src/modules/repos/style.scss +++ /dev/null @@ -1,19 +0,0 @@ - -.repo-create-container { - padding: 30px; - - .tit { - margin-bottom: 30px; - font-size: 18px; - font-weight: 600; - } - - .form-desc { - display: block; - color: #919daf; - margin-top: 6px; - white-space: normal; - } - -} - diff --git a/web/packages/tca-analysis/src/modules/schemes/code-lint/all-rules-search.tsx b/web/packages/tca-analysis/src/modules/schemes/code-lint/all-rules-search.tsx index fc6420482..d0f7c0a6a 100644 --- a/web/packages/tca-analysis/src/modules/schemes/code-lint/all-rules-search.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/code-lint/all-rules-search.tsx @@ -13,7 +13,7 @@ import Filter from '@src/components/filter'; import { SEVERITY, CATEGORY } from '../constants'; const numberParams = ['checkpackage', 'severity', 'category']; -const arrayParams = ['language']; +const arrayParams = ['language_name']; interface SearchProps { filters: any; @@ -109,16 +109,15 @@ const Search = (props: SearchProps) => { } /> - + ({ value: item.name, text: item.display_name, }))} - onChange={(value: any) => value && onChange('language', value.join(',')) + onChange={(value: any) => onChange('language_name', value) } /> @@ -150,7 +149,6 @@ const Search = (props: SearchProps) => { } /> */} - { {Object.keys(searchParams).some((key: string) => (isArray(searchParams[key]) ? !isEmpty(searchParams[key]) : searchParams[key])) && ( - + )} { find(checkTools, { id: initialValues.checktool }) && ( diff --git a/web/packages/tca-analysis/src/modules/schemes/code-lint/index.tsx b/web/packages/tca-analysis/src/modules/schemes/code-lint/index.tsx index 93367c79d..7d4c728f8 100644 --- a/web/packages/tca-analysis/src/modules/schemes/code-lint/index.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/code-lint/index.tsx @@ -8,7 +8,7 @@ * 分析方案 - 代码检查 */ import React, { useState, useEffect } from 'react'; -import { Link, useHistory } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import cn from 'classnames'; import { intersection, isEmpty, concat } from 'lodash'; @@ -28,7 +28,7 @@ import { addCheckPackages, delCheckPackage, } from '@src/services/schemes'; -import { getSchemeRouter } from '@src/utils/getRoutePath'; +import { getSchemeRouter, getSchemeBlankRouter } from '@src/utils/getRoutePath'; import Filter from '@src/components/filter'; import SelectBorderless from '@src/components/select-borderless'; @@ -254,23 +254,23 @@ const CodeLint = (props: CodeLintProps) => {

自定义规则 {customPackage.checkrule_count} 条

- 查看详细规则 - +
{allPkgs .filter((item: any) => (filter(item.labels, label) && filter(item.languages, lang)) - || selectedPkgs.includes(item.id)) + || selectedPkgs.includes(item.id)) .map((item: any) => ( { {item.name} ) : ( - {item.name} + {item.name} )} onChange(value, item.id)} /> @@ -333,9 +333,9 @@ const Item = (props: any) => { ) : ( - - {item.description} - + + {item.description} + )}
@@ -347,22 +347,22 @@ const Item = (props: any) => {

{item.languages.join('、')}

) : ( -

{item.languages.join('、')}

+

{item.languages.join('、')}

)}
- 查看详细规则 {' '} - +
); diff --git a/web/packages/tca-analysis/src/modules/schemes/index.tsx b/web/packages/tca-analysis/src/modules/schemes/index.tsx index 4a04019e6..632eaffac 100644 --- a/web/packages/tca-analysis/src/modules/schemes/index.tsx +++ b/web/packages/tca-analysis/src/modules/schemes/index.tsx @@ -8,14 +8,14 @@ * 分析方案入口文件 */ import React, { useEffect, useState } from 'react'; -import { useHistory, useParams, Link } from 'react-router-dom'; +import { useHistory, useParams } from 'react-router-dom'; import cn from 'classnames'; import { get, findIndex, isEmpty, toNumber, find } from 'lodash'; import { Tabs, Button } from 'coding-oa-uikit'; import { useStateStore } from '@src/context/store'; import Repos from '@src/components/repos'; -import { getSchemeRouter, getTmplRouter } from '@src/utils/getRoutePath'; +import { getSchemeRouter, getTmplBlankRouter } from '@src/utils/getRoutePath'; import { getSchemes, getLanguages, getTags, getSchemeBasic } from '@src/services/schemes'; import { getTmplList } from '@src/services/template'; import noDataSvg from '@src/images/no-data.svg'; @@ -96,6 +96,8 @@ const Schemes = () => { return (
history.push(getSchemeRouter(orgSid, teamName, repo.id))} /> {/* eslint-disable-next-line */} @@ -106,130 +108,129 @@ const Schemes = () => {
暂无方案,请 - +
) : ( -
- { - setVisible(true); - }} - changeSchemeHandle={(item) => { - getSchemeInfo(item.id); - }} - /> -
-
-
-

{schemeInfo.name}

- {schemeInfo.default_flag && schemeInfo.status === 1 && ( - Default - )} - {schemeInfo.status === 2 && ( - - 已废弃 - - )} -
- {schemeInfo.refer_scheme?.is_template && ( -

- 该分析方案由模板 - - 「{schemeInfo.refer_scheme.name}」 - - 创建;若需同步模板配置,请点击 - -

- )} -
- { - history.push(`${getSchemeRouter( - orgSid, - teamName, - curRepo.id, - schemeInfo.id, - )}/${key}`); +
+ { + setVisible(true); + }} + changeSchemeHandle={(item) => { + getSchemeInfo(item.id); + }} + /> +
+
+
+

{schemeInfo.name}

+ {schemeInfo.default_flag && schemeInfo.status === 1 && ( + Default + )} + {schemeInfo.status === 2 && ( + + 已废弃 + + )} +
+ {schemeInfo.refer_scheme?.is_template && ( +

+ 该分析方案由模板 + + 「{schemeInfo.refer_scheme.name}」 + + 创建;若需同步模板配置,请点击 + +

+ )} +
+ { + history.push(`${getSchemeRouter( + orgSid, + teamName, + curRepo.id, + schemeInfo.id, + )}/${key}`); + }} + > + + { + let list: any = [...schemes]; + const index = findIndex(schemes as any, { id: data.id }); + if (index > -1) { + list[index] = data; + if (data.default_flag) { + list = list.map((item: any) => (item.id === data.id + ? data + : { ...item, default_flag: false })); + } + setSchemes(list); + setSchemeInfo(data); + } }} - > - - { - let list: any = [...schemes]; - const index = findIndex(schemes as any, { id: data.id }); - if (index > -1) { - list[index] = data; - if (data.default_flag) { - list = list.map((item: any) => (item.id === data.id - ? data - : { ...item, default_flag: false })); - } - setSchemes(list); - setSchemeInfo(data); - } - }} - /> - - - - - - - - - - - {/* + /> + + + + + + + + + + + {/* 权限配置 */} - - - - -
-
+ + + +
+
+
)} { const [form] = Form.useForm(); const { searchParams, loading, filters, addToolsRules, callback } = props; - const { allPkgs = [], labels = [], languages = [], checkTools = [] } = filters as any; + const { allPkgs = [], languages = [], checkTools = [] }: any = filters; const initialValues = cloneDeep(searchParams); @@ -101,21 +101,23 @@ const Search = (props: SearchProps) => { multiple allowClear placeholder="全部" - data={getData(labels)} + data={Object.entries(CATEGORY).map(([key, value]: [string, string]) => ({ + value: toNumber(key), + text: value, + }))} onChange={(value: any) => value && onChange('category', value.join(',')) } />
({ value: item.name, text: item.display_name, }))} - onChange={(value: any) => value && onChange('language_name', value.join(',')) + onChange={(value: any) => onChange('language_name', value) } /> @@ -137,7 +139,6 @@ const Search = (props: SearchProps) => { } {/* ({ @@ -152,15 +153,16 @@ const Search = (props: SearchProps) => { onChange('real_name', value)} /> {Object.keys(searchParams).some((key: string) => (isArray(searchParams[key]) ? !isEmpty(searchParams[key]) : searchParams[key])) && ( - + )} { find(checkTools, { id: initialValues.checktool }) && ( diff --git a/web/packages/tca-analysis/src/modules/template/code-lint/all-rules.tsx b/web/packages/tca-analysis/src/modules/template/code-lint/all-rules.tsx index 9441cee3b..015e0b881 100644 --- a/web/packages/tca-analysis/src/modules/template/code-lint/all-rules.tsx +++ b/web/packages/tca-analysis/src/modules/template/code-lint/all-rules.tsx @@ -79,7 +79,7 @@ const AllRules = () => { const getAllPkgs = async () => { let pkgs = await getAllCheckPackages(orgSid, tmplId); - pkgs = (pkgs.results || []).filter((item: any) => !item.disable); + pkgs = (pkgs || [])?.filter((item: any) => !item.disable); setAllPkgs(pkgs); }; diff --git a/web/packages/tca-analysis/src/modules/template/code-lint/index.tsx b/web/packages/tca-analysis/src/modules/template/code-lint/index.tsx index d0de3fe06..1c15d257c 100644 --- a/web/packages/tca-analysis/src/modules/template/code-lint/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/code-lint/index.tsx @@ -8,7 +8,7 @@ * 分析方案 - 代码检查 */ import React, { useState, useEffect } from 'react'; -import { Link, useHistory } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import cn from 'classnames'; import { intersection, isEmpty, concat } from 'lodash'; @@ -30,7 +30,7 @@ import { addCheckPackages, delCheckPackage, } from '@src/services/template'; -import { getTmplRouter } from '@src/utils/getRoutePath'; +import { getTmplRouter, getTmplBlankRouter } from '@src/utils/getRoutePath'; import Filter from '@src/components/filter'; import SelectBorderless from '@src/components/select-borderless'; @@ -287,25 +287,25 @@ const CodeLint = (props: CodeLintProps) => {

自定义规则 {customPackage.checkrule_count} 条

- 查看详细规则 - +
{allPkgs .filter((item: any) => (filter(item.labels, label) && filter(item.languages, lang)) - || selectedPkgs.includes(item.id)) + || selectedPkgs.includes(item.id)) .map((item: any) => ( { {item.name} ) : ( - {item.name} + {item.name} )} { ) : ( - - {item.description} - + + {item.description} + )}
适用于 {item.languages.length} 种语言 - {item.languages.join('、').length > 20 ? ( + {item.languages.join('、').length > 20 ? (

{item.languages.join('、')}

- ) : ( -

{item.languages.join('、')}

- )} + ) : ( +

{item.languages.join('、')}

+ )}
- 查看详细规则 {' '} - +
); diff --git a/web/packages/tca-analysis/src/modules/template/permission/index.tsx b/web/packages/tca-analysis/src/modules/template/permission/index.tsx index 83281bc1a..1f735988c 100644 --- a/web/packages/tca-analysis/src/modules/template/permission/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/permission/index.tsx @@ -16,6 +16,7 @@ import QuestionCircle from 'coding-oa-uikit/lib/icon/QuestionCircle'; import { getPermConf, updatePermConf } from '@src/services/template'; import { getOrgMembers } from '@src/services/common'; +import { TMPL_SCHEME_PERM_OPEN_LABEL } from '@plat/modules'; import formStyle from '../style.scss'; import style from './style.scss'; @@ -78,7 +79,7 @@ const Permission = (props: PermissionProps) => { > @@ -91,7 +92,7 @@ const Permission = (props: PermissionProps) => { 管理员 document.getElementById('main-container')} + getPopupContainer={() => document.body} title="拥有模板编辑权限" > @@ -104,7 +105,7 @@ const Permission = (props: PermissionProps) => { mode="multiple" disabled={isSysTmpl} optionFilterProp="label" - getPopupContainer={() => document.getElementById('main-container')} + getPopupContainer={() => document.body} options={teamMembers.map((item: any) => ({ label: item.nickname, value: item.username, @@ -119,7 +120,7 @@ const Permission = (props: PermissionProps) => { 普通成员 document.getElementById('main-container')} + getPopupContainer={() => document.body} title="可使用模板" > @@ -132,7 +133,7 @@ const Permission = (props: PermissionProps) => { mode="multiple" disabled={isSysTmpl} optionFilterProp="label" - getPopupContainer={() => document.getElementById('main-container')} + getPopupContainer={() => document.body} options={teamMembers.map((item: any) => ({ label: item.nickname, value: item.username, diff --git a/web/packages/tca-analysis/src/modules/template/schemes/index.tsx b/web/packages/tca-analysis/src/modules/template/schemes/index.tsx index ad01dfdf7..14fb7781e 100644 --- a/web/packages/tca-analysis/src/modules/template/schemes/index.tsx +++ b/web/packages/tca-analysis/src/modules/template/schemes/index.tsx @@ -9,11 +9,10 @@ */ import React, { useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; import { Table } from 'coding-oa-uikit'; import { get } from 'lodash'; -import { getSchemeRouter } from '@src/utils/getRoutePath'; +import { getSchemeBlankRouter } from '@src/utils/getRoutePath'; import { DEFAULT_PAGER } from '@src/constant'; import { getSchemeList } from '@src/services/template'; @@ -64,36 +63,36 @@ const SchemeList = (props: SchemeListProps) => { }; return ( - item.id} - loading={loading} - className={style.schemeList} - pagination={{ - size: 'default', - current: Math.floor(pageStart / pageSize) + 1, - total: count, - pageSize, - showSizeChanger: true, - showTotal: (total, range) => `${range[0]} - ${range[1]} 条,共 ${total} 条`, - onChange: onChangePageSize, - onShowSizeChange, - }} - > - ( - {name} - )} - /> - -
+ item.id} + loading={loading} + className={style.schemeList} + pagination={{ + size: 'default', + current: Math.floor(pageStart / pageSize) + 1, + total: count, + pageSize, + showSizeChanger: true, + showTotal: (total, range) => `${range[0]} - ${range[1]} 条,共 ${total} 条`, + onChange: onChangePageSize, + onShowSizeChange, + }} + > + ( + {name} + )} + /> + +
); }; diff --git a/web/packages/tca-analysis/src/modules/template/sync-modal.tsx b/web/packages/tca-analysis/src/modules/template/sync-modal.tsx index ea55976bb..1ebb20abf 100644 --- a/web/packages/tca-analysis/src/modules/template/sync-modal.tsx +++ b/web/packages/tca-analysis/src/modules/template/sync-modal.tsx @@ -9,11 +9,11 @@ */ import React, { useEffect, useState } from 'react'; -import { Link, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { Table, Modal } from 'coding-oa-uikit'; import { get } from 'lodash'; -import { getSchemeRouter } from '@src/utils/getRoutePath'; +import { getSchemeBlankRouter } from '@src/utils/getRoutePath'; import { DEFAULT_PAGER } from '@src/constant'; import { getSchemeList } from '@src/services/template'; @@ -69,54 +69,54 @@ const SyncModal = (props: SyncModalProps) => { // }; return ( - onOk(selectedRowKeys)} - > -

- 当前模板下存在以下分析方案,请勾选需要同步的分析方案 - {!onlySync && ';若不勾选,则只修改模板,不进行同步操作'} -

- item.id} - loading={loading} - // className={style.schemeList} - rowSelection={{ - selectedRowKeys, - onChange: keys => setSelectedRowKeys(keys), - }} - pagination={{ - size: 'default', - current: Math.floor(pageStart / pageSize) + 1, - total: count, - pageSize, - showSizeChanger: true, - showTotal: (total, range) => `${range[0]} - ${range[1]} 条,共 ${total} 条`, - onChange: onChangePageSize, - // onShowSizeChange, - }} - > - ( - {name} - )} - /> - -
-
+ onOk(selectedRowKeys)} + > +

+ 当前模板下存在以下分析方案,请勾选需要同步的分析方案 + {!onlySync && ';若不勾选,则只修改模板,不进行同步操作'} +

+ item.id} + loading={loading} + // className={style.schemeList} + rowSelection={{ + selectedRowKeys, + onChange: keys => setSelectedRowKeys(keys), + }} + pagination={{ + size: 'default', + current: Math.floor(pageStart / pageSize) + 1, + total: count, + pageSize, + showSizeChanger: true, + showTotal: (total, range) => `${range[0]} - ${range[1]} 条,共 ${total} 条`, + onChange: onChangePageSize, + // onShowSizeChange, + }} + > + ( + {name} + )} + /> + +
+
); }; diff --git a/web/packages/tca-analysis/src/modules/welcome/index.tsx b/web/packages/tca-analysis/src/modules/welcome/index.tsx index b2ced797b..705fc17cb 100644 --- a/web/packages/tca-analysis/src/modules/welcome/index.tsx +++ b/web/packages/tca-analysis/src/modules/welcome/index.tsx @@ -34,7 +34,7 @@ const Welcome = () => { - -
-

- {t('录入后,仓库登记、分支项目等模块可直接选择凭证,无需重复填写。')} -

- - { - setCurAuthInfo(authinfo); - setVisibleDel(true); - }} - /> - { - setVisible(false); - setReload(!reload); - }} - onCancel={() => setVisible(false)} - /> - setVisibleDel(false)} - onOk={() => onDelAuthHandler(curAuthInfo)} - content={ -
- {t('确认删除凭证')}{' '} - - {curAuthInfo.scm_username || curAuthInfo.name || 'OAuth'} - {' '} - {t('?')} -
- } - /> - - ); -}; +const Auth = ({ showOrigin }: AuthProps) => ( + + + +); export default Auth; diff --git a/web/packages/tca-layout/src/modules/tool-libs/create-libs.tsx b/web/packages/tca-layout/src/modules/tool-libs/create-libs.tsx index ed751865b..948e82db5 100644 --- a/web/packages/tca-layout/src/modules/tool-libs/create-libs.tsx +++ b/web/packages/tca-layout/src/modules/tool-libs/create-libs.tsx @@ -7,8 +7,8 @@ import { Modal, Form, Input, Select, Tooltip, Button, message, Space } from 'cod import TrashIcon from 'coding-oa-uikit/lib/icon/Trash'; import QuestionCircle from 'coding-oa-uikit/lib/icon/QuestionCircle'; import AttentionIcon from 'coding-oa-uikit/lib/icon/AttentionRed'; -import Authority from '@tencent/micro-frontend-shared/component/authority'; -import { UserAPI } from '@plat/api'; +import AuthFormItem from '@tencent/micro-frontend-shared/tca/user-auth/auth-form-item'; +import { userAuthAPI } from '@plat/api'; import { addToolLib, getLibDetail, updateToolLib } from '@src/services/tools'; import { TOOLLIB_REPO_TYPE_OPTIONS, RepoTypeEnum, SCM_MAP, LIB_ENV, LIB_TYPE } from '@src/constant'; @@ -214,8 +214,7 @@ const CreateToollibs = (props: CreateToollibsProps) => { } > {({ getFieldValue }: { getFieldValue: any }) => (getFieldValue('scm_type') !== RepoTypeEnum.ZIP && ( - @@ -226,16 +225,11 @@ const CreateToollibs = (props: CreateToollibsProps) => { > )} - getAuthList={[ - UserAPI.authSSH().get, - UserAPI.authAccount().get, - UserAPI.getOAuthInfos, - UserAPI.getPlatformStatus, - ]} - initAuth={detail.scm_auth} + userAuthAPI={userAuthAPI} + authinfo={detail.scm_auth} selectStyle={{ width: 360 }} placeholder='github公开仓库可不提供凭证' - /> + allowClear /> ))} { ( + render={(id: number, libInfo: any) => (