diff --git a/core/webpack.config.js b/core/webpack.config.js index 7de90c0dea..06c1ef1dce 100644 --- a/core/webpack.config.js +++ b/core/webpack.config.js @@ -150,6 +150,7 @@ module.exports = () => { './config': './src/config.ts', './stores/route': './src/stores/route.ts', './stores/loading': './src/stores/loading.ts', + './utils/ws': './src/utils/ws.ts', './nusi':'./src/nusi/index.tsx', }, shared: { diff --git a/shell/app/App.tsx b/shell/app/App.tsx index 659c4e2d37..b5d44e5cb1 100644 --- a/shell/app/App.tsx +++ b/shell/app/App.tsx @@ -42,33 +42,34 @@ const start = (userData: ILoginUser) => { const IconConfig = { ...DEFAULT_ICON_CONFIGS, - prefix: 'erda' + prefix: 'erda', }; startApp().then(async (App) => { - [ - import('layout/entry'), - import('org/entry'), - import('app/org-home/entry'), - import('workBench/entry'), - import('runtime/entry'), - import('publisher/entry'), - import('project/entry'), - import('apiManagePlatform/entry'), - import('microService/entry'), - import('app/modules/edge/entry'), - import('application/entry'), - import('dataCenter/entry'), - import('user/entry'), - import('dcos/entry'), - import('addonPlatform/entry'), - ...Object.values(modules), - ].forEach((p) => p.then(m => m.default(registerModule))); - userStore.reducers.setLoginUser(userData); // 需要在app start之前初始化用户信息 + // get the organization info first, or will get org is undefined when need org info (like issueStore) if (!userData.isSysAdmin) { const orgName = get(location.pathname.split('/'), '[1]'); await orgStore.effects.getOrgByDomain({ orgName }); } + [ + import('layout/entry'), + import('org/entry'), + import('app/org-home/entry'), + import('workBench/entry'), + import('runtime/entry'), + import('publisher/entry'), + import('project/entry'), + import('apiManagePlatform/entry'), + import('microService/entry'), + import('app/modules/edge/entry'), + import('application/entry'), + import('dataCenter/entry'), + import('user/entry'), + import('dcos/entry'), + import('addonPlatform/entry'), + ...Object.values(modules), + ].forEach((p) => p.then(m => m.default(registerModule))); + userStore.reducers.setLoginUser(userData); // 需要在app start之前初始化用户信息 const Wrap = () => { const currentLocale = getCurrentLocale(); return ( @@ -147,7 +148,7 @@ const init = (userData: ILoginUser) => { window.location.href = lastPath; return; } - start({ ...userData }) + start({ ...userData }); } else { // 验证系统管理员相关路由 setSysAdminLocationByAuth({ diff --git a/shell/app/common/utils/ws.ts b/shell/app/common/utils/ws.ts deleted file mode 100644 index e26c2355bf..0000000000 --- a/shell/app/common/utils/ws.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2021 Terminus, Inc. -// -// This program is free software: you can use, redistribute, and/or modify -// it under the terms of the GNU Affero General Public License, version 3 -// or later ("AGPL"), as published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import SockJS from 'sockjs-client'; - -interface IHandlerMap { - [k: string]: Array<(d: any) => void> -} - -const handlerMap: IHandlerMap = {}; -let tryTimes = 1; -const tryLimit = 6; - -export function connect(api: string) { - if (!api) { - throw new Error('ws api cat not be empty'); - } - let socket = new SockJS(api); - socket.api = api; - - socket.onopen = () => { - clearTimeout(socket.timer); - }; - - socket.onmessage = (e: any) => { - const data = JSON.parse(e.data); - // eslint-disable-next-line no-console - console.log('receive message:', data); - (handlerMap[data.type] || []).forEach(cb => cb(data)); - }; - - socket.reconnect = () => { - if (socket.connected) { - // eslint-disable-next-line no-console - console.log('disconnecting...'); - socket.close(); - } - socket.timer = setTimeout(() => { - tryTimes += 1; - if (tryTimes < tryLimit) { - // eslint-disable-next-line no-console - console.log(`try reconnecting ${tryTimes - 1} times`); - socket = connect(socket.api); - } - }, 10000 * tryTimes); - }; - - socket.onclose = socket.reconnect; - socket.onerror = socket.reconnect; - - return socket; -} - -export type WSHandler = (data: SocketMsg) => void; -export const registerWSHandler = (type: string, handler: WSHandler) => { - if (!handlerMap[type]) { - handlerMap[type] = []; - } - handlerMap[type].push(handler); -}; diff --git a/shell/app/config-page/components/list/list.spec.d.ts b/shell/app/config-page/components/list/list.spec.d.ts index ebe0c657ec..065040d1c6 100644 --- a/shell/app/config-page/components/list/list.spec.d.ts +++ b/shell/app/config-page/components/list/list.spec.d.ts @@ -30,7 +30,7 @@ declare namespace CP_LIST { rowKey?: string; visible?: boolean; size?: ISize; - useLoadMore?: boolean; + isLoadMore?: boolean; alignCenter?: boolean; noBorder?: boolean; pageSizeOptions?: string[] diff --git a/shell/app/config-page/components/list/list.tsx b/shell/app/config-page/components/list/list.tsx index 8e107b167a..65203c88bd 100644 --- a/shell/app/config-page/components/list/list.tsx +++ b/shell/app/config-page/components/list/list.tsx @@ -32,23 +32,26 @@ const List = (props: CP_LIST.Props) => { combineList: list, }); const { total = 0, pageSize, pageNo = 1 } = state || {}; - const { useLoadMore = false, visible = true, size = 'middle', rowKey, alignCenter = false, + const { isLoadMore = false, visible = true, size = 'middle', rowKey, alignCenter = false, noBorder = false, pageSizeOptions, ...rest } = configProps || {}; // 将接口返回的list和之前的list进行拼接 React.useEffect(() => { + // if isLoadMore is true, the data will be set undefined, combineList don't need to do anything + if (data === undefined) { + return; + } update((pre) => { const newState = { ...pre, ...propsState, - } + }; return { ...newState, - combineList: newState.pageNo === 1 ? list : (newState.combineList || []).concat(list) - } - }) - }, [propsState, list]) - + combineList: newState.pageNo === 1 ? list : (newState.combineList || []).concat(list), + }; + }); + }, [propsState, list, update, data]); const pagination = React.useMemo(() => { return isNumber(pageNo) ? { @@ -74,8 +77,8 @@ const List = (props: CP_LIST.Props) => { }; const loadMore = () => { - operations?.changePageNo && execOperation(operations.changePageNo, { pageNo: pageNo + 1 }, { data: { list: [] } }); - } + operations?.changePageNo && execOperation(operations.changePageNo, { pageNo: pageNo + 1 }); + }; const changePageSize = (pSize: number) => { operations?.changePageSize && execOperation(operations.changePageSize, { pageNo: 1, pageSize: pSize }); @@ -93,11 +96,11 @@ const List = (props: CP_LIST.Props) => { {(state.combineList || []).map((item, idx) => { return ; })} - {!useLoadMore && pagination ? ( + {!isLoadMore && pagination ? ( ) : null } - {useLoadMore && total > Math.max(state.combineList?.length, 0) + {isLoadMore && total > Math.max(state.combineList?.length, 0) &&
{i18n.t('more')}
} ) : diff --git a/shell/app/config-page/components/table-group/table-group.scss b/shell/app/config-page/components/table-group/table-group.scss index 0eaff0cb34..069c08f973 100644 --- a/shell/app/config-page/components/table-group/table-group.scss +++ b/shell/app/config-page/components/table-group/table-group.scss @@ -12,6 +12,7 @@ // along with this program. If not, see . .cp-dice-table-group { position: relative; + margin-bottom: 24px; .table-board { margin-top: 16px; @@ -42,7 +43,7 @@ display: flex; justify-content: center; margin-top: 24px; - margin-bottom: 48px; + margin-bottom: 24px; color: $color-text-desc; font-size: 14px; cursor: pointer; diff --git a/shell/app/config-page/components/table-group/table-group.tsx b/shell/app/config-page/components/table-group/table-group.tsx index 85437b10fb..206b18c201 100644 --- a/shell/app/config-page/components/table-group/table-group.tsx +++ b/shell/app/config-page/components/table-group/table-group.tsx @@ -65,17 +65,21 @@ const TableBoard = (props: CP_TABLE_GROUP.ITableBoardProps) => { const TableGroup = (props: CP_TABLE_GROUP.Props) => { const { props: configProps, state: propsState, data = {} as CP_TABLE_GROUP.IData, operations, execOperation = noop, updateState = noop } = props; - const [{ pageNo, list: combineList = [], total, pageSize }, updater, update] = useUpdate({ + const [{ pageNo, combineList = [], total, pageSize }, updater, update] = useUpdate({ pageNo: propsState?.pageNo || 1, total: propsState?.total || 0, pageSize: propsState?.pageSize || 3, - list: data.list, + combineList: data.list, } || {}) as any; const { visible } = configProps; const showLoadMore = total > Math.max(combineList?.length, 0) // 将接口返回的list和之前的list进行拼接 React.useEffect(() => { + // if isLoadMore is true, the data will be set undefined, combineList don't need to do anything + if (data === undefined) { + return; + } update((pre) => { const newState = { ...pre, @@ -90,7 +94,7 @@ const TableGroup = (props: CP_TABLE_GROUP.Props) => { // 加载更多 const loadMore = () => { - operations?.changePageNo && execOperation(operations.changePageNo, { pageNo: pageNo + 1 }, { data: { list: [] } }) + operations?.changePageNo && execOperation(operations.changePageNo, { pageNo: pageNo + 1 }) } if (!visible) { diff --git a/shell/app/config-page/index.tsx b/shell/app/config-page/index.tsx index ab946ea7c2..312e1dd271 100644 --- a/shell/app/config-page/index.tsx +++ b/shell/app/config-page/index.tsx @@ -13,7 +13,7 @@ import * as React from 'react'; import { useMount, useUpdateEffect } from 'react-use'; -import { isEmpty, get, set, isEqual } from 'lodash'; +import { isEmpty, get, set, isEqual, forEach } from 'lodash'; import { produce } from 'immer'; import { Spin } from 'app/nusi'; import { useUpdate } from 'common'; @@ -171,14 +171,29 @@ const ConfigPage = React.forwardRef((props: IProps, ref: any) => { operationData: _rest, }; }); - queryPageConfig(newConfig, partial, op.showLoading); + + const formatConfig = clearLoadMoreData(newConfig); + queryPageConfig(formatConfig, partial, op.showLoading); } else if (updateInfo) { updateState(updateInfo.dataKey, updateInfo.dataVal); } }; - const pageProtocol = React.useMemo(() => get(pageConfig, 'protocol'), [pageConfig]); + const clearLoadMoreData = (newConfig: CONFIG_PAGE.RenderConfig) => { + const formatConfig = produce(newConfig, (draft) => { + const comps = get(draft, 'protocol.components'); + forEach(comps, (comp) => { + if (comp?.props?.isLoadMore) { + comps[comp?.name] = { ...comp, data: undefined }; + } + }); + }); + return formatConfig; + }; + + + const pageProtocol = React.useMemo(() => get(pageConfig, 'protocol'), [pageConfig]); const Content = React.useMemo(() => { return ( { return updateState(`${compPrefixKey}.${_cId}.state`, val); }; - const reExecOperation = (_cId: string) => (_op: any, val: any, extraVal: any) => { + const reExecOperation = (_cId: string) => (_op: any, val: any) => { if (!_op || isEmpty(_op)) return; const op = cloneDeep({ ..._op }); let updateVal = cloneDeep(val) as any; @@ -97,7 +97,6 @@ const ConfigPageRender = (props: IProps) => { _cId, op, isEmpty(updateVal) ? undefined : { dataKey: `${compPrefixKey}.${_cId}.state`, dataVal: updateVal }, - isEmpty(extraVal) ? undefined : { dataKey: `${compPrefixKey}.${_cId}`, dataVal: extraVal } ); }; diff --git a/shell/app/layout/common/invite-to-org.tsx b/shell/app/layout/common/invite-to-org.tsx index bc33ab9634..1f223b1a89 100644 --- a/shell/app/layout/common/invite-to-org.tsx +++ b/shell/app/layout/common/invite-to-org.tsx @@ -40,7 +40,7 @@ export default () => { if (domain.startsWith('local')) { domain = domain.split('.').slice(1).join('.'); } - + getOrgByDomain({ domain, orgName }).then((res: any) => { res.success && !isEmpty(res.data) && updater.domainData(res.data); }); @@ -58,7 +58,7 @@ export default () => { }).then(() => { message.success(i18n.t('join org success')); setTimeout(() => { - window.location.reload(); + location.href = `/${orgName}`; }, 200); }); }; diff --git a/shell/app/layout/stores/layout.ts b/shell/app/layout/stores/layout.ts index 541b8f336a..3bf3a6edf6 100644 --- a/shell/app/layout/stores/layout.ts +++ b/shell/app/layout/stores/layout.ts @@ -13,7 +13,7 @@ import { createStore } from 'app/cube'; import { getDiceVersion, inviteToOrg } from 'layout/services'; -import * as DiceWebSocket from 'common/utils/ws'; +import * as DiceWebSocket from 'core/utils/ws'; import { enableIconfont, setApiWithOrg } from 'common/utils'; import routeInfoStore from 'app/common/stores/route'; import { find, merge } from 'lodash'; diff --git a/shell/app/modules/dataCenter/pages/cloud-source/index.tsx b/shell/app/modules/dataCenter/pages/cloud-source/index.tsx index c345a9d878..e92df5c024 100644 --- a/shell/app/modules/dataCenter/pages/cloud-source/index.tsx +++ b/shell/app/modules/dataCenter/pages/cloud-source/index.tsx @@ -334,14 +334,17 @@ const CloudSource = () => { {i18n.t('dataCenter:Bucket Count')} -
{ goTo(goTo.pages.cloudSourceOss); }}> + { + // ref issue: 59066 + } + {/*
{ goTo(goTo.pages.cloudSourceOss); }}>
{getFormatter('STORAGE', 'B').format(bucket.storageUsage || 0)}
{i18n.t('dataCenter:Storage Count')}
-
+
*/} ); }, diff --git a/shell/app/modules/dataCenter/pages/cluster-manage/index.tsx b/shell/app/modules/dataCenter/pages/cluster-manage/index.tsx index d3a71847d0..7705c620e7 100644 --- a/shell/app/modules/dataCenter/pages/cluster-manage/index.tsx +++ b/shell/app/modules/dataCenter/pages/cluster-manage/index.tsx @@ -53,8 +53,8 @@ const ClusterManage = () => { const toggleAddModalVis = (isCancel = false) => { if (addModalVis) { + isCancel && !addClusterType && toggleTypeModalVis(); updater.addClusterType(''); - isCancel && toggleTypeModalVis(); } updater.addModalVis(!addModalVis); }; diff --git a/shell/app/modules/dcos/pages/cluster-dashboard/index.tsx b/shell/app/modules/dcos/pages/cluster-dashboard/index.tsx index 509faf2009..30a08220db 100644 --- a/shell/app/modules/dcos/pages/cluster-dashboard/index.tsx +++ b/shell/app/modules/dcos/pages/cluster-dashboard/index.tsx @@ -500,7 +500,7 @@ const ClusterDashboard = () => { map(machines, ({ ip, clusterName, ...rest }) => { const { name: colourName, value: colourValue } = getMachineColourValue(rest); return ( - +
{ + if (!shouldLoad) { + setCurQuery({}); + } + }, [shouldLoad]); React.useEffect(() => { const preQuery = curQuery; const nextQuery = getQuery({ @@ -182,9 +187,6 @@ const ChartBaseFactory = { setCurQuery(nextQuery); loadChart(nextQuery); } - if (!shouldLoad) { - setCurQuery({}); - } // eslint-disable-next-line react-hooks/exhaustive-deps }, [shouldLoad, query, fetchApi, curQuery, timeSpan, chosenSortItem, chosenApp, terminusKey]); let Chart: any; diff --git a/shell/app/modules/microService/pages/gateway/containers/api-limits.tsx b/shell/app/modules/microService/pages/gateway/containers/api-limits.tsx index 0db7fdb05f..15c51a15e7 100644 --- a/shell/app/modules/microService/pages/gateway/containers/api-limits.tsx +++ b/shell/app/modules/microService/pages/gateway/containers/api-limits.tsx @@ -46,7 +46,6 @@ enum LIMIT_TYPE { export const PureApiLimits = () => { const defaultLimitType = LIMIT_TYPE.ALL; - // const [filter, setFilter] = React.useState({} as any); const [effectFilter, setEffectFilter] = React.useState({} as any); const [modalVisible, openModal, _closeModal] = useSwitch(false); const [method, setMethod] = React.useState('GET'); @@ -61,8 +60,7 @@ export const PureApiLimits = () => { getApiFilterCondition(); }); - // const { apiConsumer, apiPackage } = filter; - const { apiPackages = [], apiConsumers = [] } = apiFilterCondition; + const { apiConsumers = [] } = apiFilterCondition; const selectBefore = ( setFilter({ ...filter, apiConsumer: value })} className="filter-select mr16"> - {apiConsumers.map(({ id, name }) => )} - - -
-
- - -
- */} { }); // toggleModal(true); }; - const publishAnnouncement = (id:number) => { - effects.publishAnnouncement({ id }).then(() => { - updateList(1); - // 发布公告后,更新更新已发布公告列表 - effects.getAllNoticeListByStatus('published'); - }); + + const updatePublishedList = async () => { + updateList(1); + // after stop announcement, required to update published announcement list + const list = await effects.getAllNoticeListByStatus('published'); + layoutStore.reducers.setAnnouncementList(list); + } + + const publishAnnouncement = async (id:number) => { + await effects.publishAnnouncement({ id }); + updatePublishedList(); }; - const deprecateNotice = (id: number) => { - effects.unPublishAnnouncement({ id }).then(() => { - updateList(1); - // 停用公告后, 更新已发布公告列表 - effects.getAllNoticeListByStatus('published'); - }); + + const deprecateNotice = async (id: number) => { + await effects.unPublishAnnouncement({ id }); + updatePublishedList(); }; /** diff --git a/shell/app/modules/project/common/components/issue/edit-issue-drawer.tsx b/shell/app/modules/project/common/components/issue/edit-issue-drawer.tsx index a042691c97..df66948e35 100644 --- a/shell/app/modules/project/common/components/issue/edit-issue-drawer.tsx +++ b/shell/app/modules/project/common/components/issue/edit-issue-drawer.tsx @@ -327,7 +327,7 @@ const IssueMetaFields = ({ labels, isEditMode, isBacklog, editAuth, issueType, f if (isEditMode && formData?.issueManHour?.isModifiedRemainingTime !== false) { setFieldCb({ issueManHour: { estimateTime: v || 0 } }); } else { // 创建模式或编辑模式但剩余时间为空时,设置剩余时间为预估时间 - setFieldCb({ issueManHour: { estimateTime: v || 0, remainingTime: v } }); + setFieldCb({ issueManHour: { estimateTime: v || 0, remainingTime: v || 0 } }); } }} disabled={disabled} diff --git a/shell/app/modules/project/pages/auto-test/scenes.tsx b/shell/app/modules/project/pages/auto-test/scenes.tsx index 846101ce06..020fb5d418 100644 --- a/shell/app/modules/project/pages/auto-test/scenes.tsx +++ b/shell/app/modules/project/pages/auto-test/scenes.tsx @@ -169,6 +169,7 @@ export const getPreviewData = (d: any) => { props: { title: labelMap[item.name] || item.name, minHeight: 40, + actions: { copy: true }, }, }, }, diff --git a/shell/app/modules/project/pages/test-manage/case/manual-test.tsx b/shell/app/modules/project/pages/test-manage/case/manual-test.tsx index 6bc2936449..5c969a20cd 100644 --- a/shell/app/modules/project/pages/test-manage/case/manual-test.tsx +++ b/shell/app/modules/project/pages/test-manage/case/manual-test.tsx @@ -30,7 +30,7 @@ import CaseFilterDrawer from './filter-drawer'; import ProjectTreeModal from './project-tree-modal'; import CaseDrawer from 'project/pages/test-manage/case/case-drawer'; import testEnvStore from 'project/stores/test-env'; -import { useEffectOnce } from 'react-use'; +import { useEffectOnce, useUpdateEffect } from 'react-use'; import moment from 'moment'; import './manual-test.scss'; @@ -40,6 +40,7 @@ const ManualTest = () => { const { getTestEnvList } = testEnvStore; const caseRef = React.useRef(null as any); const [drawerVisible, setDrawerVisible] = React.useState(false); + const [searchQuery, setSearchQuery] = React.useState(query.query); const [enhanceFilterVisible, setEnhanceFilterVisible] = React.useState(false); useEffectOnce(() => { @@ -50,7 +51,7 @@ const ManualTest = () => { setEnhanceFilterVisible(false); }; - const onSearch = (q: any) => { + const onSearch = React.useCallback((q: any) => { const { timestampSecUpdatedAtBegin, timestampSecUpdatedAtEnd } = q; if (timestampSecUpdatedAtBegin) { // eslint-disable-next-line no-param-reassign @@ -62,9 +63,16 @@ const ManualTest = () => { } updateSearch(q); getCases(q); - closeEnhanceFilter(); - }; - const debouncedSearch = debounce(onSearch, 500); + setEnhanceFilterVisible(false); + }, []); + + const debouncedSearch = React.useCallback(debounce((val: string | undefined) => { + onSearch({ pageNo: 1, query: val }); + }, 500), [onSearch]); + + useUpdateEffect(() => { + debouncedSearch(searchQuery); + }, [searchQuery]); const handleAddTestSetFromOut = (data: TEST_SET.TestSet) => { caseRef.current && caseRef.current.addNodeFromOuter(data); @@ -122,7 +130,8 @@ const ManualTest = () => { debouncedSearch({ query: e.target.value, pageNo: 1 })} + value={searchQuery} + onChange={e => setSearchQuery(e.target.value)} prefix={} />