diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/SharePage.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/SharePage.tsx index 4ebe54f3c27b8fbc91af0ddf68b980ada28d7941..17cd59d7e1d60b33e67a818751b8f87c1a8500ba 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/SharePage.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/SharePage.tsx @@ -1,255 +1,255 @@ -import { Divider, Space, Tooltip, Typography } from "antd" -import React from "react" -import { useIntl, useParams } from "umi" -import { getShareInfo, queryFuncAnalysisList } from "./services" -import ClusterChart from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Cluster' -import StandaloneChart from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Standalone' -import ChartRender from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/RenderChart' -import AnalysisTable from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/Table' -import { MinusOutlined } from "@ant-design/icons" -import LoadingComp from "./components/Loading" -import EmptyComp from "./components/EmptyComp" +import { Divider, Space, Tooltip, Typography } from 'antd'; +import React from 'react'; +import { useIntl, useParams } from 'umi'; +import { getShareInfo, queryFuncAnalysisList } from './services'; +import ClusterChart from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Cluster'; +import StandaloneChart from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Standalone'; +import ChartRender from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/RenderChart'; +import AnalysisTable from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/components/Table'; +import { MinusOutlined } from '@ant-design/icons'; +import LoadingComp from './components/Loading'; +import EmptyComp from './components/EmptyComp'; +import { Analysis } from './provider'; const PAGE_FIELD_MAP: any = [ - ['test_type', 'ws.result.details.test_type'], - ['env', 'analysis.env'], - ['project', 'analysis.project'], - ['tag', 'analysis.tag'], - ['date', 'analysis.date'], -] - -const fieldMap = new Map(PAGE_FIELD_MAP) + ['test_type', 'ws.result.details.test_type'], + ['env', 'analysis.env'], + ['project', 'analysis.project'], + ['tag', 'analysis.tag'], + ['date', 'analysis.date'], +]; + +const fieldMap = new Map(PAGE_FIELD_MAP); const useFieldMessage = (str: string) => { - const { formatMessage } = useIntl() - const id: any = fieldMap.get(str) - return id ? formatMessage({ id }) : '' -} - -const FieldTooltip: React.FC<{ name: string, content: any, hasDivider?: boolean }> = ({ name, content, hasDivider = false }) => { - const title = useFieldMessage(name) - - if (!content) - return ( - <> - - - { - hasDivider && - - } - ) - return ( - <> - < Tooltip - title={title} - > - - {content || '-'} - - - { - hasDivider && - - } - - ) -} + const { formatMessage } = useIntl(); + const id: any = fieldMap.get(str); + return id ? formatMessage({ id }) : ''; +}; + +const FieldTooltip: React.FC<{ name: string; content: any; hasDivider?: boolean }> = ({ + name, + content, + hasDivider = false, +}) => { + const title = useFieldMessage(name); + + if (!content) return <>-{hasDivider && }; + return ( + <> + + {content || '-'} + + {hasDivider && } + + ); +}; const SpaceTitle: React.FC<{ name: string; context: any }> = ({ name, context }) => { - if (!context) return <> - return ( - - {name} - {context} - - ) -} + if (!context) return <>; + return ( + + {name} + {context} + + ); +}; const SharePage: React.FC = () => { - const { share_id } = useParams() as any - const { formatMessage } = useIntl() - - const [source, setSource] = React.useState() - const [loading, setLoading] = React.useState(true) - const [cLoading, setCLoading] = React.useState(true) - const [tableData, setTableData] = React.useState([]) - const [statisticsData, setStatisticsData] = React.useState({}) - - const getFuncChartData = async (params: any) => { - setCLoading(true) - const { data, code } = await queryFuncAnalysisList({ ...params, share_id }) - setCLoading(false) - if (code !== 200) return - const { case_map, job_list, case_statistics } = data - setSource((p: any) => ({ ...p, case_map })) - setTableData(job_list) - setStatisticsData(case_statistics) + const { share_id } = useParams() as any; + const { formatMessage } = useIntl(); + + const [source, setSource] = React.useState(); + const [loading, setLoading] = React.useState(true); + const [cLoading, setCLoading] = React.useState(true); + const [tableData, setTableData] = React.useState([]); + const [statisticsData, setStatisticsData] = React.useState({}); + + const getFuncChartData = async (params: any) => { + setCLoading(true); + const { data, code } = await queryFuncAnalysisList({ ...params, share_id }); + setCLoading(false); + if (code !== 200) return; + const { case_map, job_list, case_statistics } = data; + setSource((p: any) => ({ ...p, case_map })); + setTableData(job_list); + setStatisticsData(case_statistics); + }; + + const init = async () => { + setLoading(true); + const { data, code } = await getShareInfo({ share_id }); + setLoading(false); + + if (code !== 200) return; + const { provider_env, test_type, case_statistics } = data; + + setSource({ + ...data, + test_type_name: formatMessage({ id: `analysis.${test_type}` }), + provider_name: formatMessage({ id: provider_env }), + }); + setStatisticsData(case_statistics); + + if (test_type !== 'functional' && !data?.fetchData) { + setCLoading(false); } - const init = async () => { - setLoading(true) - const { data, code } = await getShareInfo({ share_id }) - setLoading(false) - - if (code !== 200) return - const { provider_env, test_type, metricList, case_statistics } = data - - setSource({ - ...data, - test_type_name: formatMessage({ id: `analysis.${test_type}` }), - provider_name: formatMessage({ id: provider_env }), - metric: metricList - }) - setStatisticsData(case_statistics) - - if (test_type !== 'functional' && !data?.fetchData) { - setCLoading(false) - } - - if (test_type === 'functional') { - if (data?.show_type === 'pass_rate' && !data?.case_name) { - setCLoading(false) - return - } - - if (data?.show_type === 'result_trend' && !data?.sub_case_name) { - setCLoading(false) - return - } - - getFuncChartData(data) - } - } + if (test_type === 'functional') { + if (data?.show_type === 'pass_rate' && !data?.case_name) { + setCLoading(false); + return; + } - React.useEffect(() => { - init() - }, []) + if (data?.show_type === 'result_trend' && !data?.sub_case_name) { + setCLoading(false); + return; + } - const handleValueChange = (list: any[]) => { - setTableData((p: any) => { - const ids = list.map(((i: any) => i.id)) - return p.filter((i: any) => !ids.includes(i.id)).concat(list).sort((a: any, b: any) => b.id - a.id) - }) + getFuncChartData(data); } - - if (loading) - return - - return ( -
-
-

- - 时序分析分享{source?.test_type_name} - -

- -
- { - source?.test_type === 'performance' && - - } - - - - {source?.start_time} - - {source?.end_time} - - } - /> -
- - - - - { - (source?.test_type === 'performance' && source?.metric) && - - Metric: - - { - source?.metric?.map((i: any) => (
{i}
)) - } -
-
- } - - { - source?.test_type !== 'performance' && source?.show_type !== 'pass_rate' && - - } - -
- - { - cLoading && - - } - - { - (source?.test_type === 'performance' && source?.fetchData?.length > 0) && -
- - { - source?.fetchData?.map((i: any) => { - const _props = { - key: i.metric, - fetchData: { ...i, share_id, }, - setLoading: setCLoading, - provider_env: source?.provider_env, - valueChange: handleValueChange - } - - return ( - source?.provider_env === "aliyun" ? - : - - ) - }) - } - -
- } - - { - (source?.test_type === 'functional' && source?.case_map) && -
- -
- } - - { - tableData?.length > 0 && -
- { }} - dataSource={tableData} - test_type={source?.test_type} - show_type={source?.show_type} - /> -
- } - - { - tableData?.length === 0 && -
- -
- } -
- ) -} - -export default SharePage \ No newline at end of file + }; + + React.useEffect(() => { + init(); + }, []); + + const handleValueChange = (list: any[]) => { + setTableData((p: any) => { + const ids = list.map((i: any) => i.id); + return p + .filter((i: any) => !ids.includes(i.id)) + .concat(list) + .sort((a: any, b: any) => b.id - a.id); + }); + }; + + if (loading) return ; + + return ( + +
+
+

+ + 时序分析分享 + + {source?.test_type_name} + +

+ +
+ {source?.test_type === 'performance' && ( + + )} + + + + {source?.start_time} + + {source?.end_time} + + } + /> +
+ + + + + {source?.test_type === 'performance' && source?.metric_list && ( + + Metric: + + {source?.metric_list?.map((i: any) => ( +
{i?.name}
+ ))} +
+
+ )} + + {source?.test_type !== 'performance' && source?.show_type !== 'pass_rate' && ( + + )} +
+ + {cLoading && } + + {source?.test_type === 'performance' && source?.metric_list?.length > 0 && ( +
+ + {source?.metric_list?.map((i: any) => { + return source?.provider_env === 'aliyun' ? ( + + ) : ( + + ); + })} + +
+ )} + + {source?.test_type === 'functional' && source?.case_map && ( +
+ +
+ )} + + {tableData?.length > 0 && ( +
+ {}} + dataSource={tableData} + test_type={source?.test_type} + show_type={source?.show_type} + /> +
+ )} + + {tableData?.length === 0 && ( +
+ +
+ )} +
+
+ ); +}; + +export default SharePage; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalPassRate.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalPassRate.tsx index 6d62395b49b86ffd6e778617754402a07a088e9b..ba5d079d8f7d99f926f366a8b91beafc3329b62e 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalPassRate.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalPassRate.tsx @@ -1,109 +1,100 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React from "react" -import { Table, Row, Select, Col } from "antd" -import styles from '../index.less' +import React from 'react'; +import { Table, Row, Select, Col } from 'antd'; +import styles from '../index.less'; const FunctionalPassRate: React.FC = (props) => { - const { suiteList = [], test_type, isFetching, onChange, basicValues } = props + const { suiteList = [], test_type, isFetching, onChange, test_suite_id, test_case_id } = props; - const [activeSuite, setActiveSuite] = React.useState(undefined) - const [activeConf, setActiveConf] = React.useState(undefined) + const [activeSuite, setActiveSuite] = React.useState(undefined); + const [activeConf, setActiveConf] = React.useState(undefined); - React.useEffect(() => { - if (suiteList?.length > 0) { - if (basicValues) { - const { test_suite_id, test_case_id } = basicValues - if (test_suite_id && test_case_id) { - setActiveSuite(+ test_suite_id) - setActiveConf(+ test_case_id) - } - } - else { - const firstSuite = suiteList?.[0] - if (firstSuite) { - setActiveSuite(firstSuite?.test_suite_id) - setActiveConf(firstSuite?.test_case_list?.[0]?.test_case_id) - } - } + React.useEffect(() => { + if (suiteList?.length > 0) { + if (test_suite_id && test_case_id) { + setActiveSuite(+test_suite_id); + setActiveConf(+test_case_id); + } else { + const firstSuite = suiteList?.[0]; + if (firstSuite) { + setActiveSuite(firstSuite?.test_suite_id); + setActiveConf(firstSuite?.test_case_list?.[0]?.test_case_id); } + } + } - return () => { - setActiveSuite(undefined) - setActiveConf(undefined) - } - }, [suiteList, basicValues]) + return () => { + setActiveSuite(undefined); + setActiveConf(undefined); + }; + }, [suiteList, test_suite_id, test_case_id]); - React.useEffect(() => { - onChange?.({ activeSuite, activeConf }) - }, [activeSuite, activeConf]) + React.useEffect(() => { + onChange?.({ activeSuite, activeConf }); + }, [activeSuite, activeConf]); - const confList = React.useMemo(() => { - if (!suiteList) return [] - for (let len = suiteList.length, i = 0; i < len; i++) - if (suiteList[i].test_suite_id === activeSuite) { - if (suiteList[i].test_case_list.length > 0) { - const test_case_id = activeConf || suiteList[i].test_case_list[0].test_case_id - if (!activeConf) - setActiveConf(test_case_id) - return suiteList[i].test_case_list - } - return [] - } - return [] - }, [activeSuite, suiteList, test_type]) + const confList = React.useMemo(() => { + if (!suiteList) return []; + for (let len = suiteList.length, i = 0; i < len; i++) + if (suiteList[i].test_suite_id === activeSuite) { + if (suiteList[i].test_case_list.length > 0) { + const test_case_id = activeConf || suiteList[i].test_case_list[0].test_case_id; + if (!activeConf) setActiveConf(test_case_id); + return suiteList[i].test_case_list; + } + return []; + } + return []; + }, [activeSuite, suiteList, test_type]); - return ( - - - - Test Suite: - { + setActiveSuite(v); + setActiveConf(null); + }} + filterOption={(input, option: any) => + option.label?.toLowerCase().indexOf(input?.toLowerCase()) >= 0 + } + showSearch + placeholder="请选择Test Suite" + value={activeSuite} + options={suiteList?.map((i: any) => ({ + value: i.test_suite_id, + label: i.test_suite_name, + }))} + /> - ) -} + + + setActiveConf(list[0]), + }} + onRow={(record) => ({ + onClick: () => { + setActiveConf(record.test_case_id); + }, + })} + pagination={false} + /> + + + ); +}; -export default FunctionalPassRate \ No newline at end of file +export default FunctionalPassRate; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalUnPassRate.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalUnPassRate.tsx index 19add0f8950cf6ba024a96e9ac4f7ed60ba39489..1d2dc04106529bef4da62d4b0346a0d03ea49597 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalUnPassRate.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/FunctionalUnPassRate.tsx @@ -1,156 +1,162 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React from "react" -import { Table, Row, Col, Select } from "antd" -import styles from '../index.less' -import { useAnalysisProvider } from "../../provider" +import React from 'react'; +import { Table, Row, Col, Select } from 'antd'; +import styles from '../index.less'; +import { useAnalysisProvider } from '../../provider'; const FunctionalPassRate: React.FC = (props) => { - const { suiteList = [], test_type, isFetching, show_type, onChange, basicValues, runGetMetrics } = props - const { metrics = [] } = useAnalysisProvider() + const { + suiteList = [], + test_type, + isFetching, + show_type, + onChange, + test_suite_id, + test_case_id, + runGetMetrics, + sub_case_name, + } = props; - const [activeSuite, setActiveSuite] = React.useState(undefined) - const [activeConf, setActiveConf] = React.useState(undefined) - const [selectSubcase, setSelectSubcase] = React.useState(undefined) + const { metrics = [] } = useAnalysisProvider(); - React.useEffect(() => { - if (suiteList?.length > 0) { - if (basicValues) { - const { test_suite_id, test_case_id } = basicValues - if (test_suite_id && test_case_id) { - setActiveSuite(+ test_suite_id) - setActiveConf(+ test_case_id) - } - } - else { - const firstSuite = suiteList?.[0] - if (firstSuite) { - setActiveSuite(firstSuite?.test_suite_id) - setActiveConf(firstSuite?.test_case_list?.[0]?.test_case_id) - } - } - } - return () => { - setActiveSuite(undefined) - setActiveConf(undefined) - setSelectSubcase(undefined) + const [activeSuite, setActiveSuite] = React.useState(undefined); + const [activeConf, setActiveConf] = React.useState(undefined); + const [selectSubcase, setSelectSubcase] = React.useState(undefined); + + React.useEffect(() => { + if (suiteList?.length > 0) { + if (test_suite_id && test_case_id) { + setActiveSuite(+test_suite_id); + setActiveConf(+test_case_id); + setSelectSubcase(sub_case_name); + } else { + const firstSuite = suiteList?.[0]; + if (firstSuite) { + setActiveSuite(firstSuite?.test_suite_id); + setActiveConf(firstSuite?.test_case_list?.[0]?.test_case_id); } - }, [suiteList, basicValues]) + } + } + return () => { + setActiveSuite(undefined); + setActiveConf(undefined); + setSelectSubcase(undefined); + }; + }, [suiteList, test_suite_id, test_case_id, sub_case_name]); - React.useEffect(() => { - onChange?.({ activeSuite, activeConf, selectSubcase }) - }, [activeSuite, activeConf, selectSubcase]) + React.useEffect(() => { + onChange?.({ activeSuite, activeConf, selectSubcase }); + }, [activeSuite, activeConf, selectSubcase]); - React.useEffect(() => { - if (activeConf && activeSuite) { - runGetMetrics({ test_suite_id: activeSuite, test_case_id: activeConf }) - } - }, [activeConf, activeSuite]) + React.useEffect(() => { + if (activeConf && activeSuite) { + runGetMetrics({ test_suite_id: activeSuite, test_case_id: activeConf }); + } + }, [activeConf, activeSuite]); - const confList = React.useMemo(() => { - if (!suiteList) return [] - for (let len = suiteList.length, i = 0; i < len; i++) - if (suiteList[i].test_suite_id === activeSuite) { - if (suiteList[i].test_case_list.length > 0) { - const test_case_id = activeConf || suiteList[i].test_case_list[0].test_case_id - if (!activeConf) - setActiveConf(test_case_id) - return suiteList[i].test_case_list - } - return [] - } - return [] - }, [activeSuite, suiteList, test_type]) + const confList = React.useMemo(() => { + if (!suiteList) return []; + for (let len = suiteList.length, i = 0; i < len; i++) + if (suiteList[i].test_suite_id === activeSuite) { + if (suiteList[i].test_case_list.length > 0) { + const test_case_id = activeConf || suiteList[i].test_case_list[0].test_case_id; + if (!activeConf) setActiveConf(test_case_id); + return suiteList[i].test_case_list; + } + return []; + } + return []; + }, [activeSuite, suiteList, test_type]); - return ( + return ( + + - - - - - Test Suite: - { - setActiveConf(test_case_id) - if (test_type !== 'performance' && show_type !== 'pass_rate') - /* requestSubcaseList({ test_case_id, test_suite_id: activeSuite }) */ - setSelectSubcase([]) - }} - filterOption={(input, option: any) => - option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 - } - showSearch - placeholder="请选择Test Conf" - value={activeConf} - options={confList?.map((i: any) => ({ - value: i.test_case_id, - label: i.test_case_name - }))} - /> - - - - - -
record} - size="small" - loading={isFetching} - scroll={{ y: 320 }} - className={styles.selectTable} - rowSelection={{ - type: 'radio', - selectedRowKeys: [selectSubcase], - onChange: (list: any) => { - setSelectSubcase(list?.[0]) - } - }} - onRow={ - record => ({ - onClick: () => { - setSelectSubcase(record) - } - }) - } - pagination={{ - hideOnSinglePage: true, - pageSize: 2000, - style: { marginBottom: 0 } - }} - /> - + + + Test Suite: + { + setActiveConf(test_case_id); + if (test_type !== 'performance' && show_type !== 'pass_rate') + /* requestSubcaseList({ test_case_id, test_suite_id: activeSuite }) */ + setSelectSubcase([]); + }} + filterOption={(input, option: any) => + option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 + } + showSearch + placeholder="请选择Test Conf" + value={activeConf} + options={confList?.map((i: any) => ({ + value: i.test_case_id, + label: i.test_case_name, + }))} + /> + + - ) -} + + +
record} + size="small" + loading={isFetching} + scroll={{ y: 320 }} + className={styles.selectTable} + rowSelection={{ + type: 'radio', + selectedRowKeys: [selectSubcase], + onChange: (list: any) => { + setSelectSubcase(list?.[0]); + }, + }} + onRow={(record) => ({ + onClick: () => { + setSelectSubcase(record); + }, + })} + pagination={{ + hideOnSinglePage: true, + pageSize: 2000, + style: { marginBottom: 0 }, + }} + /> + + + ); +}; -export default FunctionalPassRate \ No newline at end of file +export default FunctionalPassRate; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/Performance.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/Performance.tsx index 44625624af04cefaeef28028a1f8e7af536e90df..3e55296ef47c91da6beab1d1b7fcd788fd9f6fd9 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/Performance.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/Performance.tsx @@ -1,163 +1,155 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React from "react" -import { useIntl } from "umi" -import { Table, Row, Col, Select } from "antd" -import styles from '../index.less' -import { useAnalysisProvider } from "../../provider" +import React from 'react'; +import { useIntl } from 'umi'; +import { Table, Row, Col, Select } from 'antd'; +import styles from '../index.less'; +import { useAnalysisProvider } from '../../provider'; const Performance: React.FC = (props) => { - const { suiteList, onChange, runGetMetrics, visible, basicValues } = props - const { formatMessage } = useIntl() - const { metrics = [] } = useAnalysisProvider() + const { suiteList, onChange, runGetMetrics, visible, test_suite_id, test_case_id, metric } = + props; + const { formatMessage } = useIntl(); + const { metrics = [] } = useAnalysisProvider(); - const [activeSuite, setActiveSuite] = React.useState() - const [activeConf, setActiveConf] = React.useState() - const [selectMetric, setSelectMetric] = React.useState() + const [activeSuite, setActiveSuite] = React.useState(); + const [activeConf, setActiveConf] = React.useState(); + const [selectMetric, setSelectMetric] = React.useState(); - React.useEffect(() => { - if (suiteList?.length > 0) { - if (basicValues) { - const { test_suite_id, test_case_id } = basicValues - if (test_suite_id && test_case_id) { - setActiveSuite(+ test_suite_id) - setActiveConf(+ test_case_id) - setSelectMetric(basicValues?.metric) - } - } - else { - const firstSuite = suiteList?.[0] - if (firstSuite) { - setActiveSuite(firstSuite?.test_suite_id) - setActiveConf(firstSuite?.test_case_list?.[0]?.test_case_id) - } - } + React.useEffect(() => { + if (suiteList?.length > 0) { + if (test_suite_id && test_case_id) { + if (test_suite_id && test_case_id) { + setActiveSuite(+test_suite_id); + setActiveConf(+test_case_id); + setSelectMetric(metric); } - - return () => { - setActiveConf(undefined) - setActiveSuite(undefined) - setSelectMetric([]) + } else { + const firstSuite = suiteList?.[0]; + if (firstSuite) { + setActiveSuite(firstSuite?.test_suite_id); + setActiveConf(firstSuite?.test_case_list?.[0]?.test_case_id); } - }, [basicValues, suiteList]) + } + } - React.useEffect(() => { - if (!visible) { - setSelectMetric([]) - } - }, [visible]) + return () => { + setActiveConf(undefined); + setActiveSuite(undefined); + setSelectMetric([]); + }; + }, [test_suite_id, test_case_id, metric, suiteList]); - const currentSuite = React.useMemo(() => { - if (!suiteList) return - return suiteList.filter((i: any) => (i.test_suite_id === activeSuite))[0] - }, [suiteList, activeSuite]) + React.useEffect(() => { + if (!visible) { + setSelectMetric([]); + } + }, [visible]); - React.useEffect(() => { - if (activeConf && activeSuite) { - runGetMetrics({ test_suite_id: activeSuite, test_case_id: activeConf }) - } - }, [activeConf, activeSuite]) + const currentSuite = React.useMemo(() => { + if (!suiteList) return; + return suiteList.filter((i: any) => i.test_suite_id === activeSuite)[0]; + }, [suiteList, activeSuite]); + + React.useEffect(() => { + if (activeConf && activeSuite) { + runGetMetrics({ test_suite_id: activeSuite, test_case_id: activeConf }); + } + }, [activeConf, activeSuite]); - React.useMemo(() => { - if (!currentSuite) return - const { test_case_list } = currentSuite + React.useMemo(() => { + if (!currentSuite) return; + const { test_case_list } = currentSuite; - const rl = activeConf ? test_case_list.filter((i: any) => (i.test_case_id === activeConf))[0] : test_case_list[0] - if (rl && !activeConf) - setActiveConf(rl.test_case_id) - return rl - }, [currentSuite, activeConf]) + const rl = activeConf + ? test_case_list.filter((i: any) => i.test_case_id === activeConf)[0] + : test_case_list[0]; + if (rl && !activeConf) setActiveConf(rl.test_case_id); + return rl; + }, [currentSuite, activeConf]); - React.useEffect(() => { - onChange?.({ activeSuite, activeConf, selectMetric }) - }, [activeSuite, activeConf, selectMetric]) + React.useEffect(() => { + onChange?.({ activeSuite, activeConf, selectMetric }); + }, [activeSuite, activeConf, selectMetric]); - return ( - - - - - - Test Suite: - { - setActiveConf(test_case_id) - setSelectMetric([]) - // requestMetricList({ test_suite_id: activeSuite, test_case_id }) - }} - filterOption={(input, option: any) => - option.label?.toLowerCase().indexOf(input.toLowerCase()) >= 0 - } - showSearch - placeholder="请选择Test Conf" - value={activeConf} - options={ - currentSuite?.test_case_list?.map((i: any) => ({ - value: i.test_case_id, - label: i.test_case_name - })) - } - /> - - - - - -
record} - size="small" - scroll={{ y: 320 }} - // loading={fetch} - rowSelection={{ - selectedRowKeys: selectMetric, - onChange: (list: any) => { - setSelectMetric(list) - } - }} - onRow={ - record => ({ - onClick: () => { - if (selectMetric.includes(record)) { - setSelectMetric(selectMetric.filter((i: any) => i !== record)) - } - else - setSelectMetric(selectMetric.concat(record)) - } - }) - } - className={styles.selectTable} - pagination={false} - /> - + return ( + + + + + + Test Suite: + { + setActiveConf(test_case_id); + setSelectMetric([]); + // requestMetricList({ test_suite_id: activeSuite, test_case_id }) + }} + filterOption={(input, option: any) => + option.label?.toLowerCase().indexOf(input.toLowerCase()) >= 0 + } + showSearch + placeholder="请选择Test Conf" + value={activeConf} + options={currentSuite?.test_case_list?.map((i: any) => ({ + value: i.test_case_id, + label: i.test_case_name, + }))} + /> + + - ) -} + + +
record} + size="small" + scroll={{ y: 320 }} + // loading={fetch} + rowSelection={{ + selectedRowKeys: selectMetric, + onChange: (list: any) => { + setSelectMetric(list); + }, + }} + onRow={(record) => ({ + onClick: () => { + if (selectMetric?.includes(record)) { + setSelectMetric(selectMetric.filter((i: any) => i !== record)); + } else setSelectMetric((selectMetric || []).concat(record)); + }, + })} + className={styles.selectTable} + pagination={false} + /> + + + ); +}; -export default Performance \ No newline at end of file +export default Performance; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/layouts.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/layouts.tsx index c79419d2bb0dee4e302fd8233b6d52d83392da06..84060d6234c548f31621847445c40887841a35c8 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/layouts.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/MetricSelectModal/layouts.tsx @@ -1,166 +1,154 @@ -import React from "react"; -import { Modal, Space, Button, Spin } from "antd" -import { FormattedMessage, useRequest, useParams } from "umi" +import React from 'react'; +import { Modal, Space, Button, Spin } from 'antd'; +import { FormattedMessage, useParams, useRequest } from 'umi'; -import Performance from "./Performance"; -import FunctionalPassRate from "./FunctionalPassRate"; -import FunctionalUnPassRate from "./FunctionalUnPassRate" +import Performance from './Performance'; +import FunctionalPassRate from './FunctionalPassRate'; +import FunctionalUnPassRate from './FunctionalUnPassRate'; -import { getSelectMetricOrSubcase } from '../../services' +import { getSelectMetricOrSubcase } from '../../services'; import { useAnalysisProvider } from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/provider'; const MetricSelectDrawerLayout: React.ForwardRefRenderFunction = (props, ref) => { - const { test_type, project_id, provider_env, onOk, show_type, tag, start_time, end_time, metricData } = props - const { ws_id } = useParams() as any - - const { suiteList, suiteListLoading, setMetrics } = useAnalysisProvider() - - const [visible, setVisible] = React.useState(false) - const [info, setInfo] = React.useState({}) - - const defaultParams = { ws_id, test_type, provider_env } - - const { data: metrics, run: runGetMetrics } = useRequest((params: any) => getSelectMetricOrSubcase({ - ...defaultParams, - project_id, start_time, end_time, tag, - ...params, - }), { manual: true }) - - React.useEffect(() => { - setMetrics(metrics) - }, [metrics]) - - React.useImperativeHandle(ref, () => ({ - show: async (vals: any) => { - setVisible(true) - }, - })) - - const handleClose = React.useCallback(() => { - setVisible(false) - setInfo({}) - }, []) - - const title = React.useMemo(() => { - const { selectMetric } = info - if (test_type === 'performance') - return `Metric${selectMetric?.length ? `(${selectMetric?.length})` : ''}` - return show_type === 'pass_rate' ? 'Test Conf' : 'Test Case' - }, [test_type, show_type, info]) - - const canSubmit = React.useMemo(() => { - const { selectMetric, selectSubcase, activeConf } = info - if (test_type === 'performance') - return selectMetric?.length > 0 - if (show_type !== 'pass_rate') - return selectSubcase?.length > 0 - return activeConf || false - }, [test_type, show_type, info]) - - const selectSuiteName = React.useMemo(() => { - if (!suiteList) return - for (let len = suiteList.length, i = 0; i < len; i++) - if (suiteList[i].test_suite_id === info?.activeSuite) - return suiteList[i].test_suite_name - return - }, [suiteList, info]) - - const confList = React.useMemo(() => { - if (!suiteList) return - for (let len = suiteList.length, i = 0; i < len; i++) - if (suiteList[i].test_suite_id === info?.activeSuite) { - if (suiteList[i].test_case_list.length > 0) { - return suiteList[i].test_case_list - } - return [] - } - return [] - }, [suiteList, info]) - - const selectConfName = React.useMemo(() => { - if (!confList) return - for (let len = confList.length, i = 0; i < len; i++) - if (confList[i].test_case_id === info?.activeConf) - return confList[i].test_case_name - return - }, [confList, info]) - - const handleOk = (): any => { - const { activeSuite, activeConf, selectMetric, selectSubcase } = info - const params: any = { test_suite_id: activeSuite } - - if (test_type === 'performance') { - params.test_case_id = activeConf - params.metric = selectMetric - params.title = `${selectSuiteName}/${selectConfName}`///${ selectMetric.toString() } + const { onOk } = props; + + const { ws_id } = useParams() as any; + + const { suiteList, suiteListLoading, setMetrics } = useAnalysisProvider(); + + const [visible, setVisible] = React.useState(false); + const [info, setInfo] = React.useState({}); + const [source, setSource] = React.useState({}); + const { test_type, show_type } = source || {}; + + const { data: metrics, run: runGetMetrics } = useRequest( + (params) => getSelectMetricOrSubcase({ ...source, ...params, ws_id }), + { + manual: true, + }, + ); + + React.useEffect(() => { + setMetrics(metrics); + }, [metrics]); + + React.useImperativeHandle(ref, () => ({ + show: async (vals: any) => { + setVisible(true); + setSource(vals); + }, + })); + + const handleClose = React.useCallback(() => { + setVisible(false); + setInfo({}); + }, []); + + const title = React.useMemo(() => { + const { selectMetric } = info; + if (test_type === 'performance') + return `Metric${selectMetric?.length ? `(${selectMetric?.length})` : ''}`; + return show_type === 'pass_rate' ? 'Test Conf' : 'Test Case'; + }, [test_type, show_type, info]); + + const canSubmit = React.useMemo(() => { + const { selectMetric, selectSubcase, activeConf } = info; + if (test_type === 'performance') return selectMetric?.length > 0; + if (show_type !== 'pass_rate') return selectSubcase?.length > 0; + return activeConf || false; + }, [test_type, show_type, info]); + + const selectSuiteName = React.useMemo(() => { + if (!suiteList) return; + for (let len = suiteList.length, i = 0; i < len; i++) + if (suiteList[i].test_suite_id === info?.activeSuite) return suiteList[i].test_suite_name; + return; + }, [suiteList, info]); + + const confList = React.useMemo(() => { + if (!suiteList) return; + for (let len = suiteList.length, i = 0; i < len; i++) + if (suiteList[i].test_suite_id === info?.activeSuite) { + if (suiteList[i].test_case_list.length > 0) { + return suiteList[i].test_case_list; } - - if (test_type === 'functional') { - params.test_case_id = activeConf - params.title = `${selectSuiteName}/${selectConfName}` - if (show_type !== 'pass_rate') { - params.sub_case_name = selectSubcase - params.test_case_id = activeConf - params.title = `${selectSuiteName}/${selectConfName}` ///${ selectSubcase.toString() } - } - } - - onOk(params) - handleClose() + return []; + } + return []; + }, [suiteList, info]); + + const selectConfName = React.useMemo(() => { + if (!confList) return; + for (let len = confList.length, i = 0; i < len; i++) + if (confList[i].test_case_id === info?.activeConf) return confList[i].test_case_name; + return; + }, [confList, info]); + + const handleOk = (): any => { + const { activeSuite, activeConf, selectMetric, selectSubcase } = info; + const params: any = { test_suite_id: activeSuite }; + + if (test_type === 'performance') { + params.test_case_id = activeConf; + params.metric = selectMetric; + params.title = `${selectSuiteName}/${selectConfName}`; ///${ selectMetric.toString() } } - const baseProps = { - test_type, - show_type, - basicValues: metricData, - project_id, - provider_env, - suiteList, - loading: suiteListLoading, - visible, - runGetMetrics, - metrics, - onChange: (data: any) => { - setInfo((p: any) => ({ ...p, ...data })) - } + if (test_type === 'functional') { + params.test_case_id = activeConf; + params.title = `${selectSuiteName}/${selectConfName}`; + if (show_type !== 'pass_rate') { + params.sub_case_name = selectSubcase; + params.test_case_id = activeConf; + params.title = `${selectSuiteName}/${selectConfName}`; ///${ selectSubcase.toString() } + } } - return ( - - - - - } - > - - { - test_type === "performance" && - - } - { - test_type !== "performance" && - ( - show_type === "pass_rate" ? - : - - ) - } - - - ) -} - -export default React.forwardRef(MetricSelectDrawerLayout) \ No newline at end of file + onOk(params); + handleClose(); + }; + + const baseProps = { + ...source, + suiteList, + loading: suiteListLoading, + visible, + runGetMetrics, + metrics, + onChange: (data: any) => { + setInfo((p: any) => ({ ...p, ...data })); + }, + }; + + return ( + + + + + } + > + + {test_type === 'performance' && } + {test_type !== 'performance' && + (show_type === 'pass_rate' ? ( + + ) : ( + + ))} + + + ); +}; + +export default React.forwardRef(MetricSelectDrawerLayout); diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Cluster.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Cluster.tsx index fd571ea25d12842c41dfacce75f4fbae46dbf9ca..621ef3c5b3475008e66d1db7768bcccd852c8382 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Cluster.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Cluster.tsx @@ -1,263 +1,353 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useRequest, useIntl, useParams } from "umi" -import React from "react" +import { useIntl, useParams } from 'umi'; +import React from 'react'; import { queryPerfAnalysisList } from '../../services'; -import * as echarts from 'echarts' -import { Title, CardWrapper, ChartWrapper } from "./styled"; -import { textTip, serverLinkTip, commitLinkTip } from '..' -import { Row, Space, Typography } from "antd"; -import Legend from "./Legend" -import MetricDropdown from "./MetricDropdown"; -import { targetJump } from "@/utils/utils" -import { axisUnitFormatter, } from "./Standalone"; +import * as echarts from 'echarts'; +import { Title, CardWrapper, ChartWrapper } from './styled'; +import { textTip, serverLinkTip, commitLinkTip } from '..'; +import { Checkbox, Row, Space, Typography, Spin } from 'antd'; +import Legend from './Legend'; +import MetricDropdown from './MetricDropdown'; +import { targetJump } from '@/utils/utils'; +import { axisUnitFormatter } from './Standalone'; +import { useAnalysisProvider } from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/provider'; -const symbol = 'path://M873,435C877.4182739257812,435,881,438.58172607421875,881,443C881,447.41827392578125,877.4182739257812,451,873,451C868.5817260742188,451,865,447.41827392578125,865,443C865,438.58172607421875,868.5817260742188,435,873,435ZM873,436C869.134033203125,436,866,439.1340026855469,866,443C866,446.8659973144531,869.134033203125,450,873,450C876.865966796875,450,880,446.8659973144531,880,443C880,439.1340026855469,876.865966796875,436,873,436ZM873,439C875.2091674804688,439,877,440.7908630371094,877,443C877,445.2091369628906,875.2091674804688,447,873,447C870.7908325195312,447,869,445.2091369628906,869,443C869,440.7908630371094,870.7908325195312,439,873,439Z' +const symbol = + 'path://M873,435C877.4182739257812,435,881,438.58172607421875,881,443C881,447.41827392578125,877.4182739257812,451,873,451C868.5817260742188,451,865,447.41827392578125,865,443C865,438.58172607421875,868.5817260742188,435,873,435ZM873,436C869.134033203125,436,866,439.1340026855469,866,443C866,446.8659973144531,869.134033203125,450,873,450C876.865966796875,450,880,446.8659973144531,880,443C880,439.1340026855469,876.865966796875,436,873,436ZM873,439C875.2091674804688,439,877,440.7908630371094,877,443C877,445.2091369628906,875.2091674804688,447,873,447C870.7908325195312,447,869,445.2091369628906,869,443C869,440.7908630371094,870.7908325195312,439,873,439Z'; const ClusterChart: React.FC = (props) => { - const { fetchData = {}, setFetchData, provider_env, valueChange, setLoading } = props - const { formatMessage } = useIntl() - const { ws_id, share_id } = useParams() as any - const ref = React.useRef(null) - const [chart, setChart] = React.useState(undefined) - const numUnitLocale = (str: any) => new Map([ - [1, formatMessage({ id: 'analysis.wan' })], - [2, formatMessage({ id: 'analysis.yi' })], - [3, formatMessage({ id: 'analysis.zhao' })] - ]).get(str) + const { + valueChange, + setLoading: setPageLoading, + metricList, + opts, + name: metric_name, + refreshKey, + } = props; + const { info, setInfo } = useAnalysisProvider(); + const { provider_env } = info; - const { data, mutate } = useRequest( - (params = fetchData) => queryPerfAnalysisList(params), - { - debounceInterval: 200, - refreshDeps: [fetchData], - ready: !!fetchData?.metric, - onSuccess() { - setLoading(false) - }, - onError() { - setLoading(false) - } - } - ) + const { formatMessage } = useIntl(); + const { ws_id, share_id } = useParams() as any; + const ref = React.useRef(null); + const [chart, setChart] = React.useState(undefined); + const [dataSet, setDataSet] = React.useState(); + const [lineIgnore, setLineIgnore] = React.useState([]); + const [loading, setLoading] = React.useState(true); + const [chartSource, setChartSource] = React.useState(); + const numUnitLocale = (str: any) => + [ + [formatMessage({ id: 'analysis.wan' })], + [formatMessage({ id: 'analysis.yi' })], + [formatMessage({ id: 'analysis.zhao' })], + ][str]; - React.useEffect(() => { - if (!data) return - const { job_list } = data - if (!job_list) return - valueChange?.(job_list) - }, [data]) + const init = async (params: any) => { + setLoading(true); + const { data, code, msg } = await queryPerfAnalysisList(params); + setLoading(false); + setPageLoading(false); + + if (code !== 200) { + setDataSet(undefined); + return console.log(msg); + } + setDataSet(data); + }; - const handleMetricChange = ($query: any) => { - const { metric } = $query - setFetchData?.((p: any) => { - return p.map((x: any) => { - if (x.key === fetchData?.key) - return { - ...x, - metric, - } - return x - }) - }) - if (metric.length === 0) mutate(null) + React.useEffect(() => { + if (!!metricList) { + let $params: any = { metric: metricList, share_id }; + if (!share_id) { + $params = { ...info, ...$params, ws_id }; + } + init($params); } + }, [metricList, refreshKey]); + + const handleChangeOpt = (vals: any) => { + setInfo((pre: any) => ({ + ...pre, + metric: pre.metric?.map((ctx: any) => { + if (ctx.name === metric_name) { + return { + ...ctx, + opts: vals, + }; + } + return ctx; + }), + })); + }; + + React.useEffect(() => { + if (!dataSet) return; + const { job_list } = dataSet; + if (!job_list) return; + valueChange?.(job_list); + }, [dataSet]); - const cts = React.useMemo(() => { - if (!data) return - const { metric_map } = data + const handleMetricChange = (list: any) => { + setInfo((pre: any) => ({ + ...pre, + metric: pre.metric?.map((ctx: any) => { + if (ctx.name === metric_name) { + return { + ...ctx, + metricList: list, + }; + } + return ctx; + }), + })); + if (list.length === 0) setDataSet(undefined); + }; + + const findLastNonEmptyIndex = (arr: any) => { + for (let i = arr.length - 1; i >= 0; i--) { + if (!!arr[i]?.[1]) return i; + } + return 0; // 如果数组中所有元素都是空值,则返回-1 + }; - return Object.keys(metric_map)?.reduce((pre: any, metric_name: any) => { - const ctx = metric_map[metric_name] - const { result_data, baseline_data } = ctx + React.useEffect(() => { + if (!ref.current) return; + if (!dataSet) return; - const { xAxis, lineDatas } = Object.entries(result_data).reduce( - (p: any, cur: any) => { - const [date, vals] = cur - if (vals) { - const { value, ...rest } = vals - const val = value ? Number(Number(value).toFixed(2)) : null + const myChart = echarts.init(ref.current); + myChart.clear(); + myChart.showLoading(); + const { metric_map } = dataSet; + const cts = Object.keys(metric_map)?.reduce( + (pre: any, metric_name: any) => { + const ctx = metric_map[metric_name]; + const { result_data, baseline_data } = ctx; - p.lineDatas.push({ - ...rest, - date, - baseline_data, - value: val, - symbol: value?.note ? symbol : undefined, - }) - } - else { - p.lineDatas.push({ date, value: null }) - } + const newDataSet = Object.entries(result_data).filter((ctx: any) => { + if (opts?.includes('data')) { + const [, val] = ctx; + return !!val; + } + return true; + }); + const lastIdx = findLastNonEmptyIndex(newDataSet); - if (!p.xAxis.includes(data)) - p.xAxis.push(date) + const allDataList = newDataSet.reduce( + (p: any, cur: any, idx: number) => { + const [date, vals] = cur; + if (vals) { + const { value, ...rest } = vals; + const val = value ? Number(Number(value).toFixed(2)) : null; + const lineProps = { + ...rest, + date, + baseline_data, + value: val, + symbol: value?.note ? symbol : undefined, + }; + if (lastIdx === idx) { + lineProps.label = { + show: true, + textStyle: { color: 'red' }, + }; + } + p.lineDatas.push(lineProps); + } else { + p.lineDatas.push({ date, value: null }); + } - return p - }, - { xAxis: [], lineDatas: [] } - ) + if (!p.xAxis.includes(newDataSet)) p.xAxis.push(date); - pre.title.push({ - name: metric_name, - isBaseline: 0 - }) + return p; + }, + { xAxis: [], lineDatas: [] }, + ); - pre.baseline.push({ - type: 'line', - name: metric_name, - symbol: 'none', - tooltip: { show: false }, - lineStyle: { - width: 1, - type: 'dashed' - }, - data: xAxis?.map((i: any) => ({ date: i, value: baseline_data.value })), - }) + const { xAxis, lineDatas } = allDataList; + pre.title.push({ + name: metric_name, + isBaseline: 0, + }); - pre.series.push({ - type: 'line', - name: metric_name, - showAllSymbol: true, - smooth: true, - symbolSize: 8, - connectNulls: false, - data: lineDatas - }) + pre.baseline.push({ + type: 'line', + name: metric_name, + symbol: 'none', + tooltip: { show: false }, + lineStyle: { + width: 1, + type: 'dashed', + }, + data: xAxis?.map((i: any) => ({ date: i, value: baseline_data.value })), + }); - pre.xAxis = Array.from(new Set(pre.xAxis.concat(xAxis))) - return pre - }, { baseline: [], title: [], series: [], xAxis: [] }) - }, [data]) + pre.series.push({ + type: 'line', + name: metric_name, + showAllSymbol: true, + smooth: true, + symbolSize: 8, + connectNulls: false, + data: lineDatas, + }); - React.useEffect(() => { - if (!ref.current) return - if (!cts) return - const { xAxis, series, title, baseline } = cts - const myChart = echarts.init(ref.current) - myChart.clear() - myChart.showLoading() + pre.xAxis = Array.from(new Set(pre.xAxis.concat(xAxis))); + return pre; + }, + { baseline: [], title: [], series: [], xAxis: [] }, + ); + setChartSource(cts); + const { xAxis, series, title, baseline } = cts; - myChart.setOption({ - legend: { - data: title.map((i: any) => i.name), - show: false, - }, - tooltip: { - // trigger: 'axis', - trigger: 'item', - axisPointer: { - snap: true, - type: 'cross', - }, - enterable: true, - backgroundColor: '#fff', - borderColor: "#fff", - textStyle: { - color: 'rgba(0,0,0,0.65)', - fontSize: 14, - }, - extraCssText: 'box-shadow: 0 2px 8px 0 rgba(0,0,0,0.15);border-radius: 2px;padding:12px;', - formatter: function (params: any) { - const item = params.data || {} - const { baseline_data } = item - const element = ( - `
+ myChart.setOption({ + legend: { + data: title.map((i: any) => i.name), + show: false, + }, + tooltip: { + // trigger: 'axis', + trigger: 'item', + axisPointer: { + snap: true, + type: 'cross', + }, + enterable: true, + backgroundColor: '#fff', + borderColor: '#fff', + textStyle: { + color: 'rgba(0,0,0,0.65)', + fontSize: 14, + }, + extraCssText: 'box-shadow: 0 2px 8px 0 rgba(0,0,0,0.15);border-radius: 2px;padding:12px;', + formatter: function (params: any) { + const item = params.data || {}; + const { baseline_data } = item; + const element = `
${params.marker} ${params.name} ${params.seriesName}
${commitLinkTip('JobID', item?.job_id, ws_id)} ${textTip('commit', item?.commit)} ${textTip('Avg', item?.value)} ${textTip('CV', item?.cv_value)} - ${textTip(formatMessage({ id: 'analysis.baseline.value' }), baseline_data?.value && Number(baseline_data?.value).toFixed(2))} - ${textTip(formatMessage({ id: 'analysis.baseline.cv' }), baseline_data?.cv_value)} + ${textTip( + formatMessage({ id: 'analysis.baseline.value' }), + baseline_data?.value && Number(baseline_data?.value).toFixed(2), + )} + ${textTip( + formatMessage({ id: 'analysis.baseline.cv' }), + baseline_data?.cv_value, + )} ${textTip('commit', item?.commit)} ${serverLinkTip(item?.server)} ${textTip(formatMessage({ id: 'analysis.specs' }), item?.instance_type)} ${textTip('Image', item?.image)} ${textTip('Bandwidth', item?.bandwidth)} ${textTip('RunMode', item?.run_mode)} - ${textTip(formatMessage({ id: 'analysis.table.column.note' }), item?.note)} - ${textTip(formatMessage({ id: 'analysis.chart.compare_result' }), item?.compare_result)} -
` - .trim() - ) - return `
${element}
` - }, - }, - grid: { left: '5%', right: 60, bottom: '10%', top: "5%" }, //left: 50, - xAxis: { - type: 'category', // 还有其他的type,可以去官网喵两眼哦 - data: xAxis, - axisLabel: { - showMaxLabel: true, - } - }, - yAxis: { - type: 'value', - axisLine: { show: false }, - axisTick: { show: false }, - splitLine: { show: false, lineStyle: { type: 'dashed' }, }, - axisLabel: { - formatter: (value: any) => axisUnitFormatter(value, numUnitLocale) - }, - }, - series: [ - ...series, - ...baseline - ], - }) + ${textTip( + formatMessage({ id: 'analysis.table.column.note' }), + item?.note, + )} + ${textTip( + formatMessage({ id: 'analysis.chart.compare_result' }), + item?.compare_result, + )} +
`.trim(); + return `
${element}
`; + }, + }, + grid: { left: '5%', right: 60, bottom: '10%', top: '5%' }, //left: 50, + xAxis: { + type: 'category', // 还有其他的type,可以去官网喵两眼哦 + data: xAxis, + axisLabel: { + showMaxLabel: true, + }, + }, + yAxis: { + type: 'value', + axisLine: { show: false }, + axisTick: { show: false }, + splitLine: { show: false, lineStyle: { type: 'dashed' } }, + axisLabel: { + formatter: (value: any) => axisUnitFormatter(value, numUnitLocale), + }, + }, + series: [...series, ...baseline], + }); - if (!share_id) - myChart.on("click", 'series.line', (params: any) => { - if (params?.data) { - const { job_id } = params?.data - if (job_id) targetJump(`/ws/${ws_id}/test_result/${job_id}`) - } - }) - - myChart.hideLoading() - setChart(myChart) - return () => { - myChart.dispose() - setChart(undefined) + if (!share_id) + myChart.on('click', 'series.line', (params: any) => { + if (params?.data) { + const { job_id } = params?.data; + if (job_id) targetJump(`/ws/${ws_id}/test_result/${job_id}`); } - }, [cts]) - - if (!data) return <> - if (data?.job_list?.length === 0) return <> - - return ( - - - <Space size={0}> - { - fetchData?.metric.map((t: any) => ( - <Typography.Text key={t}>{t}</Typography.Text> - )) - } - { - ws_id && - <MetricDropdown - onChange={handleMetricChange} - fetchData={fetchData} - /> - } - </Space> - + }); - - - { - cts.title?.map((i: any) => ( - - )) - } - - + myChart.hideLoading(); + setChart(myChart); + return () => { + myChart.dispose(); + setChart(undefined); + }; + }, [dataSet, opts]); - + return ( + + + <div style={{ flexWrap: 'wrap', width: '100%', display: 'flex', gap: '8px 16px' }}> + {metricList?.map((t: any) => ( + <Typography.Text key={t}>{t}</Typography.Text> + ))} + <div> + {ws_id && <MetricDropdown metricList={metricList} onChange={handleMetricChange} />} + </div> + </div> + <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}> + <Checkbox.Group + value={opts || [provider_env !== 'aliyun' && 'server', 'data'].filter(Boolean)} + onChange={handleChangeOpt} + style={{ fontWeight: 'normal' }} + disabled={!!share_id} + options={[ + { + label: '区分机器', + value: 'server', + disabled: provider_env === 'aliyun', + }, + { label: '排除空数据', value: 'data' }, + ]} + /> + </div> + - - ) -} + + + {chartSource?.title?.map((i: any) => ( + + ))} + + + + + + + ); +}; -export default React.memo(ClusterChart, (prev, next) => JSON.stringify(prev?.fetchData) === JSON.stringify(next?.fetchData)) \ No newline at end of file +export default ClusterChart; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Legend.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Legend.tsx index 70e8a8af863090b3299e5693d1ee1a7e573693c9..7d3eea9d3ee51289588258fae4b36ea20a8d7b55 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Legend.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Legend.tsx @@ -1,108 +1,132 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import styled from "styled-components"; -import React from "react" -import { Space, Typography } from "antd"; +import styled from 'styled-components'; +import React from 'react'; +import { Space, Typography } from 'antd'; type LineProps = { - borderColor?: string; - isBaseline?: number; -} + borderColor?: string; + isBaseline?: number; +}; const Line = styled.div.attrs((props: LineProps) => ({ - style: { - borderStyle: props?.isBaseline ? "dashed" : "solid", - borderColor: props.borderColor - } -})) ` - height: 0; - width: 10px; - border-width: 1px; -` + style: { + borderStyle: props?.isBaseline ? 'dashed' : 'solid', + borderColor: props.borderColor, + }, +}))` + height: 0; + width: 10px; + border-width: 1px; +`; type LegendBoxProps = { - state: boolean; -} + state: boolean; +}; -const unSelectColor = "rgba(0,0,0, .35)" +const unSelectColor = 'rgba(0,0,0, .35)'; -const LegendBox = styled(Space).attrs((props: any) => ({ - className: props.state ? "" : "legend_un_select" -})) ` - cursor: pointer; - &.legend_un_select { - .ant-typography { - color: ${unSelectColor}; - } - ${Line} { - border-color: ${unSelectColor} !important; - } +const LegendBox = styled.div.attrs((props: any) => ({ + className: props.state ? '' : 'legend_un_select', +}))` + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + flex-wrap: wrap; + &.legend_un_select { + .ant-typography { + color: ${unSelectColor}; + } + ${Line} { + border-color: ${unSelectColor} !important; } -` + } +`; -const Legend: React.FC = ({ name, chartRef, isBaseline, isStandalone, source, provider_env }) => { - const [state, setState] = React.useState(true) +const Legend: React.FC = (props) => { + const { + name, + chartRef, + isBaseline, + isStandalone, + source, + provider_env, + lineIgnore, + setLineIgnore, + opts, + } = props; - const handleClick = () => { - chartRef?.dispatchAction({ - type: 'legendToggleSelect', - name, - }) - } + const [state, setState] = React.useState(true); - const handleLegendSelectChange = (params: any) => { - const { selected } = params - setState(selected[name]) + const isServerLine = opts?.includes('server') || !!isBaseline; + const handleClick = () => { + if (isServerLine) { + chartRef?.dispatchAction({ + type: 'legendToggleSelect', + name, + }); + } else { + setLineIgnore((pre: any) => { + if (pre.includes(name)) return pre.filter((ctx: any) => ctx !== name); + return pre.concat(name); + }); } + }; - React.useEffect(() => { - if (!chartRef) return - if (!name) return - chartRef.on("legendselectchanged", { seriesName: name }, handleLegendSelectChange) - return () => { - chartRef.off("legendselectchanged", { seriesName: name }, handleLegendSelectChange) - } - }, [chartRef]) + const handleLegendSelectChange = (params: any) => { + const { selected } = params; + setState(selected[name]); + }; - if (isStandalone) - return ( - - { - source?.map((item: any) => ( - - )) - } - - ) + React.useEffect(() => { + if (!chartRef) return; + chartRef.on('legendselectchanged', { seriesName: name }, handleLegendSelectChange); + return () => { + chartRef.off('legendselectchanged', { seriesName: name }, handleLegendSelectChange); + }; + }, [chartRef, opts, lineIgnore]); + if (isStandalone) return ( - -
- - < Typography.Text style={{ fontSize: 10 }} ellipsis> - {name} - -
- { - provider_env === "aliyun" && - - - - 基线AVG值 - - - } -
- ) -} + + {source?.map((item: any) => ( + + ))} + + ); + + const boxColorState = React.useMemo(() => { + if (isServerLine) { + return state; + } + return !lineIgnore?.includes(name); + }, [opts, lineIgnore, state, isServerLine]); + + const borderColor = chartRef?.getModel()?.getColorFromPalette(name); + + return ( + +
+ {isServerLine && } + + {name} + +
+ {provider_env === 'aliyun' && ( + + {isServerLine && } + 基线AVG值 + + )} +
+ ); +}; -export default Legend \ No newline at end of file +export default Legend; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/MetricDropdown.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/MetricDropdown.tsx index a58011c1417b19193ce743df7b5ae3d6b4ed8e3f..74edfb1b90f2676ce74f1e47880a05ce320cb71c 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/MetricDropdown.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/MetricDropdown.tsx @@ -1,83 +1,85 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React from "react" -import { PlusCircleOutlined } from "@ant-design/icons" -import { Dropdown, Space, Table } from "antd"; -import { useAnalysisProvider } from "../../provider" -import styled from "styled-components"; -import { getSearchFilter } from "@/components/TableFilters"; +import React from 'react'; +import { PlusCircleOutlined } from '@ant-design/icons'; +import { Dropdown, Space, Table } from 'antd'; +import { useAnalysisProvider } from '../../provider'; +import styled from 'styled-components'; +import { getSearchFilter } from '@/components/TableFilters'; const DropdownMenu = styled(Space)` - background: #fff; - width: 322px; -` + background: #fff; + width: 322px; +`; const TableCls = styled(Table)` - .record-row-hide { - display: none; - } -` + .record-row-hide { + display: none; + } +`; const MetricDropdown: React.FC = (props) => { - const { fetchData, onChange } = props - const { metrics } = useAnalysisProvider() - const [params, setParams] = React.useState({}) - const [open, setOpen] = React.useState(false) - const [selectMetric, setSelectMetric] = React.useState(fetchData?.metric || []) + const { metricList, onChange } = props; + const { metrics } = useAnalysisProvider(); + const [params, setParams] = React.useState({}); + const [open, setOpen] = React.useState(false); + const [selectMetric, setSelectMetric] = React.useState(metricList || []); - React.useEffect(() => { - return () => { - setParams({}) - } - }, []) + React.useEffect(() => { + return () => { + setParams({}); + }; + }, []); - React.useEffect(() => { - if (!open) { - onChange?.({ ...fetchData, metric: selectMetric }) - } - }, [selectMetric, open]) + React.useEffect(() => { + if (!open) { + onChange?.(selectMetric); + } + }, [selectMetric, open]); - return ( - ( - - { - if (!params?.name) return '' - if (!~record.label?.indexOf(params?.name)) return "record-row-hide" - return "" - }} - rowSelection={{ - selectedRowKeys: selectMetric, - onChange: (list: any) => { - setSelectMetric(list) - } - }} - scroll={{ - y: 38 * 6, - }} - columns={[{ - dataIndex: "label", - title: "指标", - ...getSearchFilter(params, setParams, "name") - }]} - dataSource={metrics?.map((i: any) => ({ - value: i, - label: i - }))} - /> - - )} - > - - - ) -} + return ( + ( + + { + if (!params?.name) return ''; + if (!~record.label?.indexOf(params?.name)) return 'record-row-hide'; + return ''; + }} + rowSelection={{ + selectedRowKeys: selectMetric || [], + onChange: (list: any) => { + setSelectMetric(list); + }, + }} + scroll={{ + y: 38 * 6, + }} + columns={[ + { + dataIndex: 'label', + title: '指标', + ...getSearchFilter(params, setParams, 'name'), + }, + ]} + dataSource={metrics?.map((i: any) => ({ + value: i, + label: i, + }))} + /> + + )} + > + + + ); +}; -export default MetricDropdown \ No newline at end of file +export default MetricDropdown; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Standalone.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Standalone.tsx index a58865e8f48fed5cea90bc63553db8065d97cac8..7f424de5ddb86e6a56753ed48ef01b87a3f01016 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Standalone.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/Standalone.tsx @@ -1,371 +1,529 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useRequest, useIntl, useParams } from "umi" -import React from "react" +import { useIntl, useParams } from 'umi'; +import React from 'react'; import { queryPerfAnalysisList } from '../../services'; -import * as echarts from 'echarts' -import { Title, CardWrapper, ChartWrapper } from "./styled"; -import { textTip, serverLinkTip, commitLinkTip, renderProviderText, colors, isRepeat } from '..' -import { Row, Space, Spin, Typography } from "antd"; -import Legend from "./Legend" -import MetricDropdown from "./MetricDropdown"; -import { targetJump } from "@/utils/utils" - -const symbol = 'path://M873,435C877.4182739257812,435,881,438.58172607421875,881,443C881,447.41827392578125,877.4182739257812,451,873,451C868.5817260742188,451,865,447.41827392578125,865,443C865,438.58172607421875,868.5817260742188,435,873,435ZM873,436C869.134033203125,436,866,439.1340026855469,866,443C866,446.8659973144531,869.134033203125,450,873,450C876.865966796875,450,880,446.8659973144531,880,443C880,439.1340026855469,876.865966796875,436,873,436ZM873,439C875.2091674804688,439,877,440.7908630371094,877,443C877,445.2091369628906,875.2091674804688,447,873,447C870.7908325195312,447,869,445.2091369628906,869,443C869,440.7908630371094,870.7908325195312,439,873,439Z' +import * as echarts from 'echarts'; +import { Title, CardWrapper, ChartWrapper } from './styled'; +import { textTip, serverLinkTip, commitLinkTip, renderProviderText, colors, isRepeat } from '..'; +import { Checkbox, Row, Space, Spin, Typography } from 'antd'; +import Legend from './Legend'; +import MetricDropdown from './MetricDropdown'; +import { targetJump } from '@/utils/utils'; +import { useAnalysisProvider } from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/provider'; + +const symbol = + 'path://M873,435C877.4182739257812,435,881,438.58172607421875,881,443C881,447.41827392578125,877.4182739257812,451,873,451C868.5817260742188,451,865,447.41827392578125,865,443C865,438.58172607421875,868.5817260742188,435,873,435ZM873,436C869.134033203125,436,866,439.1340026855469,866,443C866,446.8659973144531,869.134033203125,450,873,450C876.865966796875,450,880,446.8659973144531,880,443C880,439.1340026855469,876.865966796875,436,873,436ZM873,439C875.2091674804688,439,877,440.7908630371094,877,443C877,445.2091369628906,875.2091674804688,447,873,447C870.7908325195312,447,869,445.2091369628906,869,443C869,440.7908630371094,870.7908325195312,439,873,439Z'; export function toFixed(number: any, precision: number) { - let str: any = number + '' - const len = str.length - let last = str.substring(len - 1, len) - if (last == '5') { - last = '6' - str = str.substring(0, len - 1) + last - return (str - 0).toFixed(precision) - } else { - return (+ number).toFixed(precision) - } + let str: any = number + ''; + const len = str.length; + let last = str.substring(len - 1, len); + if (last == '5') { + last = '6'; + str = str.substring(0, len - 1) + last; + return (str - 0).toFixed(precision); + } else { + return (+number).toFixed(precision); + } } export const axisUnitFormatter = (value: any, unitLocale: any) => { - const len = (parseInt(value) + "").length - const s = parseInt((len / 4) as any) - if (len > 6) { - return toFixed(value / Math.pow(10000, s), 2) + unitLocale(s) + const len = (parseInt(value) + '').length; + const s = parseInt((len / 4) as any); + if (len > 6) { + return toFixed(value / Math.pow(10000, s), 2) + unitLocale(s); + } + return value; +}; + +const transPoint: any = ( + pointers: any, + item: any, + date: any, + metric_name: any, + baseline_data: any, +) => { + const val: any = toFixed(+item.value, 2); //parseInt(o.value) // + + const cv = (item?.cv_value?.replace('±', '')?.replace('%', '') * 100) / 100 / 100; + const up_value = toFixed((1 + cv) * val, 2); + const down_value = toFixed((1 - cv) * val, 2); + + const point = { + ...item, + date, + metric_name, + baseline_data, + up_value, + down_value, + value: val, + }; + + if (item.note) point.symbol = symbol; + + if (pointers[date] && isRepeat(pointers[date], val)) { + point.symbol = 'circle'; + point.itemStyle = { color: '#000' }; + } + return point; +}; + +const commonAreaOpt = { + type: 'line', + smooth: true, + lineStyle: { opacity: 0 }, + tooltip: { show: false }, + symbol: 'none', + select: { disabled: true }, + selectedMode: false, + itemStyle: { + show: false, + }, +}; +const StandaloneChart: React.FC = (props) => { + const { info, setInfo } = useAnalysisProvider(); + const { + valueChange, + setLoading: setPageLoading, + metricList, + opts, + name: metric_name, + refreshKey, + } = props; + const { provider_env } = info; + const { formatMessage } = useIntl(); + const { ws_id, share_id } = useParams() as any; + + const ref = React.useRef(null); + + const [chart, setChart] = React.useState(undefined); + const [sourceData, setSourceData] = React.useState(); + const [lineIgnore, setLineIgnore] = React.useState([]); + const [loading, setLoading] = React.useState(true); + const [dataSet, setDataSet] = React.useState(); + const numUnitLocale = (str: any) => + [ + [formatMessage({ id: 'analysis.wan' })], + [formatMessage({ id: 'analysis.yi' })], + [formatMessage({ id: 'analysis.zhao' })], + ][str]; + + const init = async (params: any) => { + setLoading(true); + const { data, code, msg } = await queryPerfAnalysisList(params); + setLoading(false); + setPageLoading(false); + + if (code !== 200) { + setDataSet(undefined); + return console.log(msg); } - return value -} - -const StandaloneChart: React.FC = ({ fetchData = {}, provider_env, valueChange, setFetchData, setLoading }) => { - const { formatMessage } = useIntl() - const { ws_id, share_id } = useParams() as any - const ref = React.useRef(null) - const [chart, setChart] = React.useState(undefined) - - const numUnitLocale = (str: any) => new Map([ - [1, formatMessage({ id: 'analysis.wan' })], - [2, formatMessage({ id: 'analysis.yi' })], - [3, formatMessage({ id: 'analysis.zhao' })] - ]).get(str) - - const { data, mutate, loading, run } = useRequest( - (params = fetchData) => queryPerfAnalysisList(params), - { - manual: true, - debounceInterval: 100, - refreshDeps: [fetchData], - onSuccess() { - setLoading(false) - }, - onError() { - setLoading(false) - } + setDataSet(data); + }; + + React.useEffect(() => { + if (!!metricList) { + let $params: any = { metric: metricList, share_id }; + if (!share_id) { + $params = { ...info, ...$params, ws_id }; + } + init($params); + } + }, [metricList, refreshKey]); + + React.useEffect(() => { + if (!dataSet) return; + const { job_list } = dataSet; + if (!job_list) return; + valueChange?.(job_list); + }, [dataSet]); + + const handleMetricChange = (list: any) => { + setInfo((pre: any) => ({ + ...pre, + metric: pre.metric?.map((ctx: any) => { + if (ctx.name === metric_name) { + return { + ...ctx, + metricList: list, + }; } - ) - - - - React.useEffect(() => { - if (fetchData.metric) { - run() + return ctx; + }), + })); + if (list.length === 0) setDataSet(undefined); + }; + + const filterEmptyValueObj = (obj: any) => + Object.entries(obj).filter((ctx: any) => { + const [, value] = ctx; + if (opts?.includes('data')) { + return !!value; + } + return true; + }); + + const handleChangeOpt = (vals: any) => { + setInfo((pre: any) => ({ + ...pre, + metric: pre.metric?.map((ctx: any) => { + if (ctx.name === metric_name) { + return { + ...ctx, + opts: vals, + }; } - }, [fetchData]) - - React.useEffect(() => { - if (!data) return - const { job_list } = data - if (!job_list) return - valueChange?.(job_list) - }, [data]) - - const handleMetricChange = ($query: any) => { - const { metric } = $query - setFetchData?.((p: any) => { - return p.map((x: any) => { - if (x.key === fetchData?.key) - return { - ...x, - metric, - } - return x - }) - }) - if (metric.length === 0) - mutate(null) + return ctx; + }), + })); + setLineIgnore([]); + }; + + const findLastNonEmptyIndex = (arr: any) => { + for (let i = arr.length - 1; i >= 0; i--) { + if (!!arr[i]?.value) return i; } - - const cts = React.useMemo(() => { - if (!data) return - const { metric_map, job_list } = data - - const pointers = job_list.reduce((x: any, y: any) => { - const { date, value } = y - const val: any = toFixed(+ value, 2) //parseInt(o.value) // - x[date] = x[date] ? x[date].concat(val) : [val] - return x - }, {}) - - return Object.keys(metric_map)?.reduce((pre: any, metric_name: any) => { - const ctx = metric_map[metric_name] - const { result_data, baseline_data } = ctx - - const { xAxis, lineDatas, upLine, downLine, titles } = Object.entries(result_data).reduce( - (p: any, cur: any) => { - const [ip, list] = cur - const ipValues = Object.keys(list).reduce((a: any, date: any) => { - const s = list[date] - if (s) { - const val: any = toFixed(+ s.value, 2) //parseInt(o.value) // - - const cv = s.cv_value.replace('±', '').replace('%', '') * 100 / 100 / 100 - const up_value = toFixed((1 + cv) * val, 2) - const down_value = toFixed((1 - cv) * val, 2) - - const point = { - ...s, - date, - metric_name, - ip, - baseline_data, - up_value, - down_value, - value: val, - } - - if (s.note) point.symbol = symbol - - if (pointers[date] && isRepeat(pointers[date], val)) { - point.symbol = 'circle' - point.itemStyle = { color: '#000' } - } - return a.concat(point) - } - return a.concat({ date, ip, value: null, baseline_data, metric_name }) - }, []) - - const name = `${metric_name}(${ip})` - - p.titles.push({ - name, - }) - - p.lineDatas.push({ - type: 'line', - smooth: true, - ip, - symbolSize: 8, - connectNulls: false, - name, - z: 9999, - opacity: 1, - showAllSymbol: true, - data: ipValues - }) - - p.upLine.push({ - type: 'line', smooth: true, - lineStyle: { opacity: 0 }, - tooltip: { show: false }, - symbol: "none", - // name: `${metric_name}-U`, - name, - select: { disabled: true }, - selectedMode: false, - areaStyle: { color: '#ccc' }, - data: ipValues.map((i: any) => ({ ...i, value: i.up_value })) - }) - - p.downLine.push({ - type: 'line', smooth: true, - lineStyle: { opacity: 0 }, - tooltip: { show: false }, - symbol: "none", - name, - select: { disabled: true }, - selectedMode: false, - // name: `${metric_name}-D`, - areaStyle: { color: '#fff', opacity: 1 }, z: 99, - data: ipValues.map((i: any) => ({ ...i, value: i.down_value })) - }) - - p.xAxis = Object.keys(list) - return p - }, - { xAxis: [], lineDatas: [], upLine: [], downLine: [], titles: [] } - ) - - pre.baseline.push({ - type: 'line', - name: metric_name, - symbol: 'none', - tooltip: { show: false }, - lineStyle: { - width: 1, - type: 'dashed' - }, - z: 9999, - data: xAxis?.map((i: any) => ({ date: i, value: baseline_data.value })), - }) - pre.title.push([{ - name: metric_name, - isBaseline: 1, - }, ...titles]) - pre.series = pre.series.concat(upLine, downLine, lineDatas) - pre.xAxis = xAxis - return pre - }, { baseline: [], title: [], series: [], xAxis: [] }) - }, [data]) - - React.useEffect(() => { - if (!ref.current) return - if (!cts) return - const { xAxis, series, title, baseline } = cts - const myChart = echarts.init(ref.current) - myChart.clear() - myChart.showLoading() - - myChart.setOption({ - legend: { - icon: "rect", - itemHeight: 2, - itemWidth: 10, - data: title.flat().map((x: any) => x.name), - show: false, - }, - tooltip: { - trigger: 'axis', - axisPointer: { - snap: true, - type: 'cross' - }, - enterable: true, - backgroundColor: '#fff', - borderColor: "#fff", - textStyle: { - color: 'rgba(0,0,0,0.65)', - fontSize: 14, - }, - extraCssText: 'box-shadow: 0 2px 8px 0 rgba(0,0,0,0.15);border-radius: 2px;padding:12px;max-height: 320px;overflow: auto;', - formatter: function (params: any) { - const tips = params.reduce((pre: any, cur: any) => { - const item = cur.data || {} - if (!item.value) return pre - const element = ( - `
+ return 0; // 如果数组中所有元素都是空值,则返回-1 + }; + const toSeriesLine = ( + name: any, + data: any, + ip: any, + connectNulls: boolean = false, + xAxis: any, + ) => { + const listAll = xAxis.map((ctx: any) => { + const hasDate = data.find((item: any) => item.date === ctx); + if (hasDate) return hasDate; + return { date: ctx }; + }); + const lastIdx = findLastNonEmptyIndex(listAll); + return [ + { + ip, + name, + data: listAll.map((ctx: any, idx: number) => { + if (lastIdx === idx) { + ctx.label = { + show: true, + textStyle: { color: 'red' }, + }; + } + return ctx; + }), + type: 'line', + smooth: true, + symbolSize: 8, + connectNulls, + z: 9999, + opacity: 1, + showAllSymbol: true, + }, + { + ...commonAreaOpt, + areaStyle: { color: '#ccc' }, + data: listAll.map((ctx: any) => ({ ...ctx, symbol: 'none', value: ctx.up_value })), + name, + }, + { + ...commonAreaOpt, + areaStyle: { color: '#fff', opacity: 1 }, + z: 99, + data: listAll.map((ctx: any) => ({ ...ctx, symbol: 'none', value: ctx.down_value })), + name, + }, + ]; + }; + + React.useEffect(() => { + if (!ref.current) return; + if (!dataSet) return; + const myChart = echarts.init(ref.current); + myChart.clear(); + myChart.showLoading(); + const { job_list } = dataSet; + const { metric_map } = dataSet; + const list = Object.entries(metric_map).reduce((pre: any, cur: any) => { + const [metric, source] = cur; + const { result_data, baseline_data } = source; + + if (!pre[metric]) pre[metric] = {}; + pre[metric].baseline_data = baseline_data; + pre[metric].server_all = Object.entries(result_data).reduce((p: any, c: any) => { + const [$ip, obj] = c; + if (lineIgnore.includes(`${metric}(${$ip})`)) return p; + filterEmptyValueObj(obj).forEach((ctx: any) => { + const [date, val] = ctx; + if (!p[date]) p[date] = []; + p[date] = p[date].concat(val); + }); + return p; + }, {}); + + Object.entries(result_data).forEach((ctx: any) => { + const [server, obj] = ctx; + const serverObj = filterEmptyValueObj(obj).reduce((p: any, c: any) => { + const [date, val] = c; + p[date] = val; + return p; + }, {}); + pre[metric][server] = serverObj; + }); + return pre; + }, {}); + const pointers = job_list.reduce((x: any, y: any) => { + const { date, value } = y; + const val: any = toFixed(+value, 2); //parseInt(o.value) // + x[date] = x[date] ? x[date].concat(val) : [val]; + return x; + }, {}); + + const chartSource = Object.entries(list).reduce( + (pre: any, cur: any) => { + const [metric_name, serverData] = cur; + const { baseline_data, ...rest } = serverData; + const { server_all, ...lineData } = rest; + let titles: any = []; + + const serverAllData = Object.entries(server_all).map((ctx: any) => { + const [date, list] = ctx; + const vals = list.filter(Boolean); + const len = vals?.length; + if (!!len) { + const item = vals[0]; + if (item) return transPoint(pointers, item, date, metric_name, baseline_data); + } + return { date, value: null, baseline_data, metric_name }; + }); + pre.xAxis = [...new Set(pre.xAxis.concat(serverAllData.map((ctx: any) => ctx.date)))]; + + if (!opts?.includes('server')) { + pre.series = pre.series.concat( + toSeriesLine(`${metric_name}(不区分机器)`, serverAllData, null, true, pre.xAxis), + ); + } + Object.entries(lineData).forEach((ctx: any) => { + const [objKey, list] = ctx; + const ip = objKey; + + const name = `${metric_name}(${objKey})`; + titles.push({ name }); + if (opts?.includes('server')) { + const ipValues = Object.entries(list).reduce((p: any, c: any) => { + const [date, item] = c; + if (item) { + return p.concat({ + ...transPoint(pointers, item, date, metric_name, baseline_data), + ip: objKey, + }); + } + return p.concat({ date, ip: objKey, value: null, baseline_data, metric_name }); + }, []); + pre.series = pre.series.concat(toSeriesLine(name, ipValues, ip, false, pre.xAxis)); + } + }); + pre.title = pre.title.concat([[{ name: metric_name, isBaseline: 1 }, ...titles]]); + + pre.baseline = pre.baseline.concat({ + type: 'line', + name: metric_name, + symbol: 'none', + tooltip: { show: false }, + lineStyle: { + width: 1, + type: 'dashed', + }, + z: 9999, + data: pre.xAxis?.map((date: any) => ({ date, value: baseline_data.value })), + }); + return pre; + }, + { + title: [], + series: [], + titles: [], + xAxis: [], + baseline: [], + }, + ); + setSourceData(chartSource); + const { xAxis, series, title, baseline } = chartSource; + + myChart.setOption({ + legend: { + icon: 'rect', + itemHeight: 2, + itemWidth: 10, + data: title.flat().map((x: any) => x.name), + show: false, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + snap: true, + type: 'cross', + }, + enterable: true, + backgroundColor: '#fff', + borderColor: '#fff', + textStyle: { + color: 'rgba(0,0,0,0.65)', + fontSize: 14, + }, + extraCssText: + 'box-shadow: 0 2px 8px 0 rgba(0,0,0,0.15);border-radius: 2px;padding:12px;max-height: 320px;overflow: auto;', + formatter: function (params: any) { + const tips = params.reduce((pre: any, cur: any) => { + const item = cur.data || {}; + if (!item.value) return pre; + const element = `
${cur.marker} ${cur.seriesName}
+ ${textTip(formatMessage({ id: 'analysis.date' }), item?.date)} ${commitLinkTip('JobID', item?.job_id, ws_id)} + ${textTip('ip', item?.server)} ${textTip('commit', item?.commit)} ${textTip('Avg', item?.value)} ${textTip('CV', item?.cv_value)} - ${textTip(formatMessage({ id: 'analysis.baseline.value' }), item?.baseline_data.value && Number(item?.baseline_data.value).toFixed(2))} - ${textTip(formatMessage({ id: 'analysis.baseline.cv' }), item?.baseline_data.cv_value)} + ${textTip( + formatMessage({ id: 'analysis.baseline.value' }), + item?.baseline_data.value && + Number(item?.baseline_data.value).toFixed(2), + )} + ${textTip( + formatMessage({ id: 'analysis.baseline.cv' }), + item?.baseline_data.cv_value, + )} ${textTip('commit', item?.commit)} ${serverLinkTip(params.seriesName)} ${renderProviderText(params, provider_env)} - ${ws_id && textTip(formatMessage({ id: 'analysis.table.column.note' }), item?.note)} - ${textTip(formatMessage({ id: 'analysis.chart.compare_result' }), item?.compare_result)} -
` - .trim() - ) - return pre.concat(`
${element}
`) - }, "") - - if (tips.length === 0) return "" - return `
${tips}
` - }, - }, - color: colors, - grid: { left: '5%', right: 60, bottom: '10%', top: 20 }, //left: 50, - xAxis: { - type: 'category', // 还有其他的type,可以去官网喵两眼哦 - data: xAxis, - axisLabel: { - showMaxLabel: true, - }, - axisPointer: { - z: 9999 - } - }, - yAxis: { - type: 'value', - axisLine: { show: false }, - axisTick: { show: false }, - splitLine: { show: false }, - axisPointer: { - z: 100 - }, - min: "dataMin", - max: "dataMax", - splitArea: { - areaStyle: { - color: ['#fff', '#fff'] - } - }, - axisLabel: { - formatter: (value: any) => axisUnitFormatter(value, numUnitLocale) - }, - zlevel: 100 - }, - series: [ - ...series, - ...baseline, - ], - }) - - if (!share_id) - myChart.on("click", 'series.line', (params: any) => { - if (params?.data && ws_id) { - const { job_id } = params?.data - if (job_id) targetJump(`/ws/${ws_id}/test_result/${job_id}`) - } - }) - - myChart.hideLoading() - setChart(myChart) - return () => { - myChart.dispose() - setChart(undefined) + ${ + ws_id && + textTip( + formatMessage({ id: 'analysis.table.column.note' }), + item?.note, + ) + } + ${textTip( + formatMessage({ id: 'analysis.chart.compare_result' }), + item?.compare_result, + )} +
`.trim(); + return pre.concat(`
${element}
`); + }, ''); + + if (tips.length === 0) return ''; + return `
${tips}
`; + }, + }, + color: colors, + grid: { left: '5%', right: 60, bottom: '10%', top: 20 }, //left: 50, + xAxis: { + type: 'category', // 还有其他的type,可以去官网喵两眼哦 + data: xAxis, + axisLabel: { + showMaxLabel: true, + }, + axisPointer: { + z: 9999, + }, + }, + yAxis: { + type: 'value', + axisLine: { show: false }, + axisTick: { show: false }, + splitLine: { show: false }, + axisPointer: { + z: 100, + }, + min: dataSet?.min ?? 'dataMin', + max: dataSet?.max ?? 'dataMax', + splitArea: { + areaStyle: { + color: ['#fff', '#fff'], + }, + }, + axisLabel: { + formatter: (value: any) => axisUnitFormatter(value, numUnitLocale), + }, + zlevel: 100, + }, + series: [...series, ...baseline], + }); + + if (!share_id) + myChart.on('click', 'series.line', (params: any) => { + if (params?.data && ws_id) { + const { job_id } = params?.data; + if (job_id) targetJump(`/ws/${ws_id}/test_result/${job_id}`); } - }, [cts]) - - if (!data) return <> - if (data?.job_list?.length === 0) return <> - - return ( - - - - <Space size={0} wrap> - { - fetchData?.metric?.map((t: any) => ( - <Typography.Text key={t}>{t}</Typography.Text> - )) - } - { - ws_id && - <MetricDropdown - onChange={handleMetricChange} - fetchData={fetchData} - /> - } - </Space> - - - - { - cts?.title?.map((i: any, idx: any) => ( - // eslint-disable-next-line react/no-array-index-key - - )) - } - - - - - - - - ) -} - -export default React.memo(StandaloneChart, (prev, next) => { - return JSON.stringify(prev?.fetchData) === JSON.stringify(next?.fetchData) -}) + }); + + myChart.hideLoading(); + setChart(myChart); + return () => { + myChart.dispose(); + setChart(undefined); + }; + }, [dataSet, opts, lineIgnore]); + + return ( + + + <div style={{ flexWrap: 'wrap', width: '100%', display: 'flex', gap: '8px 16px' }}> + {metricList?.map((t: any) => ( + <Typography.Text key={t}>{t}</Typography.Text> + ))} + <div> + {ws_id && <MetricDropdown metricList={metricList} onChange={handleMetricChange} />} + </div> + </div> + <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}> + <Checkbox.Group + value={opts || ['server', 'data']} + disabled={!!share_id} + onChange={handleChangeOpt} + style={{ fontWeight: 'normal' }} + options={[ + { label: '区分机器', value: 'server' }, + { label: '排除空数据', value: 'data' }, + ]} + /> + </div> + + + + {sourceData?.title?.map((i: any, idx: any) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} + + + + + + + ); +}; + +export default StandaloneChart; +/* export default React.memo(StandaloneChart, (prev, next) => { + return JSON.stringify(prev?.fetchData) === JSON.stringify(next?.fetchData); +}); */ diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/styled.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/styled.tsx index 8aeeb701d12c5407f7a42a5a3804c6849f527f2a..f4422d24ada8c505a0b0ed4ae4ec50d6d607742a 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/styled.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/PerformanceCharts/styled.tsx @@ -1,29 +1,28 @@ - -import styled from "styled-components" -import { Card } from "antd" +import styled from 'styled-components'; +import { Card } from 'antd'; export const Title = styled.div` - width: 100%; - font-size: 16px; - font-weight: 500; - color: rgba(0,0,0,0.85); - line-height: 43px; - height: 43px; - border-bottom: 1px solid #E9E9E9; - text-indent: 1em; -` + width: 100%; + font-size: 16px; + font-weight: 500; + color: rgba(0, 0, 0, 0.85); + line-height: 43px; + height: 43px; + border-bottom: 1px solid #e9e9e9; + // text-indent: 1em; +`; export const CardWrapper = styled(Card)` - /* height: 360px; */ - /* margin-top: 10px; */ - width: 100%; - .ant-card-body { - padding: 0 20px; - border: none; - } -` + /* height: 360px; */ + /* margin-top: 10px; */ + width: 100%; + .ant-card-body { + padding: 0 20px; + border: none; + } +`; export const ChartWrapper = styled.div` - width: 100%; - height: ${360 - 48}px; -` \ No newline at end of file + width: 100%; + height: ${360 - 48}px; +`; diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/TabPaneCard.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/TabPaneCard.tsx index c62792ca1e0e2439ce65dc09cfd7e9b80a2b1bd3..d9ce71113fb758a8666388de03399d95a75946f6 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/TabPaneCard.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/TabPaneCard.tsx @@ -1,525 +1,522 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useState, useRef, useEffect } from 'react' -import { Row, Col, Form, Select, DatePicker, Button, Tooltip, message, Descriptions, Space } from 'antd' -import moment from 'moment' -import { useRequest, useLocation, useParams, useIntl, FormattedMessage } from 'umi' -import styled from 'styled-components' -import AnalysisTable from './Table' - -import ChartRender from './RenderChart' +import React, { useState, useRef, useEffect } from 'react'; +import { + Row, + Col, + Form, + Select, + DatePicker, + Button, + Tooltip, + message, + Descriptions, + Space, +} from 'antd'; +import moment from 'moment'; +import { useRequest, useLocation, useParams, useIntl, FormattedMessage } from 'umi'; +import styled from 'styled-components'; +import AnalysisTable from './Table'; + +import ChartRender from './RenderChart'; import { queryProjectList, queryPerfAnalysisList, queryFuncAnalysisList } from '../services'; -import { tagList as queryJobTagList } from "@/pages/WorkSpace/TagManage/service" -import SelectMertric from "./MetricSelectModal/layouts" -import styles from './index.less' -import { QuestionCircleOutlined } from '@ant-design/icons' - -import ClusterChart from './PerformanceCharts/Cluster' -import StandaloneChart from './PerformanceCharts/Standalone' -import EmptyComp from "./EmptyComp" -import LoadingComp from './Loading' -import { v4 as uuid } from 'uuid' -import { useAnalysisProvider } from '../provider' +import { tagList as queryJobTagList } from '@/pages/WorkSpace/TagManage/service'; +import SelectMertric from './MetricSelectModal/layouts'; +import styles from './index.less'; +import { QuestionCircleOutlined } from '@ant-design/icons'; + +import ClusterChart from './PerformanceCharts/Cluster'; +import StandaloneChart from './PerformanceCharts/Standalone'; +import EmptyComp from './EmptyComp'; +import LoadingComp from './Loading'; +import { useAnalysisProvider } from '@/pages/WorkSpace/TestAnalysis/AnalysisTime/provider'; const TootipTipRow = styled(Row)` - position:relative; - width:100%; - .tootip_pos { - position:absolute; - left:330px; - top:4px; - } -` + position: relative; + width: 100%; + .tootip_pos { + position: absolute; + left: 330px; + top: 4px; + } +`; const fontStyle = { - fontWeight: 'bold' -} + fontWeight: 'bold', +}; const SuiteConfMetric = (props: any) => { - const str = props.title?.split('/') - - return ( -
- - - {str?.[0]} - - - {str?.[1]} - - - { - props?.metric?.map( - (item: any) => ( -
{item}
- ) - ) - } -
-
-
- ) -} + const str = props.title?.split('/'); + + return ( +
+ + + {str?.[0]} + + + {str?.[1]} + + + {props?.metric?.map((item: any) => ( +
{item?.name}
+ ))} +
+
+
+ ); +}; const TabPaneCard: React.ForwardRefRenderFunction = (props, ref) => { - const { formatMessage } = useIntl() - const { ws_id } = useParams() as any - const { setMetrics } = useAnalysisProvider() - const { query }: any = useLocation() - const { info, setInfo } = props - const { show_type, provider_env, test_type } = info - const [chartData, setChartData] = useState({}) - const [statisticsData, setStatisticsData] = useState({}) - - const [tableData, setTableData] = useState([]) - const [metricData, setMetricData] = useState(null) - const [loading, setLoading] = useState(JSON.stringify(query) !== "{}") - const [fetchData, setFetchData] = React.useState([]) - - const selectMetricRef: any = useRef() - const [form] = Form.useForm() - - const project_id = Form.useWatch("project_id", form) - const tag_id = Form.useWatch("tag", form) - const form_time = Form.useWatch("time", form) - - React.useImperativeHandle(ref, () => ({ - reset() { - form.resetFields() - setMetricData(undefined) - } - })) - - const { data: tagList } = useRequest( - (params = { ws_id, page_size: 999 }) => queryJobTagList(params), - { initialData: [] } /* , manual: true */ - ) - const { data: projectList } = useRequest(() => queryProjectList({ ws_id, page_size: 500 }), { initialData: [] }) - - const requestAnalysisData = async (params: any) => { - setInfo(params) - setLoading(true) - setTableData([]) - setChartData({}) - setStatisticsData({}) - - const { data, code } = test_type === 'performance' ? - await queryPerfAnalysisList(params) : - await queryFuncAnalysisList(params) - setLoading(false) - - if (code !== 200) return - if (!data) return - - const { job_list, metric_map, case_map, case_statistics } = data - if (job_list && job_list.length > 0) { - setChartData(test_type === 'performance' ? metric_map : case_map) - setTableData(job_list) - setStatisticsData(case_statistics) - return - } + const { formatMessage } = useIntl(); + const { ws_id } = useParams() as any; + const tabName = BUILD_APP_ENV === 'openanolis' ? 'aliyun' : 'aligroup'; + const { setMetrics, info, setInfo } = useAnalysisProvider(); + const { query, key: $routeKey }: any = useLocation(); + const { show_type, provider_env, test_type } = info; + const [chartData, setChartData] = useState({}); + const [statisticsData, setStatisticsData] = useState({}); + + const [tableData, setTableData] = useState([]); + const [loading, setLoading] = useState(JSON.stringify(query) !== '{}'); + + const selectMetricRef: any = useRef(); + const [form] = Form.useForm(); + + const project_id = Form.useWatch('project_id', form); + + React.useImperativeHandle(ref, () => ({ + reset() { + form.resetFields(); + }, + })); + + const onReset = () => { + setStatisticsData({}); + setTableData([]); + setChartData({}); + }; + + React.useEffect(() => { + form.resetFields(); + onReset(); + setInfo({ ws_id, test_type: 'performance', provider_env: tabName, show_type: 'pass_rate' }); + }, [$routeKey]); + + React.useEffect(() => { + onReset(); + }, [show_type, provider_env, test_type]); + + const { data: tagList } = useRequest( + (params = { ws_id, page_size: 999 }) => queryJobTagList(params), + { initialData: [] } /* , manual: true */, + ); + const { data: projectList } = useRequest(() => queryProjectList({ ws_id, page_size: 500 }), { + initialData: [], + }); + + const requestAnalysisData = async (params: any) => { + setLoading(true); + setTableData([]); + setChartData({}); + setStatisticsData({}); + + const { data, code } = + test_type === 'performance' + ? await queryPerfAnalysisList(params) + : await queryFuncAnalysisList(params); + setLoading(false); + + if (code !== 200) return; + if (!data) return; + + const { job_list, metric_map, case_map, case_statistics } = data; + if (job_list && job_list.length > 0) { + setChartData(test_type === 'performance' ? metric_map : case_map); + setTableData(job_list); + setStatisticsData(case_statistics); + return; } + }; - const handleSelectMertric = () => { - if (form.getFieldValue('project_id')) { - selectMetricRef.current.show() - } else { - message.error(formatMessage({ id: 'analysis.selected.error' })) - } + const handleSelectMertric = () => { + if (form.getFieldValue('project_id')) { + selectMetricRef.current.show({ ...info, metric: info?.metric?.map((ctx: any) => ctx.name) }); + } else { + message.error(formatMessage({ id: 'analysis.selected.error' })); } + }; - const getAnalysisFormData = () => { - const params: any = test_type === 'functional' ? { show_type } : { provider_env } - const values = form.getFieldsValue() - const { time, ...rest } = values - - if (time && time.length > 0) { - const [start_time, end_time] = time - params.start_time = moment(start_time).format('YYYY-MM-DD') - params.end_time = moment(end_time).format('YYYY-MM-DD') - } + const getAnalysisFormData = () => { + const params: any = test_type === 'functional' ? { show_type } : { provider_env }; + const values = form.getFieldsValue(); + const { time, ...rest } = values; - return { - ...params, - ...rest - } + if (time && time.length > 0) { + const [start_time, end_time] = time; + params.start_time = moment(start_time).format('YYYY-MM-DD'); + params.end_time = moment(end_time).format('YYYY-MM-DD'); } - const fetchAnalysis = (data: any) => { - const { test_suite_id, test_case_id } = data - const params = getAnalysisFormData() - const { project_id } = params - if (project_id && test_suite_id && test_case_id) { - requestAnalysisData({ - ws_id, - ...params, - ...data, - }) - } + return { + ...params, + ...rest, + }; + }; + + const fetchAnalysis = (data: any) => { + const { test_suite_id, test_case_id } = data; + const params = getAnalysisFormData(); + const { project_id } = params; + if (project_id && test_suite_id && test_case_id) { + requestAnalysisData({ + ws_id, + ...params, + ...data, + }); } - - React.useEffect(() => { - const baseFormData = getAnalysisFormData() - const obj = { ...baseFormData, ...metricData, metric: metricData?.metric?.toString() } - if (fetchData?.length !== 0) { - fetchData?.forEach(({ metric }: any) => { - const $name = metric?.[0] - obj[$name] = metric.toString() - }) - } - setInfo(obj) - }, [fetchData, metricData]) - - const handleMerticSelectOk = (data: any) => { - setMetricData(data) - if (test_type !== "performance") - fetchAnalysis(data) - else { - const { metric } = data - const params = getAnalysisFormData() - const metrics = metric.map((i: any) => ({ ...params, ...data, metric: [i], key: uuid(), ws_id })) - setTableData([]) - setFetchData(metrics) - } + }; + + const handleMerticSelectOk = (data: any) => { + const changeData = { ...info, ...data }; + if (test_type !== 'performance') { + fetchAnalysis(data); + } else { + setTableData([]); + if (data?.metric) { + changeData.metric = data?.metric?.map((ctx: any) => ({ + name: ctx, + metricList: [ctx], + opts: [provider_env !== 'aliyun' && 'server', 'data'].filter(Boolean), + })); + } } - - const handleFormChange = (_: any) => { - setMetrics([]) - if (_?.[0].name?.[0] === 'project_id') { - setMetricData(null) - setTableData([]) - setChartData(null) - setFetchData([]) - return - } - const params = getAnalysisFormData() - setTableData([]) - setChartData(null) - // setMetricData(null) - setFetchData([]) - if (metricData) { - const { test_suite_id, test_case_id, metric } = metricData - const { project_id } = params - if (test_suite_id && test_case_id && project_id) { - if (test_type === "performance") { - const metrics = metric.map((i: any) => ({ ...params, ...metricData, metric: [i], key: uuid(), ws_id })) - setFetchData(metrics) - return - } - requestAnalysisData({ - ws_id, - ...params, - ...metricData - }) - } - } + setInfo(changeData); + }; + + const handleFormChange = () => { + setMetrics([]); + setTableData([]); + setChartData(null); + + const { test_suite_id, test_case_id, metric } = info; + const params = getAnalysisFormData(); + const changeData = { ...info, ...params }; + if (test_type === 'performance') { + if (metric) { + changeData.metric = metric?.map((ctx: any) => ({ + ...ctx, + refreshKey: new Date().getTime(), + })); + } + } else if (test_suite_id && test_case_id && project_id) { + requestAnalysisData({ + ...params, + ...info, + }); } - - useEffect(() => { - return () => { - setChartData(null) - setTableData([]) - setFetchData([]) + setInfo(changeData); + }; + + useEffect(() => { + if (query && JSON.stringify(query) !== '{}') { + const { + test_type: $test_type, + project_id, + tag, + start_time, + end_time, + provider_env: $provider_env, + metric, + title, + test_suite_id, + test_case_id, + sub_case_name, + days, + } = query; + + if (test_type === $test_type) { + const params: any = { + project_id, + tag, + test_suite_id, + test_case_id, + sub_case_name, + show_type, + provider_env: $provider_env, + ws_id, + }; + + let start = start_time; + let end = end_time || moment().format('YYYY-MM-DD'); + + const hasNearDay = + !isNaN(+days) && Object.prototype.toString.call(+days) === '[object Number]'; + + if (hasNearDay) { + start = moment().subtract(days - 1, 'days'); + end = moment(); } - }, [provider_env, show_type, test_type]) - - useEffect(() => { - if (query && JSON.stringify(query) !== '{}') { - const { - test_type: $test_type, project_id, tag, start_time, end_time, provider_env: $provider_env, - metric, title, test_suite_id, test_case_id, sub_case_name, days - } = query - - const $metric = Object.prototype.toString.call(metric) === "[object Array]" ? metric : metric?.split(",") - - if (test_type === $test_type) { - const params: any = { - metric: $metric, - project_id, tag, - test_suite_id, test_case_id, sub_case_name, - show_type, - provider_env: $provider_env, - ws_id - } - - let start = start_time - let end = end_time || moment().format("YYYY-MM-DD") - const hasNearDay = !isNaN(+ days) && Object.prototype.toString.call(+ days) === "[object Number]" - - if (hasNearDay) { - start = moment().subtract(days - 1, "days") - end = moment() - } + params.start_time = moment(start).format('YYYY-MM-DD'); + params.end_time = moment(end).format('YYYY-MM-DD'); - params.start_time = moment(start).format("YYYY-MM-DD") - params.end_time = moment(end).format("YYYY-MM-DD") - - if (title) { - if (test_type !== "performance") { - setLoading(false) - requestAnalysisData(params) - } - else { - const metrics = $metric?.map((i: any) => ({ - ...params, - metric: query[i] ? query[i]?.split(",") : [i], - key: uuid() - })) - setTableData([]) - setFetchData(metrics) - } - } - if (!title) { - setLoading(false) - } - - setMetricData({ - title, - metric: $metric, - sub_case_name, - test_suite_id, - test_case_id - }) + if (title) { + if (test_type !== 'performance') { + requestAnalysisData(params); + } + } + // Object.prototype.toString.call(metric) === '[object Array]' ? metric : metric?.split(','); + if (metric) { + const metrics = + Object.prototype.toString.call(metric) === '[object Array]' + ? metric + : metric?.split(','); + params.metric = metrics.map((ctx: any) => ({ + name: ctx, + opts: query?.[`::opts::${ctx}`]?.split(','), + metricList: query?.[ctx]?.split(',') || [ctx], + })); + } - if (hasNearDay) { - form.setFieldsValue({ time: [moment(start), moment()] }) - } - else if (start_time) - form.setFieldsValue({ time: [moment(start_time), end_time ? moment(end_time) : moment()] }) - - form.setFieldsValue({ - project_id: + project_id || undefined, - tag: + tag || '', - }) - } + if (hasNearDay) { + form.setFieldsValue({ time: [moment(start), moment()] }); + } else if (start_time) { + form.setFieldsValue({ + time: [moment(start_time), end_time ? moment(end_time) : moment()], + }); } - }, []) - const handleListChange = (list: any[]) => { - setTableData((p: any) => { - const ids = list.map(((i: any) => i.id)) - return p.filter((i: any) => !ids.includes(i.id)).concat(list).sort((a: any, b: any) => b.id - a.id) - }) + form.setFieldsValue({ + project_id: +project_id || undefined, + tag: +tag || '', + }); + + setInfo((pre: any) => ({ + ...pre, + ...params, + title, + sub_case_name, + test_suite_id, + test_case_id, + })); + setLoading(false); + } } - - return ( -
- { - loading && - - } - - -
- - } - name="project_id" - > - option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0} - options={ - [{ value: "", label: formatMessage({ id: "analysis.indistinguishable" }) }].concat( - tagList - .filter(({ creator }: any) => Object.prototype.toString.call(creator) === "[object Number]") - .map( - (i: any) => ({ - value: i.id, - label: i.name - }) - ) - ) - } - /> - -
- - - -
- - - } - name="time" - > - - - - - - - { - (test_type === 'functional' && show_type === 'pass_rate') && - Test Conf: - } - { - (test_type === 'functional' && show_type === 'result_trend') && - Test Case: - } - { - test_type !== 'functional' && - - : - - } - { - metricData && - - } - autoAdjustOverflow={true} - placement="bottomLeft" - color='#fff' - overlayInnerStyle={{ minWidth: 300, maxHeight: 300, overflowY: 'auto', color: "#333" }} - > - - {metricData.title} - - - } - - - - - - { - test_type === 'performance' && - - - { - fetchData?.map((i: any) => { - const _props = { - fetchData: i, - key: i.key, - provider_env, - valueChange: handleListChange, - setFetchData, - setLoading - } - - return ( - provider_env === "aliyun" ? - : - - ) - }) - } - - - } - - - - { - (test_type !== 'performance' && tableData?.length > 0) && - - } - - { - tableData?.length === 0 && - - } - { - tableData?.length > 0 && - { - if (test_type === "performance") { - setFetchData((p: any) => p.map((i: any) => ({ ...i, key: uuid() }))) - return - } - fetchAnalysis(metricData) - }} - dataSource={tableData} - test_type={test_type} - show_type={show_type} - /> + }, []); + + const handleListChange = (list: any[]) => { + setTableData((p: any) => { + const ids = list.map((i: any) => i.id); + return p + .filter((i: any) => !ids.includes(i.id)) + .concat(list) + .sort((a: any, b: any) => b.id - a.id); + }); + }; + + return ( +
+ +
+ + } name="project_id"> + + option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 } - - - - + Object.prototype.toString.call(creator) === '[object Number]', + ) + .map((i: any) => ({ + value: i.id, + label: i.name, + })), + )} + /> + +
+ + + +
+ + + } name="time"> + + + + + + + {test_type === 'functional' && show_type === 'pass_rate' && ( + Test Conf: + )} + {test_type === 'functional' && show_type === 'result_trend' && ( + Test Case: + )} + {test_type !== 'functional' && ( + + : + + )} + {info && ( + + } + autoAdjustOverflow={true} + placement="bottomLeft" + color="#fff" + overlayInnerStyle={{ + minWidth: 300, + maxHeight: 300, + overflowY: 'auto', + color: '#333', + }} + > + {info.title} + + )} + + + + + {loading && } + + {test_type === 'performance' && ( + + + {info.metric?.map((i: any) => { + return provider_env === 'aliyun' ? ( + + ) : ( + + ); + })} + + + )} + + + + {test_type !== 'performance' && tableData?.length > 0 && ( + - - - ) -} - -export default React.forwardRef(TabPaneCard) \ No newline at end of file + )} + +
+ {tableData?.length === 0 && } + {tableData?.length > 0 && ( + { + if (test_type === 'performance') { + setInfo((p: any) => ({ + ...p, + metric: p.metric?.map((ctx: any) => ({ + ...ctx, + refreshKey: new Date().getTime(), + })), + })); + return; + } else { + fetchAnalysis(info); + } + }} + dataSource={tableData} + test_type={test_type} + show_type={show_type} + /> + )} +
+
+
+ + + + ); +}; + +export default React.forwardRef(TabPaneCard); diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/Table.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/Table.tsx index 8f47be1f9054d8063dabedcbefbf395444e8518e..3fdb302daa38973c7932476e72ff4d756b410a20 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/Table.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/components/Table.tsx @@ -1,101 +1,93 @@ -import { memo, useRef } from 'react' -import { Table, Card } from 'antd' -import { useAccess, useParams, FormattedMessage } from 'umi' -import EditMarks from './EditMarks' -import { EllipsisEditColumn } from '@/pages/WorkSpace/TestResult/Details/components' +import { memo, useRef } from 'react'; +import { Table, Card } from 'antd'; +import { useAccess, useParams, FormattedMessage } from 'umi'; +import EditMarks from './EditMarks'; +import { EllipsisEditColumn } from '@/pages/WorkSpace/TestResult/Details/components'; import ServerLink from '@/components/MachineWebLink/index'; -export default memo( - (props: any) => { - const { ws_id } = useParams() as any - const { refresh, dataSource, test_type, show_type } = props - const editMarks: any = useRef(null) - const access = useAccess() +export default memo((props: any) => { + const { ws_id } = useParams() as any; + const { refresh, dataSource, test_type, show_type } = props; + const editMarks: any = useRef(null); + const access = useAccess(); - const handleEditMarks = (_: any) => { - editMarks.current.show(_) - } + const handleEditMarks = (_: any) => { + editMarks.current.show(_); + }; - const columns = [ - { - title: , - dataIndex: 'job_id' - }, - { - title: , - dataIndex: 'job_name', - render: (_: any, row: any) => ( - ws_id ? - - {_} - : - _ - ) - }, - { - title: , - dataIndex: 'server', - render: (_: any, row: any) => ( - - ) - }, - { - title: , - dataIndex: 'creator' - }, - { - title: , - dataIndex: 'start_time' - }, - { - title: , - dataIndex: 'end_time' - }, - (ws_id && access.WsTourist()) && - { - title: , - dataIndex: 'note', - render: (_: any, row: any) => ( - handleEditMarks(row) - } - /> - ) - } - ].filter(Boolean) as any; + const columns = [ + { + title: , + dataIndex: 'job_id', + }, + { + title: , + dataIndex: 'job_name', + render: (_: any, row: any) => + ws_id ? ( + + {_} + + ) : ( + _ + ), + }, + { + title: , + dataIndex: 'server', + render: (_: any, row: any) => ( + + ), + }, + { + title: , + dataIndex: 'creator', + }, + { + title: , + dataIndex: 'start_time', + }, + { + title: , + dataIndex: 'end_time', + }, + ws_id && + access.WsTourist() && { + title: , + dataIndex: 'note', + render: (_: any, row: any) => ( + handleEditMarks(row)} + /> + ), + }, + ].filter(Boolean) as any; - return ( - <> - -
- - - - ) - } -) \ No newline at end of file + return ( + <> + +
+ + + + ); +}); diff --git a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/index.tsx b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/index.tsx index e2784a5941a31dd24f329301197f04d4fcf2b28c..88babad48f0f8cb6d0522121cb18ec72f417c136 100644 --- a/src/pages/WorkSpace/TestAnalysis/AnalysisTime/index.tsx +++ b/src/pages/WorkSpace/TestAnalysis/AnalysisTime/index.tsx @@ -1,278 +1,264 @@ import React from 'react'; -import { writeDocumentTitle, useCopyText } from '@/utils/hooks'; +import { DocTitleWrite, useCopyText } from '@/utils/hooks'; import { Layout, Tabs, Row, Radio, Col, message, Button, Popover, Typography } from 'antd'; -import styles from './index.less' +import styles from './index.less'; import { useLocation, useIntl, FormattedMessage, useParams, useRequest } from 'umi'; -import TabPaneCard from './components/TabPaneCard' +import TabPaneCard from './components/TabPaneCard'; import { stringify } from 'querystring'; -import styled from "styled-components" -import { getSelectSuiteConfs, queryPerfomanceMetrics } from './services' +import styled from 'styled-components'; +import { getSelectSuiteConfs, queryPerfomanceMetrics } from './services'; import { Analysis } from './provider'; import { getShareId } from '../../TestResult/Details/service'; import { LinkOutlined } from '@ant-design/icons'; const AnalysisLayout = styled(Layout.Content).attrs((props: any) => ({ - style: { - minHeight: props?.minHeight, - } -})) ` - overflow: auto; - margin-bottom: 10px; - background: #fff; + style: { + minHeight: props?.minHeight, + }, +}))` + overflow: auto; + margin-bottom: 10px; + background: #fff; `; const FlexColGap = styled.div<{ gap?: string }>` - display: flex; - flex-direction: column; - gap: ${({ gap }) => gap || 10}px; -` + display: flex; + flex-direction: column; + gap: ${({ gap }) => gap || 10}px; +`; const AnalysisTime: React.FC = (props) => { - const { formatMessage } = useIntl() - const { query, key } = useLocation() as any - const { ws_id } = useParams() as any - const { route } = props - - const [metrics, setMetrics] = React.useState([]) - const tabPaneRef = React.useRef(null) - const tabName = BUILD_APP_ENV === 'openanolis' ? 'aliyun' : 'aligroup' - const routeIntlName = `Workspace.TestAnalysis.${route.name}` - - const [shareRadioVal, setShareRadioVal] = React.useState(1) + const { formatMessage } = useIntl(); + const { query, key: $routeKey } = useLocation() as any; + const { ws_id } = useParams() as any; + const { route } = props; - const queryMetricList = async (params: any) => { - const { data, code, msg } = await queryPerfomanceMetrics(params) - if (code !== 200) return console.log(msg) - setMetrics(data || []) - } - - React.useEffect(() => { - if (JSON.stringify(query) !== "{}") { - const { test_suite_id, test_case_id, project_id } = query - if (test_suite_id && test_case_id && project_id) { - queryMetricList({ ...query, ws_id }) - } - } - }, [query, ws_id]) + const [metrics, setMetrics] = React.useState([]); + const tabPaneRef = React.useRef(null); + const tabName = BUILD_APP_ENV === 'openanolis' ? 'aliyun' : 'aligroup'; + const routeIntlName = `Workspace.TestAnalysis.${route.name}`; - writeDocumentTitle(routeIntlName) - // const { height: layoutHeight } = useClientSize() + const [shareRadioVal, setShareRadioVal] = React.useState(1); - const [info, setInfo] = React.useState({ - test_type: query?.test_type || "performance", - provider_env: query?.provider_env || tabName, - show_type: query?.show_type || "pass_rate", + const queryMetricList = async (params: any) => { + const { data, code, msg } = await queryPerfomanceMetrics(params); + if (code !== 200) return console.log(msg); + setMetrics(data || []); + }; - project_id: query?.project_id || undefined, - tag: query?.tag || undefined, - start_time: query?.start_time || undefined, - end_time: query?.end_time || undefined, - }) + React.useEffect(() => { + if (JSON.stringify(query) !== '{}') { + const { test_suite_id, test_case_id, project_id } = query; + if (test_suite_id && test_case_id && project_id) { + queryMetricList({ ...query, ws_id }); + } + } + }, [query, ws_id]); + // const { height: layoutHeight } = useClientSize() - const { data: suiteList, loading, run } = useRequest( - getSelectSuiteConfs, - { - manual: true, - debounceInterval: 500, - } - ) + const [info, setInfo] = React.useState({ + test_type: query?.test_type || 'performance', + provider_env: query?.provider_env || tabName, + show_type: query?.show_type || 'pass_rate', - React.useEffect(() => { - const { test_type, provider_env, project_id } = info - if (test_type && provider_env && project_id) - run({ ws_id, test_type, provider_env, project_id }) - }, [info?.test_type, info?.provider_env, info?.project_id]) + project_id: query?.project_id || undefined, + tag: query?.tag || undefined, + start_time: query?.start_time || undefined, + end_time: query?.end_time || undefined, + }); - const clearAndSetFields = (val: any) => { - const { test_type, provider_env = 'aliyun', show_type = 'pass_rate' } = info + const { + data: suiteList, + loading, + run, + } = useRequest(getSelectSuiteConfs, { + manual: true, + debounceInterval: 500, + }); - setInfo({ - test_type, - provider_env, - show_type: ['pass_rate', 'result_trend'].includes(show_type) ? show_type : 'pass_rate', - ...val - }) - setMetrics([]) - tabPaneRef.current?.reset() - } + React.useEffect(() => { + const { test_type, provider_env, project_id } = info; + if (test_type && provider_env && project_id) + run({ ws_id, test_type, provider_env, project_id }); + }, [info?.test_type, info?.provider_env, info?.project_id, $routeKey]); - const handleProviderChange = ({ target }: any) => { - clearAndSetFields({ provider_env: target?.value }) - } + const clearAndSetFields = (val: any) => { + const { test_type, provider_env = 'aliyun', show_type = 'pass_rate' } = info; - const handleTabClick = (tab: string) => { - clearAndSetFields({ test_type: tab }) - } + setInfo({ + test_type, + provider_env, + show_type: ['pass_rate', 'result_trend'].includes(show_type) ? show_type : 'pass_rate', + ...val, + }); + setMetrics([]); + tabPaneRef.current?.reset(); + }; - const handleShowTypeChange = ({ target }: any) => { - clearAndSetFields({ show_type: target?.value }) - } + const handleProviderChange = ({ target }: any) => { + clearAndSetFields({ provider_env: target?.value }); + }; - const tabData = [ - { key: 'performance', tab: formatMessage({ id: 'analysis.performance' }) }, - { key: 'functional', tab: formatMessage({ id: 'analysis.functional' }) }, - ] + const handleTabClick = (tab: string) => { + clearAndSetFields({ test_type: tab }); + }; - const handleCopyUri = useCopyText(formatMessage({ id: 'analysis.copy.to.clipboard' })) + const handleShowTypeChange = ({ target }: any) => { + clearAndSetFields({ show_type: target?.value }); + }; - const copy = async ($key: any) => { - const { origin, pathname } = window.location - const { test_type, show_type, provider_env, metric, ...rest } = info + const tabData = [ + { key: 'performance', tab: formatMessage({ id: 'analysis.performance' }) }, + { key: 'functional', tab: formatMessage({ id: 'analysis.functional' }) }, + ]; - if (!info.project_id) return message.error(formatMessage({ id: 'analysis.selected.error' })) + const handleCopyUri = useCopyText(formatMessage({ id: 'analysis.copy.to.clipboard' })); + const copy = async ($key: any) => { + const { origin, pathname } = window.location; + const { test_type, show_type, provider_env, metric, ...rest } = info; - const isFunc = test_type === "functional" - const params = { - ...rest, - test_type, - provider_env, - show_type, - [isFunc ? "show_type" : "provider_env"]: isFunc ? show_type : provider_env, - ws_id, - } + if (!info.project_id) return message.error(formatMessage({ id: 'analysis.selected.error' })); - if ($key !== 1 && test_type !== "functional" && metric) { - const metricList = metric?.split(',') - params.metricList = metricList - params.fetchData = metricList?.map((i: any) => ({ metric: info?.[i]?.split(',') })) - } + const isFunc = test_type === 'functional'; + const params = { + ...rest, + test_type, + provider_env, + show_type, + [isFunc ? 'show_type' : 'provider_env']: isFunc ? show_type : provider_env, + ws_id, + }; - if ($key === 1 && test_type === 'performance') { - params.metric = metric - } + if ($key !== 1 && test_type !== 'functional' && metric) { + params.metric_list = metric; + } - if ($key !== 1) { - const { data } = await getShareId(params) - handleCopyUri(`${origin}/share/analysis/${data}`) - return - } + if ($key === 1 && test_type === 'performance') { + params.metric = metric + .map((ctx: any) => { + const { name: metric_name } = ctx; + params[metric_name] = ctx.metricList?.toString(); + params[`::opts::${metric_name}`] = ctx.opts?.toString(); + return metric_name; + }) + .toString(); + } - const text = origin + pathname + '?' + stringify(params) - handleCopyUri(text) + if ($key !== 1) { + const { data } = await getShareId(params); + handleCopyUri(`${origin}/share/analysis/${data}`); + return; } - return ( - - - - - - - } - content={ - - - - - setShareRadioVal(evt?.target?.value)} - > - - { - [ - { - value: 1, - label: formatMessage({ id: 'analysis.share_link.radio1' }) - }, - { - value: 2, - label: formatMessage({ id: 'analysis.share_link.radio2' }) - }, - ].map((i: any) => ( -
- {i.label} -
- )) - } -
-
- -
- } - > - -
+ const text = origin + pathname + '?' + stringify(params); + handleCopyUri(text); + }; - { - info?.test_type === 'performance' ? - - - {formatMessage({ id: 'aligroupServer' })} - - - {formatMessage({ id: 'aliyunServer' })} - - : - - - - - - - - - } - - } + return ( + + + + +
+ + } + content={ + + + + + setShareRadioVal(evt?.target?.value)} > - { - tabData.map((i: any) => ( - - )) - } - - - - { - setInfo((p: any) => ({ ...p, ...i })) - }} - /> - + + {[ + { + value: 1, + label: formatMessage({ id: 'analysis.share_link.radio1' }), + }, + { + value: 2, + label: formatMessage({ id: 'analysis.share_link.radio2' }), + }, + ].map((i: any) => ( +
+ {i.label} +
+ ))} +
+ + + + } + > + + + + {info?.test_type === 'performance' ? ( + + + {formatMessage({ id: 'aligroupServer' })} + + + {formatMessage({ id: 'aliyunServer' })} + + + ) : ( + + + + + + + + + )} - - - ) -} + } + > + {tabData.map((i: any) => ( + + ))} + + +
+ + + + + + ); +}; -export default AnalysisTime +export default AnalysisTime; diff --git a/src/pages/WorkSpace/TestAnalysis/index.tsx b/src/pages/WorkSpace/TestAnalysis/index.tsx index 84fe51a4d458d1e8608bfb0093dd46968b0f692b..ab955b8ff285d0a5e3d4b644a93f3de13cffbf27 100644 --- a/src/pages/WorkSpace/TestAnalysis/index.tsx +++ b/src/pages/WorkSpace/TestAnalysis/index.tsx @@ -1,48 +1,47 @@ import { Button, Layout, Modal } from 'antd'; import { useState, useRef } from 'react'; import Icon from '@/assets/img/compareHome.png'; -import { useClientSize, writeDocumentTitle } from '@/utils/hooks'; -import { history, FormattedMessage, useParams } from 'umi' -import styles from './AnalysisCompare/index.less' +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; +import { history, FormattedMessage, useParams } from 'umi'; +import styles from './AnalysisCompare/index.less'; import Draggable from 'react-draggable'; -import AddJob from './AnalysisCompare/AddJob' +import AddJob from './AnalysisCompare/AddJob'; // import Icon from '@/assets/img/loss.png'; const addGroup = { members: [], - name: "Group1", - product_version: "Group1", - type: "job", - disabled: false -} + name: 'Group1', + product_version: 'Group1', + type: 'job', + disabled: false, +}; export default (props: any) => { - const { ws_id } = useParams() as any - writeDocumentTitle(`Workspace.TestAnalysis.${props.route.name}`) - const [disabled, setDisabled] = useState(true) + const { ws_id } = useParams() as any; + const [disabled, setDisabled] = useState(true); const [visibleAddGroupItem, setVisibleAddGroupItem] = useState(false); - const [bounds, setBounds] = useState({ left: 0, top: 0, bottom: 0, right: 0 }) + const [bounds, setBounds] = useState({ left: 0, top: 0, bottom: 0, right: 0 }); const draggleRef = useRef(null); - const { height: layoutHeight } = useClientSize() + const { height: layoutHeight } = useClientSize(); const handleAddJobGroup = () => { - setVisibleAddGroupItem(true) - } + setVisibleAddGroupItem(true); + }; const destroyAll = () => { Modal.destroyAll(); - } + }; const handleAddGroupItemCancel = () => { setVisibleAddGroupItem(false); - destroyAll() - } + destroyAll(); + }; const handleAddGroupItemOk = (obj: any) => { - setVisibleAddGroupItem(false) - window.sessionStorage.setItem(`${ws_id}-compareData`, JSON.stringify([obj])) - window.sessionStorage.setItem('originType', 'test_analysis') - window.sessionStorage.setItem(`${ws_id}-noGroupJobData`, JSON.stringify([])) - history.push(`/ws/${ws_id}/test_analysis/compare`) - } + setVisibleAddGroupItem(false); + window.sessionStorage.setItem(`${ws_id}-compareData`, JSON.stringify([obj])); + window.sessionStorage.setItem('originType', 'test_analysis'); + window.sessionStorage.setItem(`${ws_id}-noGroupJobData`, JSON.stringify([])); + history.push(`/ws/${ws_id}/test_analysis/compare`); + }; const onStart = (event: any, uiData: any) => { const { clientWidth, clientHeight } = window?.document?.documentElement; const targetRect = draggleRef?.current?.getBoundingClientRect(); @@ -51,21 +50,53 @@ export default (props: any) => { right: clientWidth - (targetRect?.right - uiData?.x), top: -targetRect?.top + uiData?.y, bottom: clientHeight - (targetRect?.bottom - uiData?.y), - }) - } + }); + }; return ( +
-
+
- icon + icon
-
-
+
+ +
+
+ +
- +
@@ -79,11 +110,11 @@ export default (props: any) => { }} onMouseOver={() => { if (disabled) { - setDisabled(false) + setDisabled(false); } }} onMouseOut={() => { - setDisabled(true) + setDisabled(true); }} > {'Group1'} @@ -98,7 +129,7 @@ export default (props: any) => { maskClosable={false} destroyOnClose={true} wrapClassName={styles.job_Modal} - modalRender={modal => ( + modalRender={(modal) => ( { )} > - +
- ) -} + ); +}; diff --git a/src/pages/WorkSpace/TestJob/index.tsx b/src/pages/WorkSpace/TestJob/index.tsx index b55a8f66f44aaf86a9b23922bd27e601c3d584e4..4c7f57a009b525512a91faed6453226825af7d63 100644 --- a/src/pages/WorkSpace/TestJob/index.tsx +++ b/src/pages/WorkSpace/TestJob/index.tsx @@ -2,275 +2,331 @@ /* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-use-before-define */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import React, { useState, useEffect, useRef, useMemo } from 'react' -import { Row, Tag, Space, Button, Col, Spin, Typography, message, Menu, Input, Popover, Popconfirm, notification } from 'antd' - -import { history, useRequest, useModel, useAccess, Access, useIntl, FormattedMessage, useParams, useLocation } from 'umi' -import { requestCodeMessage, AccessTootip, redirectErrorPage } from '@/utils/utils' -import { useClientSize, writeDocumentTitle, useCopyText } from '@/utils/hooks' -import styles from './index.less' -import EllipsisPulic from '@/components/Public/EllipsisPulic' -import NotLoggedIn from '@/components/Public/NotLoggedIn' -import { queryJobTypeItems } from '@/pages/WorkSpace/JobTypeManage/CreateJobType/services' -import { queryJobTypeList } from '@/pages/WorkSpace/JobTypeManage/services' - -import { ArrowLeftOutlined, SearchOutlined, CloseOutlined } from '@ant-design/icons' - -import BasciForm from './components/JobForms/BasicForm' -import EnvForm from './components/JobForms/EnvForm' -import MoreForm from './components/JobForms/MoreForm' -import SelectSuite from './components/SelectSuite' -import SaveTemplate from './components/SaveTemplate' -import TemplateForm from './components/JobForms/TemplateForm' - -import { createWsJobTest, queryTestTemplateData, queryTestExportValues, formatYamlToJson, testYaml, queryCheckJobTemplate } from './services' -import { saveTestTemplate, queryTestTemplateList, updateTestTemplate } from '@/pages/WorkSpace/TestTemplateManage/service' - -import _ from 'lodash' -import { ReactComponent as YamlFormat } from '@/assets/svg/yaml_format.svg' -import { ReactComponent as YamlCopy } from '@/assets/svg/yaml_copy.svg' -import { ReactComponent as YamlDownload } from '@/assets/svg/yaml_download.svg' -import { ReactComponent as YamlTest } from '@/assets/svg/yaml_test.svg' -import CodeEditer from '@/components/CodeEditer' -import { useSize } from 'ahooks' +import React, { useState, useEffect, useRef, useMemo } from 'react'; +import { + Row, + Tag, + Space, + Button, + Col, + Spin, + Typography, + message, + Menu, + Input, + Popover, + Popconfirm, + notification, +} from 'antd'; + +import { + history, + useRequest, + useModel, + useAccess, + Access, + useIntl, + FormattedMessage, + useParams, + useLocation, +} from 'umi'; +import { requestCodeMessage, AccessTootip, redirectErrorPage } from '@/utils/utils'; +import { useClientSize, DocTitleWrite, useCopyText } from '@/utils/hooks'; +import styles from './index.less'; +import EllipsisPulic from '@/components/Public/EllipsisPulic'; +import NotLoggedIn from '@/components/Public/NotLoggedIn'; +import { queryJobTypeItems } from '@/pages/WorkSpace/JobTypeManage/CreateJobType/services'; +import { queryJobTypeList } from '@/pages/WorkSpace/JobTypeManage/services'; + +import { ArrowLeftOutlined, SearchOutlined, CloseOutlined } from '@ant-design/icons'; + +import BasciForm from './components/JobForms/BasicForm'; +import EnvForm from './components/JobForms/EnvForm'; +import MoreForm from './components/JobForms/MoreForm'; +import SelectSuite from './components/SelectSuite'; +import SaveTemplate from './components/SaveTemplate'; +import TemplateForm from './components/JobForms/TemplateForm'; + +import { + createWsJobTest, + queryTestTemplateData, + queryTestExportValues, + formatYamlToJson, + testYaml, + queryCheckJobTemplate, +} from './services'; +import { + saveTestTemplate, + queryTestTemplateList, + updateTestTemplate, +} from '@/pages/WorkSpace/TestTemplateManage/service'; + +import _ from 'lodash'; +import { ReactComponent as YamlFormat } from '@/assets/svg/yaml_format.svg'; +import { ReactComponent as YamlCopy } from '@/assets/svg/yaml_copy.svg'; +import { ReactComponent as YamlDownload } from '@/assets/svg/yaml_download.svg'; +import { ReactComponent as YamlTest } from '@/assets/svg/yaml_test.svg'; +import CodeEditer from '@/components/CodeEditer'; +import { useSize } from 'ahooks'; interface PropsTypes { - templateEditFormInfo?: any, - basicFormInfo?: any, - envFormInfo?: any, - moreFormInfo?: any, - new_test_config?: any + templateEditFormInfo?: any; + basicFormInfo?: any; + envFormInfo?: any; + moreFormInfo?: any; + new_test_config?: any; } const TestJob: React.FC = (props) => { - const { formatMessage } = useIntl() - const { name } = props.route - const { height: layoutHeight, width: layoutWidth } = useClientSize() - const hasNav = ["TemplatePreview", "TemplateEdit", "JobTypePreview"].includes(name) + const { formatMessage } = useIntl(); + const { name } = props.route; + const { height: layoutHeight, width: layoutWidth } = useClientSize(); + const hasNav = ['TemplatePreview', 'TemplateEdit', 'JobTypePreview'].includes(name); - const { initialState, setInitialState } = useModel('@@initialState') + const { initialState, setInitialState } = useModel('@@initialState'); const { authList } = initialState; - const { ws_id, jt_id } = useParams() as any - const { query, state, pathname } = useLocation() as any + const { ws_id, jt_id } = useParams() as any; + const { query, state, pathname } = useLocation() as any; const access = useAccess(); - writeDocumentTitle(`Workspace.${name}`) - const [test_config, setTest_config] = useState([]) - const [detail, setDetail] = useState({ name: '', server_type: '', test_type: '' }) - const [items, setItems] = useState({ basic: {}, env: {}, suite: {}, more: {} }) - const [loading, setLoading] = useState(true) - const [envErrorFlag, setEnvErrorFlag] = useState(false) - const [isloading, setIsloading] = useState(false) - const [disabled, setDisabled] = useState(name === 'TemplatePreview') - const [modifyTemplate, setModifyTemplate] = useState(false) - const [templateDatas, setTemplateDatas] = useState({}) - const [templateBtnVisible, setTemplateBtnVisible] = useState(false) - const moreForm: any = useRef(null) - const envForm: any = useRef(null) - const basicForm: any = useRef(null) - const suiteTable: any = useRef(null) - const bodyRef: any = useRef(null) + const [test_config, setTest_config] = useState([]); + const [detail, setDetail] = useState({ name: '', server_type: '', test_type: '' }); + const [items, setItems] = useState({ basic: {}, env: {}, suite: {}, more: {} }); + const [loading, setLoading] = useState(true); + const [envErrorFlag, setEnvErrorFlag] = useState(false); + const [isloading, setIsloading] = useState(false); + const [disabled, setDisabled] = useState(name === 'TemplatePreview'); + const [modifyTemplate, setModifyTemplate] = useState(false); + const [templateDatas, setTemplateDatas] = useState({}); + const [templateBtnVisible, setTemplateBtnVisible] = useState(false); + const moreForm: any = useRef(null); + const envForm: any = useRef(null); + const basicForm: any = useRef(null); + const suiteTable: any = useRef(null); + const bodyRef: any = useRef(null); // yaml 回显 - const projectListData: any = useRef(null) - const baselineListData: any = useRef(null) - const tagsData: any = useRef(null) - const reportTemplateData: any = useRef(null) - const caseData: any = useRef(null) - - const saveTemplateDrawer: any = useRef() - const templateEditForm: any = useRef() - const [projectId, setProjectId] = useState() - const [templateEnabel, setTemplateEnable] = useState(false) - const [fetching, setFetching] = useState(false) - const [newSaveLoading, setNewSaveLoading] = useState(false) - const [isReset, setIsReset] = useState(false) - const [timeTagList, setTimeTagList]: any = useState([]) - - const [jobInfo, setJobInfo] = useState('') - const [isYamlFormat, setIsYamlFormat] = useState(false) - const { data: templateList, run: requestTemplateRun, refresh: templatePopoverRefresh } = useRequest( - (params: any) => queryTestTemplateList(params), - { - initialData: [], - throttleInterval: 300, - manual: true, - } - ) + const projectListData: any = useRef(null); + const baselineListData: any = useRef(null); + const tagsData: any = useRef(null); + const reportTemplateData: any = useRef(null); + const caseData: any = useRef(null); + + const saveTemplateDrawer: any = useRef(); + const templateEditForm: any = useRef(); + const [projectId, setProjectId] = useState(); + const [templateEnabel, setTemplateEnable] = useState(false); + const [fetching, setFetching] = useState(false); + const [newSaveLoading, setNewSaveLoading] = useState(false); + const [isReset, setIsReset] = useState(false); + const [timeTagList, setTimeTagList]: any = useState([]); + + const [jobInfo, setJobInfo] = useState(''); + const [isYamlFormat, setIsYamlFormat] = useState(false); + const { + data: templateList, + run: requestTemplateRun, + refresh: templatePopoverRefresh, + } = useRequest((params: any) => queryTestTemplateList(params), { + initialData: [], + throttleInterval: 300, + manual: true, + }); const handleModifySetting = () => { - setDisabled(false) - setModifyTemplate(true) - } + setDisabled(false); + setModifyTemplate(true); + }; const goValidate = (form: any) => { - return new Promise( - (resolve, reject) => ( - form - .validateFields() - .then(resolve) - .catch(() => { - reject() - setFetching(false) - setNewSaveLoading(false) - }) - ) - ) - } + return new Promise((resolve, reject) => + form + .validateFields() + .then(resolve) + .catch(() => { + reject(); + setFetching(false); + setNewSaveLoading(false); + }), + ); + }; const getPageData = async () => { - setLoading(true) - let job_type_id = jt_id + setLoading(true); + let job_type_id = jt_id; if (name === 'TestExport') { - const { data } = await queryTestExportValues({ job_id: jt_id, ...query }) - setTemplateDatas(data) - setTest_config(data.test_config) - job_type_id = data.job_type_id + const { data } = await queryTestExportValues({ job_id: jt_id, ...query }); + setTemplateDatas(data); + setTest_config(data.test_config); + job_type_id = data.job_type_id; } - if (["TestTemplate", "TemplatePreview", "TemplateEdit", "TestJob"].includes(name)) { - let template_id: any = null - if (name === 'TestJob' && query.template_id) - template_id = query.template_id + if (['TestTemplate', 'TemplatePreview', 'TemplateEdit', 'TestJob'].includes(name)) { + let template_id: any = null; + if (name === 'TestJob' && query.template_id) template_id = query.template_id; - if (["TestTemplate", "TemplatePreview", "TemplateEdit"].includes(name)) - template_id = jt_id + if (['TestTemplate', 'TemplatePreview', 'TemplateEdit'].includes(name)) + template_id = jt_id; if (template_id) { - const { code, data } = await queryTestTemplateData({ template_id }) + const { code, data } = await queryTestTemplateData({ template_id }); if (code === 500) { - redirectErrorPage(500) - return + redirectErrorPage(500); + return; } - const [datas] = data - job_type_id = datas.job_type_id - setTemplateDatas(datas) - setTest_config(datas.test_config) - setTemplateEnable(datas.enable) - } - else { - setTemplateDatas({}) + const [datas] = data; + job_type_id = datas.job_type_id; + setTemplateDatas(datas); + setTest_config(datas.test_config); + setTemplateEnable(datas.enable); + } else { + setTemplateDatas({}); } } - const { data: [jobTypedetail] } = await queryJobTypeList({ jt_id: job_type_id }) - if (!jobTypedetail) - return redirectErrorPage(404) - setDetail(jobTypedetail) - const { data: items } = await queryJobTypeItems({ jt_id: job_type_id }) - filterItems(items) - setLoading(false) - if (name === 'TestJob') requestTemplateRun({ ws_id, job_type_id, enable: 'True' }) - } + const { + data: [jobTypedetail], + } = await queryJobTypeList({ jt_id: job_type_id }); + if (!jobTypedetail) return redirectErrorPage(404); + setDetail(jobTypedetail); + const { data: items } = await queryJobTypeItems({ jt_id: job_type_id }); + filterItems(items); + setLoading(false); + if (name === 'TestJob') requestTemplateRun({ ws_id, job_type_id, enable: 'True' }); + }; const filterItems = (t: any) => { - const basic: any = {}, env: any = {}, suite: any = {}, more: any = {} + const basic: any = {}, + env: any = {}, + suite: any = {}, + more: any = {}; t?.forEach((i: any) => { - if (i.config_index === 1) basic[i.name] = i - if (i.config_index === 2) env[i.name] = i - if (i.config_index === 3) suite[i.name] = i - if (i.config_index === 4) more[i.name] = i - }) + if (i.config_index === 1) basic[i.name] = i; + if (i.config_index === 2) env[i.name] = i; + if (i.config_index === 3) suite[i.name] = i; + if (i.config_index === 4) more[i.name] = i; + }); - setItems({ basic, env, suite, more }) - } + setItems({ basic, env, suite, more }); + }; - const handleCopyText = useCopyText(formatMessage({ id: 'request.copy.success' })) + const handleCopyText = useCopyText(formatMessage({ id: 'request.copy.success' })); const handleReset = (flag = false) => { - setIsReset(flag) - setJobInfo('') - templateEditForm.current?.reset() - basicForm.current?.reset() - moreForm.current?.reset() - envForm.current?.reset() - suiteTable.current?.reset() - } + setIsReset(flag); + setJobInfo(''); + templateEditForm.current?.reset(); + basicForm.current?.reset(); + moreForm.current?.reset(); + envForm.current?.reset(); + suiteTable.current?.reset(); + }; //数据初始化 init hooks //新建job 1 jobType预览 2 模板预览 3 模板测试 4 useEffect(() => { - handleReset() - getPageData() - }, [pathname, query]) + handleReset(); + getPageData(); + }, [pathname, query]); const compact = (obj: any) => { - const result: any = {} - Object.keys(obj).forEach( - key => { - const z = obj[key] - if (z === null || z === undefined || z === '') - return - const t = Object.prototype.toString.call(z) - if (t === '[object Array]') { - const arrayItem = z.filter( - (item: any) => { - let noData = false - Object.keys(item).forEach( - ctx => { - const o = item[ctx] - if (o === null || o === undefined || o === '') - noData = true - } - ) - if (!noData) - return item - } - ) - if (arrayItem.length !== 0) - result[key] = arrayItem - } - else if (t === '[object Object]') { - if (JSON.stringify(z) !== '{}') - result[key] = compact(z) - } - else - result[key] = z - } - ) - return Object.assign({}, result) - } - + const result: any = {}; + Object.keys(obj).forEach((key) => { + const z = obj[key]; + if (z === null || z === undefined || z === '') return; + const t = Object.prototype.toString.call(z); + if (t === '[object Array]') { + const arrayItem = z.filter((item: any) => { + let noData = false; + Object.keys(item).forEach((ctx) => { + const o = item[ctx]; + if (o === null || o === undefined || o === '') noData = true; + }); + if (!noData) return item; + }); + if (arrayItem.length !== 0) result[key] = arrayItem; + } else if (t === '[object Object]') { + if (JSON.stringify(z) !== '{}') result[key] = compact(z); + } else result[key] = z; + }); + return Object.assign({}, result); + }; const transformDate = async (result: PropsTypes | null = null) => { - let data: any = {} - let installKernel = '' + let data: any = {}; + let installKernel = ''; if (name === 'TestTemplate' || name === 'TemplatePreview' || name === 'TemplateEdit') { - const templateFormVal = result ? result.templateEditFormInfo : await goValidate(templateEditForm.current.form) - data = Object.assign(data, compact(templateFormVal)) + const templateFormVal = result + ? result.templateEditFormInfo + : await goValidate(templateEditForm.current.form); + data = Object.assign(data, compact(templateFormVal)); } if (JSON.stringify(items.basic) !== '{}') { - const basicVal = result ? result.basicFormInfo : await goValidate(basicForm.current.form) - data = Object.assign(data, compact(basicVal)) + const basicVal = result + ? result.basicFormInfo + : await goValidate(basicForm.current.form); + data = Object.assign(data, compact(basicVal)); } if (JSON.stringify(items.env) !== '{}') { - const envVal = result ? result.envFormInfo : await goValidate(envForm.current.form) + const envVal = result ? result.envFormInfo : await goValidate(envForm.current.form); const { - env_info, rpm_info, script_info, moniter_contrl, - monitor_info, reclone_contrl, app_name, os, - vm, need_reboot, kernel_version, kernel_install, - code_repo, code_branch, compile_branch, cpu_arch, commit_id, kernel_packages, - build_config, build_machine, scripts, kernel, devel, headers, hotfix_install, ...rest - }: any = envVal - - installKernel = kernel_install - const envStr = env_info + env_info, + rpm_info, + script_info, + moniter_contrl, + monitor_info, + reclone_contrl, + app_name, + os, + vm, + need_reboot, + kernel_version, + kernel_install, + code_repo, + code_branch, + compile_branch, + cpu_arch, + commit_id, + kernel_packages, + build_config, + build_machine, + scripts, + kernel, + devel, + headers, + hotfix_install, + ...rest + }: any = envVal; + + installKernel = kernel_install; + const envStr = env_info; const build_pkg_info = { - code_repo, code_branch, compile_branch, cpu_arch, commit_id, - build_config, build_machine, scripts, ...rest - } - - const kernel_info = { hotfix_install, scripts, - kernel_packages: kernel_packages?.map((item: any)=> item?.trim()), // 去除输入内容两端空格 - } - - let scriptInfo = script_info - let rpmInfo = rpm_info + code_repo, + code_branch, + compile_branch, + cpu_arch, + commit_id, + build_config, + build_machine, + scripts, + ...rest, + }; + + const kernel_info = { + hotfix_install, + scripts, + kernel_packages: kernel_packages?.map((item: any) => item?.trim()), // 去除输入内容两端空格 + }; + + let scriptInfo = script_info; + let rpmInfo = rpm_info; if (!need_reboot) { - rpmInfo = rpmInfo ? rpmInfo.map((i: any) => ({ ...i, pos: 'before' })) : undefined - scriptInfo = scriptInfo ? scriptInfo.map((i: any) => ({ ...i, pos: 'before' })) : undefined + rpmInfo = rpmInfo ? rpmInfo.map((i: any) => ({ ...i, pos: 'before' })) : undefined; + scriptInfo = scriptInfo + ? scriptInfo.map((i: any) => ({ ...i, pos: 'before' })) + : undefined; } const envProps: any = { @@ -280,24 +336,26 @@ const TestJob: React.FC = (props) => { moniter_contrl, // monitor_info, reclone_contrl, - iclone_info: (os || app_name || vm) ? { os, app_name, vm } : undefined, + iclone_info: os || app_name || vm ? { os, app_name, vm } : undefined, need_reboot, kernel_version, - } + }; - if (code_repo) envProps.build_pkg_info = build_pkg_info - else envProps.kernel_info = kernel_info + if (code_repo) envProps.build_pkg_info = build_pkg_info; + else envProps.kernel_info = kernel_info; - data = Object.assign(data, compact(envProps), { monitor_info }) + data = Object.assign(data, compact(envProps), { monitor_info }); } if (JSON.stringify(items.more) !== '{}') { - const moreVal: any = result ? result.moreFormInfo : await goValidate(moreForm.current.form) - const email = moreVal.email ? moreVal.email.replace(/\s/g, ',') : null - data = Object.assign(data, compact({ ...moreVal, email })) + const moreVal: any = result + ? result.moreFormInfo + : await goValidate(moreForm.current.form); + const email = moreVal.email ? moreVal.email.replace(/\s/g, ',') : null; + data = Object.assign(data, compact({ ...moreVal, email })); } - const testConfigData = result ? result.new_test_config : test_config + const testConfigData = result ? result.new_test_config : test_config; if (testConfigData.length > 0) { const test_conf = testConfigData.map((item: any) => { @@ -309,8 +367,8 @@ const TestJob: React.FC = (props) => { priority, cleanup_info, run_mode, - test_case_list: cases - }: any = item + test_case_list: cases, + }: any = item; return { ...compact({ test_suite, @@ -342,20 +400,24 @@ const TestJob: React.FC = (props) => { // }, is_instance, custom_ip, - custom_channel - } = ctx + custom_channel, + } = ctx; const envs: any = env_info?.filter((i: any) => { - if (i.name && i.val) return i - }) - const evnInfoStr = envs?.reduce((i: any, p: any, idx: number) => i.concat(`${idx ? '\n' : ''}${p.name?.trim()}=${p.val}`), '') - - let customer_server = undefined + if (i.name && i.val) return i; + }); + const evnInfoStr = envs?.reduce( + (i: any, p: any, idx: number) => + i.concat(`${idx ? '\n' : ''}${p.name?.trim()}=${p.val}`), + '', + ); + + let customer_server = undefined; if (custom_channel && custom_ip) { customer_server = { custom_channel, - custom_ip - } + custom_ip, + }; } // console.log(monitor_info) @@ -378,183 +440,200 @@ const TestJob: React.FC = (props) => { // ip, // tag: tag && _.isArray(tag) ? tag.toString() : '', // }, - customer_server - }) - }) - } - }) - data.test_config = test_conf + customer_server, + }); + }), + }; + }); + data.test_config = test_conf; } - if (installKernel !== 'install_push') - data.kernel_version = null - const report_template = data.report_template - delete data.report_template - if ('report_name' in data && _.get(data, 'report_name')) data.report_template = report_template - return data - } + if (installKernel !== 'install_push') data.kernel_version = null; + const report_template = data.report_template; + delete data.report_template; + if ('report_name' in data && _.get(data, 'report_name')) + data.report_template = report_template; + return data; + }; const handleFormatChange = async (operateType: any = '') => { - let parmas = {} - const envVal = envForm && envForm.current ? await goValidate(envForm.current.form) : {} - const server_provider = detail?.server_type + let parmas = {}; + const envVal = envForm && envForm.current ? await goValidate(envForm.current.form) : {}; + const server_provider = detail?.server_type; // operateType !== 'template' && setIsloading(true) - setIsloading(true) + setIsloading(true); if (!isYamlFormat) { - const formData = await transformDate() - const dataCopy = _.cloneDeep(formData) - const $test_config = _.get(dataCopy, 'test_config') + const formData = await transformDate(); + const dataCopy = _.cloneDeep(formData); + const $test_config = _.get(dataCopy, 'test_config'); if (_.isArray($test_config)) { $test_config.forEach((item: any) => { - let cases = _.get(item, 'cases') - const runMode = _.get(item, 'run_mode') + let cases = _.get(item, 'cases'); + const runMode = _.get(item, 'run_mode'); if (_.isArray(cases)) { cases = cases.map((obj: any) => { - let server: AnyType = {} - const objCopy = _.cloneDeep(obj) - if (_.has(objCopy, 'server_object_id') && objCopy.server_object_id !== undefined) { + let server: AnyType = {}; + const objCopy = _.cloneDeep(obj); + if ( + _.has(objCopy, 'server_object_id') && + objCopy.server_object_id !== undefined + ) { // server = {} - const is_instance = _.get(objCopy, 'is_instance') - let key = 'id' - if (is_instance === 0 && server_provider === 'aliyun') key = 'config' - if (is_instance === 1 && server_provider === 'aliyun') key = 'instance' - if (runMode === 'cluster') key = 'cluster' - server[key] = _.get(objCopy, 'server_object_id') + const is_instance = _.get(objCopy, 'is_instance'); + let key = 'id'; + if (is_instance === 0 && server_provider === 'aliyun') + key = 'config'; + if (is_instance === 1 && server_provider === 'aliyun') + key = 'instance'; + if (runMode === 'cluster') key = 'cluster'; + server[key] = _.get(objCopy, 'server_object_id'); } - if (_.has(objCopy, 'server_tag_id') && objCopy.server_tag_id.length) server = { tag: _.get(objCopy, 'server_tag_id') } + if (_.has(objCopy, 'server_tag_id') && objCopy.server_tag_id.length) + server = { tag: _.get(objCopy, 'server_tag_id') }; if (_.get(objCopy, 'customer_server')) { - const customServer = _.get(objCopy, 'customer_server') - server = { channel_type: customServer.custom_channel, ip: customServer.custom_ip } + const customServer = _.get(objCopy, 'customer_server'); + server = { + channel_type: customServer.custom_channel, + ip: customServer.custom_ip, + }; } - if (server) obj.server = server - delete obj.server_object_id - delete obj.server_tag_id - delete item.run_mode - delete obj.is_instance - delete obj.customer_server - return obj - }) - item.cases = cases + if (server) obj.server = server; + delete obj.server_object_id; + delete obj.server_tag_id; + delete item.run_mode; + delete obj.is_instance; + delete obj.customer_server; + return obj; + }); + item.cases = cases; } - }) + }); } - dataCopy.test_config = $test_config - delete dataCopy.moniter_contrl - delete dataCopy.reclone_contrl - if (!dataCopy.kernel_version) delete dataCopy.kernel_version - parmas = { type: 'json2yaml', json_data: dataCopy, workspace: ws_id } + dataCopy.test_config = $test_config; + delete dataCopy.moniter_contrl; + delete dataCopy.reclone_contrl; + if (!dataCopy.kernel_version) delete dataCopy.kernel_version; + parmas = { type: 'json2yaml', json_data: dataCopy, workspace: ws_id }; } - if (isYamlFormat) parmas = { type: 'yaml2json', yaml_data: jobInfo, workspace: ws_id } + if (isYamlFormat) parmas = { type: 'yaml2json', yaml_data: jobInfo, workspace: ws_id }; - const { code, msg, data } = await formatYamlToJson(parmas) + const { code, msg, data } = await formatYamlToJson(parmas); // operateType !== 'template' && setIsloading(false) - setIsloading(false) - let result = {} + setIsloading(false); + let result = {}; if (code === 200) { - if (!isYamlFormat) setJobInfo(data) + if (!isYamlFormat) setJobInfo(data); if (isYamlFormat) { - const dataCopy = _.cloneDeep(data) - const { test_config: $suite_list } = data + const dataCopy = _.cloneDeep(data); + const { test_config: $suite_list } = data; if (_.isArray($suite_list)) { $suite_list.forEach((item: any) => { - let cases = _.get(item, 'cases') - item.run_mode = 'standalone' + let cases = _.get(item, 'cases'); + item.run_mode = 'standalone'; if (_.isArray(cases)) { cases = cases.map((obj: any) => { - const server = _.get(obj, 'server') + const server = _.get(obj, 'server'); if (_.has(server, 'id')) { - obj.server_object_id = _.get(server, 'id') + obj.server_object_id = _.get(server, 'id'); } if (_.get(server, 'config')) { - obj.server_object_id = _.get(server, 'config') - obj.is_instance = 0 + obj.server_object_id = _.get(server, 'config'); + obj.is_instance = 0; } if (_.get(server, 'instance')) { - obj.server_object_id = _.get(server, 'instance') - obj.is_instance = 1 + obj.server_object_id = _.get(server, 'instance'); + obj.is_instance = 1; } if (_.get(server, 'cluster')) { - obj.server_object_id = _.get(server, 'cluster') - item.run_mode = 'cluster' + obj.server_object_id = _.get(server, 'cluster'); + item.run_mode = 'cluster'; } - if (_.get(server, 'tag') && _.get(server, 'tag').length) obj.server_tag_id = _.get(server, 'tag') + if (_.get(server, 'tag') && _.get(server, 'tag').length) + obj.server_tag_id = _.get(server, 'tag'); if (_.get(server, 'channel_type') && _.get(server, 'ip')) { - obj.customer_server = { custom_channel: _.get(server, 'channel_type'), custom_ip: _.get(server, 'ip') } - obj.ip = _.get(server, 'ip') + obj.customer_server = { + custom_channel: _.get(server, 'channel_type'), + custom_ip: _.get(server, 'ip'), + }; + obj.ip = _.get(server, 'ip'); } if (_.get(server, 'name')) { - let ip = server.name - if (_.isArray(ip)) ip = ip.join(',') - obj.ip = ip + let ip = server.name; + if (_.isArray(ip)) ip = ip.join(','); + obj.ip = ip; } - delete obj.server - return obj - }) - item.cases = cases + delete obj.server; + return obj; + }); + item.cases = cases; } - }) + }); } - dataCopy.test_config = $suite_list?.map((i: any) => ({ ...i, test_suite_id: i.test_suite, cases: i.cases.map((t: any) => ({ ...t, test_case_id: t.test_case })) })) - dataCopy.moniter_contrl = false - dataCopy.reclone_contrl = _.get(envVal, 'reclone_contrl') || false - if (_.get(dataCopy, 'monitor_info')) dataCopy.moniter_contrl = true + dataCopy.test_config = $suite_list?.map((i: any) => ({ + ...i, + test_suite_id: i.test_suite, + cases: i.cases.map((t: any) => ({ ...t, test_case_id: t.test_case })), + })); + dataCopy.moniter_contrl = false; + dataCopy.reclone_contrl = _.get(envVal, 'reclone_contrl') || false; + if (_.get(dataCopy, 'monitor_info')) dataCopy.moniter_contrl = true; // 以下是表单值的同步 - result = getFormData(dataCopy) + result = getFormData(dataCopy); } if (operateType !== 'template' && operateType !== 'submit') - setIsYamlFormat(!isYamlFormat) + setIsYamlFormat(!isYamlFormat); } else { - requestCodeMessage(code, msg) + requestCodeMessage(code, msg); } - return { code, result } - } + return { code, result }; + }; const handleServerChannel = (testConfig: any[]) => { - const flag = location.search.indexOf('inheriting_machine') !== -1 + const flag = location.search.indexOf('inheriting_machine') !== -1; return testConfig.map((item: any) => { - const cases = _.get(item, 'cases') || [] + const cases = _.get(item, 'cases') || []; item.cases = cases.map((ctx: any) => { - const { customer_server, server_object_id } = ctx || {} - const channel_type = _.get(customer_server, 'custom_channel') - const ip = _.get(customer_server, 'custom_ip') + const { customer_server, server_object_id } = ctx || {}; + const channel_type = _.get(customer_server, 'custom_channel'); + const ip = _.get(customer_server, 'custom_ip'); if (channel_type && ip) { ctx.server = { ip, - channel_type - } - delete ctx.customer_server + channel_type, + }; + delete ctx.customer_server; } if (flag && server_object_id) { - delete ctx.server_tag_id + delete ctx.server_tag_id; } - return ctx - }) + return ctx; + }); - return item - }) - } + return item; + }); + }; const handleSubmit = async () => { - if (fetching) return - setEnvErrorFlag(false) - let formData = {} - setFetching(true) + if (fetching) return; + setEnvErrorFlag(false); + let formData = {}; + setFetching(true); if (isYamlFormat) { - const { code, result = {} } = await handleFormatChange('submit') + const { code, result = {} } = await handleFormatChange('submit'); if (code !== 200) { - setFetching(false) - return + setFetching(false); + return; } - formData = await transformDate(result) + formData = await transformDate(result); } - formData = await transformDate() + formData = await transformDate(); let data = { ...formData, workspace: ws_id, job_type: detail.id, - } + }; if (name === 'TestTemplate') { data = { @@ -562,52 +641,61 @@ const TestJob: React.FC = (props) => { data_from: 'template', template_id: templateDatas.id, job_type: detail.id, - } + }; } if (name === 'TestExport') { data = { ...data, data_from: 'rerun', - job_id: jt_id - } + job_id: jt_id, + }; } if (isMonitorEmpty(data)) { - setFetching(false) - return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })) + setFetching(false); + return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })); } if (!data.test_config) { - setFetching(false) - return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })) + setFetching(false); + return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })); } // console.log(data.test_config) - const $test_config = handleServerChannel(data.test_config) - const { code, msg } = await createWsJobTest({ ...data, test_config: $test_config }).catch(()=> setFetching(false)) + const $test_config = handleServerChannel(data.test_config); + const { code, msg } = await createWsJobTest({ ...data, test_config: $test_config }).catch( + () => setFetching(false), + ); if (code === 200) { - setInitialState({ ...initialState, refreshMenu: !initialState?.refreshMenu }) - history.push(`/ws/${ws_id}/test_result`) - message.success(formatMessage({ id: 'ws.test.job.operation.success' })) + setInitialState({ ...initialState, refreshMenu: !initialState?.refreshMenu }); + history.push(`/ws/${ws_id}/test_result`); + message.success(formatMessage({ id: 'ws.test.job.operation.success' })); } else if (code === 1380) { - setEnvErrorFlag(true) + setEnvErrorFlag(true); } else { - requestCodeMessage(code, msg) + requestCodeMessage(code, msg); } - setFetching(false) - } + setFetching(false); + }; const isMonitorEmpty = (data: any) => { - let flag = false + let flag = false; if (data && data.moniter_contrl) { - const arrData = Array.isArray(data.monitor_info) ? data.monitor_info : [] - flag = arrData.some((item: any) => item && item.monitor_type === 'custom_machine' && !item.server) + const arrData = Array.isArray(data.monitor_info) ? data.monitor_info : []; + flag = arrData.some( + (item: any) => item && item.monitor_type === 'custom_machine' && !item.server, + ); } - return flag - } + return flag; + }; const getFormData = (dataCopy: any) => { - const { template_name, description, enable, - name, project, baseline, + const { + template_name, + description, + enable, + name, + project, + baseline, monitor_info, need_reboot, rpm_info, @@ -620,12 +708,19 @@ const TestJob: React.FC = (props) => { reclone_contrl, moniter_contrl, test_config, - cleanup_info, tags, notice_subject, report_name, callback_api, email, ding_token, report_template - } = dataCopy - const { os = '', app_name = '' } = iclone_info || {} - const { branch, ...kernels } = kernel_info || {} - const templateEditFormInfo = { template_name, description, enable } - const basicFormInfo = { name, project, baseline } + cleanup_info, + tags, + notice_subject, + report_name, + callback_api, + email, + ding_token, + report_template, + } = dataCopy; + const { os = '', app_name = '' } = iclone_info || {}; + const { branch, ...kernels } = kernel_info || {}; + const templateEditFormInfo = { template_name, description, enable }; + const basicFormInfo = { name, project, baseline }; const envFormInfo = { monitor_info, rpm_info, @@ -638,10 +733,10 @@ const TestJob: React.FC = (props) => { moniter_contrl, kernel_version, ...kernels, - ...build_pkg_info - } + ...build_pkg_info, + }; - const testConfigInfo = test_config + const testConfigInfo = test_config; const moreFormInfo = { cleanup_info, tags, @@ -650,126 +745,132 @@ const TestJob: React.FC = (props) => { ding_token, report_template, report_name, - callback_api - } - templateEditForm.current?.setVal(templateEditFormInfo) - basicForm.current?.setVal(basicFormInfo) - envForm.current?.setVal({ ...envFormInfo, kernel_info, build_pkg_info }) - moreForm.current?.setVal(moreFormInfo) - const new_test_config = suiteTable.current?.setVal(testConfigInfo) || [] - return { templateEditFormInfo, basicFormInfo, envFormInfo, moreFormInfo, new_test_config } - } + callback_api, + }; + templateEditForm.current?.setVal(templateEditFormInfo); + basicForm.current?.setVal(basicFormInfo); + envForm.current?.setVal({ ...envFormInfo, kernel_info, build_pkg_info }); + moreForm.current?.setVal(moreFormInfo); + const new_test_config = suiteTable.current?.setVal(testConfigInfo) || []; + return { templateEditFormInfo, basicFormInfo, envFormInfo, moreFormInfo, new_test_config }; + }; const handleSaveTemplateOk = async (vals: any) => { - if (fetching) return false - setFetching(true) - let data = await transformDate() + if (fetching) return false; + setFetching(true); + let data = await transformDate(); if (isMonitorEmpty(data)) { - setFetching(false) - return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })) + setFetching(false); + return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })); } if (!data.test_config) { - setFetching(false) - return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })) + setFetching(false); + return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })); } data = { workspace: ws_id, ...data, - job_type: detail.id - } - const $test_config = handleServerChannel(data.test_config) - const { code, msg } = await saveTestTemplate({ ...data, test_config: $test_config, ...vals }).catch(()=> setFetching(false)) + job_type: detail.id, + }; + const $test_config = handleServerChannel(data.test_config); + const { code, msg } = await saveTestTemplate({ + ...data, + test_config: $test_config, + ...vals, + }).catch(() => setFetching(false)); if (code === 200) { - message.success(formatMessage({ id: 'ws.test.job.operation.success' })) - saveTemplateDrawer.current.hide() - templatePopoverRefresh() - setInitialState({ ...initialState, refreshMenu: !initialState?.refreshMenu }) - } - else - requestCodeMessage(code, msg) - setFetching(false) - } + message.success(formatMessage({ id: 'ws.test.job.operation.success' })); + saveTemplateDrawer.current.hide(); + templatePopoverRefresh(); + setInitialState({ ...initialState, refreshMenu: !initialState?.refreshMenu }); + } else requestCodeMessage(code, msg); + setFetching(false); + }; const handleOpenTemplate = async () => { - let resultData = {} + let resultData = {}; if (isYamlFormat) { - const { code, result } = await handleFormatChange('template') - resultData = result - if (code !== 200) return + const { code, result } = await handleFormatChange('template'); + resultData = result; + if (code !== 200) return; } - const data = isYamlFormat ? await transformDate(resultData) : await transformDate() - if (isMonitorEmpty(data)) return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })) - if (!data.test_config) return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })) - if (name === 'TestJob' || name === 'TestExport') - saveTemplateDrawer.current.show() - else - handleSaveTemplateOk({}) - } + const data = isYamlFormat ? await transformDate(resultData) : await transformDate(); + if (isMonitorEmpty(data)) + return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })); + if (!data.test_config) + return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })); + if (name === 'TestJob' || name === 'TestExport') saveTemplateDrawer.current.show(); + else handleSaveTemplateOk({}); + }; const handleChangeTemplateName = ({ target }: any) => { - requestTemplateRun({ ws_id, job_type_id: detail.id, name: target.value }) - } + requestTemplateRun({ ws_id, job_type_id: detail.id, name: target.value }); + }; const handleTemplateEditFunction = async (data: any) => { - const $test_config = handleServerChannel(data.test_config) + const $test_config = handleServerChannel(data.test_config); const { code, msg } = await updateTestTemplate({ template_id: templateDatas.id, workspace: ws_id, job_type: detail.id, ...data, - test_config: $test_config - }) - notification.destroy() + test_config: $test_config, + }); + notification.destroy(); if (code === 200) { - const { data: [datas] } = await queryTestTemplateData({ template_id: templateDatas.id }) - setTemplateDatas(datas) - setModifyTemplate(false) - setDisabled(true) - setTemplateEnable(data.enable) - message.success(formatMessage({ id: 'operation.success' })) + const { + data: [datas], + } = await queryTestTemplateData({ template_id: templateDatas.id }); + setTemplateDatas(datas); + setModifyTemplate(false); + setDisabled(true); + setTemplateEnable(data.enable); + message.success(formatMessage({ id: 'operation.success' })); if (name !== 'TemplatePreview') - history.push({ pathname: `/ws/${ws_id}/job/templates`, state: state || {} }) - } - else - requestCodeMessage(code, msg) - setFetching(false) - } + history.push({ pathname: `/ws/${ws_id}/job/templates`, state: state || {} }); + } else requestCodeMessage(code, msg); + setFetching(false); + }; const handleCancelTemplate = (key: any) => { - notification.close(key) + notification.close(key); // setFetching(false) // history.push({ pathname: `/ws/${ws_id}/job/templates`, state: state || {} }) - } + }; const handleSaveTemplateModify = async () => { - if (fetching) return - setFetching(true) - const data = await transformDate() + if (fetching) return; + setFetching(true); + const data = await transformDate(); if (isMonitorEmpty(data)) { - setFetching(false) - return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })) + setFetching(false); + return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })); } if (!data.test_config) { - setFetching(false) - message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })) - return + setFetching(false); + message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })); + return; } if (!data.baseline) { - data.baseline = null + data.baseline = null; } if (!data.baseline_job_id) { - data.baseline_job_id = null + data.baseline_job_id = null; } if (!data.cleanup_info) { - data.cleanup_info = "" + data.cleanup_info = ''; } - if (Object.prototype.toString.call(data) === "[object Object]") { + if (Object.prototype.toString.call(data) === '[object Object]') { const key = `open${Date.now()}`; const btn = ( - ); - const res = await queryCheckJobTemplate({ template_id: jt_id }) + const res = await queryCheckJobTemplate({ template_id: jt_id }); if (res.code === 200 && res.data.length > 0) { notification.warning({ duration: null, @@ -787,202 +888,269 @@ const TestJob: React.FC = (props) => { key, }); } else { - handleTemplateEditFunction(data) + handleTemplateEditFunction(data); } } - setFetching(false) - } + setFetching(false); + }; const handleSaveCreateSubmit = async () => { - if (newSaveLoading) return - setNewSaveLoading(true) + if (newSaveLoading) return; + setNewSaveLoading(true); - const data = await transformDate() + const data = await transformDate(); if (isMonitorEmpty(data)) { - setNewSaveLoading(false) - return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })) + setNewSaveLoading(false); + return message.warning(formatMessage({ id: 'ws.test.job.machine.cannot.be.empty' })); } if (!data.test_config) { - setNewSaveLoading(false) - return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })) + setNewSaveLoading(false); + return message.warning(formatMessage({ id: 'ws.test.job.suite.cannot.be.empty' })); } if (!data.baseline) { - data.baseline = null + data.baseline = null; } - const $test_config = handleServerChannel(data.test_config) + const $test_config = handleServerChannel(data.test_config); const { code, msg } = await updateTestTemplate({ template_id: templateDatas.id, workspace: ws_id, job_type: detail.id, ...data, - test_config: $test_config - }) + test_config: $test_config, + }); if (code === 200) { - message.success(formatMessage({ id: 'request.save.success' })) - history.push(`/ws/${ws_id}/test_job/${detail.id}?template_id=${templateDatas.id}`) - } - else requestCodeMessage(code, msg) - setNewSaveLoading(false) - } + message.success(formatMessage({ id: 'request.save.success' })); + history.push(`/ws/${ws_id}/test_job/${detail.id}?template_id=${templateDatas.id}`); + } else requestCodeMessage(code, msg); + setNewSaveLoading(false); + }; const handleTemplatePopoverChange = (v: any) => { - setTemplateBtnVisible(v) - } + setTemplateBtnVisible(v); + }; const modalProps = { - disabled, ws_id, + disabled, + ws_id, template: templateDatas, test_type: detail?.test_type, business_type: detail?.business_type || '', server_provider: detail?.server_type, server_type: detail?.server_type, - } + }; const bodyPaddding = useMemo(() => { - if (layoutWidth >= 1240) return (1240 - 1000) / 2 - return 20 - }, [layoutWidth]) + if (layoutWidth >= 1240) return (1240 - 1000) / 2; + return 20; + }, [layoutWidth]); - const bodySize = useSize(bodyRef) + const bodySize = useSize(bodyRef); const layoutCss = useMemo(() => { - const defaultCss = { minHeight: layoutHeight, overflow: 'auto', background: "#f5f5f5" } - return hasNav ? { ...defaultCss, paddingTop: 50 } : defaultCss - }, [layoutHeight, hasNav]) + const defaultCss = { minHeight: layoutHeight, overflow: 'auto', background: '#f5f5f5' }; + return hasNav ? { ...defaultCss, paddingTop: 50 } : defaultCss; + }, [layoutHeight, hasNav]); const handleTestYaml = async () => { - const parmas = { yaml_data: jobInfo, workspace: ws_id } - const { code, msg } = await testYaml(parmas) - if (code !== 200) - requestCodeMessage(code, msg) - else - message.success(formatMessage({ id: 'operation.success' })) - } + const parmas = { yaml_data: jobInfo, workspace: ws_id }; + const { code, msg } = await testYaml(parmas); + if (code !== 200) requestCodeMessage(code, msg); + else message.success(formatMessage({ id: 'operation.success' })); + }; const handleClose = () => { - setIsYamlFormat(false) - setJobInfo('') - } + setIsYamlFormat(false); + setJobInfo(''); + }; const fakeClick = (obj: any) => { - const ev = document.createEvent("MouseEvents"); - ev.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + const ev = document.createEvent('MouseEvents'); + ev.initMouseEvent( + 'click', + true, + false, + window, + 0, + 0, + 0, + 0, + 0, + false, + false, + false, + false, + 0, + null, + ); obj.dispatchEvent(ev); - } + }; const exportRaw = ($name: string, data: any) => { const urlObject = window.URL || window.webkitURL || window; const export_blob = new Blob([data]); - const save_link: any = document.createElementNS("http://www.w3.org/1999/xhtml", "a") + const save_link: any = document.createElementNS('http://www.w3.org/1999/xhtml', 'a'); save_link.href = urlObject.createObjectURL(export_blob); save_link.download = $name; fakeClick(save_link); - } + }; const handleDownload = async () => { // const parmas = { type: 'yaml2json', yaml_data: jobInfo } // let { code, data } = await formatYamlToJson(parmas) - const fileName = 'job_' + (+new Date()) + const fileName = 'job_' + +new Date(); // if (code === 200 && _.get(data, 'name')) fileName = _.get(data, 'name') exportRaw(`${fileName}.yaml`, jobInfo); - } + }; const queryProjectId = (id: any) => { - setProjectId(id) - } + setProjectId(id); + }; const renderButton = ( <> - {templateEnabel && } - + {templateEnabel && ( + + )} + - ) + ); return (
+ {/** 用户未登录提示 */} - {(!loading && !authList?.user_id) ? -
: null} + {!loading && !authList?.user_id ? ( +
+
+ +
+
+ ) : null} - { - hasNav && + {hasNav && (
{ - if (name === 'JobTypePreview') history.push(`/ws/${ws_id}/job/types`) - else { - /* @ts-ignore */ - history.push(access.IsWsSetting() ? { pathname: `/ws/${ws_id}/job/templates`, state: state || {} } : "./") - } + style={{ + height: 50, + width: 50, + cursor: 'pointer', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }} + onClick={() => { + if (name === 'JobTypePreview') + history.push(`/ws/${ws_id}/job/types`); + else { + /* @ts-ignore */ + history.push( + access.IsWsSetting() + ? { + pathname: `/ws/${ws_id}/job/templates`, + state: state || {}, + } + : './', + ); } - } + }} >
- {name === 'JobTypePreview' && } - {name === 'TemplatePreview' && } - {name === 'TemplateEdit' && } + {name === 'JobTypePreview' && ( + + + + )} + {name === 'TemplatePreview' && ( + + + + )} + {name === 'TemplateEdit' && ( + + + + )}
- { - name === 'TemplatePreview' && + {name === 'TemplatePreview' && ( - { - modifyTemplate && + {modifyTemplate && ( <> {renderButton} - } - { - !modifyTemplate && + )} + {!modifyTemplate && ( <> - - AccessTootip()}>} + + AccessTootip()}> + + + } > - + - } + )} - } - { - name === 'TemplateEdit' && - - {renderButton} - - } + )} + {name === 'TemplateEdit' && {renderButton}}
- } + )} -
-
- - +
+ +
{detail.name} - { - (name === 'TestJob' && !loading) && + {name === 'TestJob' && !loading && ( = (props) => { autoComplete="off" prefix={} className={styles.job_search_inp} - placeholder={formatMessage({ id: 'ws.test.job.search.placeholder.template' })} + placeholder={formatMessage({ + id: 'ws.test.job.search.placeholder.template', + })} onChange={handleChangeTemplateName} /> } content={ - - { - Array.isArray(templateList) && templateList.length > 0 && - <> - { - templateList.map( + + {Array.isArray(templateList) && + templateList.length > 0 && ( + <> + {templateList.map( (item: any) => ( -
{ - if (!item.job_type) return message.error(formatMessage({ id: 'ws.test.job.please.delete' })) - history.push(`/ws/${ws_id}/test_job/${item.job_type_id}?template_id=${item.id}`) - setTemplateBtnVisible(false) + if ( + !item.job_type + ) + return message.error( + formatMessage( + { + id: 'ws.test.job.please.delete', + }, + ), + ); + history.push( + `/ws/${ws_id}/test_job/${item.job_type_id}?template_id=${item.id}`, + ); + setTemplateBtnVisible( + false, + ); }} > - +
- ) - ) - } - - } - { - Array.isArray(templateList) && templateList.length === 0 && -
- -
- } + ), + )} + + )} + {Array.isArray(templateList) && + templateList.length === 0 && ( +
+ +
+ )}
} > - + - } + )}
- { - detail.server_type && - - } + {detail.server_type && ( + + )} - {detail.test_type === 'business' ? - : - detail.test_type && } + {detail.test_type === 'business' ? ( + + ) : ( + detail.test_type && ( + + ) + )}
{detail.description}
-
+
- +
- { - (name === 'TestJob' || name === 'TestExport') && + {(name === 'TestJob' || name === 'TestExport') && (
- {isYamlFormat ? - : + {isYamlFormat ? ( + + ) : ( - } + )}
- } -
+ )} +
- {name === 'TestJob' && } - {name === 'TestExport' && } - { - (name === 'TemplatePreview' || name === 'TemplateEdit' || name === 'TestTemplate') && - - } + {name === 'TestJob' && ( + + + + )} + {name === 'TestExport' && ( + + + + )} + {(name === 'TemplatePreview' || + name === 'TemplateEdit' || + name === 'TestTemplate') && ( + + + + )} - - { - (name === 'TemplatePreview' || name === 'TemplateEdit' || name === 'TestTemplate') && + {(name === 'TemplatePreview' || + name === 'TemplateEdit' || + name === 'TestTemplate') && (
- + + +
- } - { - JSON.stringify(items.basic) !== '{}' && + )} + {JSON.stringify(items.basic) !== '{}' && (
- + + +
= (props) => { {...modalProps} />
- } - { - JSON.stringify(items.env) !== '{}' && + )} + {JSON.stringify(items.env) !== '{}' && (
- + + +
= (props) => { {...modalProps} />
- } - { - detail.test_type && + )} + {detail.test_type && (
- + + + *  +
@@ -1138,7 +1394,9 @@ const TestJob: React.FC = (props) => { setTest_config(data)} + handleData={(data: any) => + setTest_config(data) + } contrl={items.suite} onRef={suiteTable} setPageLoading={setLoading} @@ -1146,12 +1404,13 @@ const TestJob: React.FC = (props) => { />
- } - { - JSON.stringify(items.more) !== '{}' && + )} + {JSON.stringify(items.more) !== '{}' && (
- + + +
= (props) => { callback={setTimeTagList} />
- } + )} - { - isYamlFormat && -
-
- + {isYamlFormat && ( +
+
+ - handleCopyText(jobInfo.replace('---', ''))} style={{ marginLeft: 10 }}> + + handleCopyText(jobInfo.replace('---', '')) + } + style={{ marginLeft: 10 }} + > - + - + setJobInfo( - value - )} + onChange={(value: any) => setJobInfo(value)} /> - } + )} - { - (name === 'TestJob' || name === 'TestTemplate' || name === 'TestExport') && + {(name === 'TestJob' || name === 'TestTemplate' || name === 'TestExport') && ( = (props) => { okText={} cancelText={} > - + {/* @@ -1216,42 +1493,64 @@ const TestJob: React.FC = (props) => { */} - + {/** 有时间的系统标签时,二次弹框确认; */} - {timeTagList.length ? + {timeTagList.length ? ( <> } cancelText={} placement="topRight" > - + } cancelText={} placement="topRight" > - + - : + ) : ( <> - - + + - } + )} - } + )} - - ) -} + + ); +}; -export default TestJob \ No newline at end of file +export default TestJob; diff --git a/src/pages/WorkSpace/TestPlan/PlanForm/index.tsx b/src/pages/WorkSpace/TestPlan/PlanForm/index.tsx index 9bd5bb9b691fc087535c4ecf5ce12a9844998af7..93c350ca047f882df2b6f0b633fe8eab5c30499c 100644 --- a/src/pages/WorkSpace/TestPlan/PlanForm/index.tsx +++ b/src/pages/WorkSpace/TestPlan/PlanForm/index.tsx @@ -1,145 +1,173 @@ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-unused-expressions */ -import { useState, useRef, useEffect } from 'react' -import { Breadcrumb, Row, Steps, Button, Result, Col, Space, message, Spin, Badge } from 'antd' +import { useState, useRef, useEffect } from 'react'; +import { Breadcrumb, Row, Steps, Button, Result, Col, Space, message, Spin, Badge } from 'antd'; -import BasicSetting from './components/BasicSetting' -import TestSetting from './components/TestSetting' -import ReportSetting from './components/ReportSetting' -import TouchSetting from './components/TouchSetting' +import BasicSetting from './components/BasicSetting'; +import TestSetting from './components/TestSetting'; +import ReportSetting from './components/ReportSetting'; +import TouchSetting from './components/TouchSetting'; -import { useClientSize, writeDocumentTitle } from '@/utils/hooks' -import { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons' +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; +import { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons'; import { - CreateContainer, ContainerBody, ContainerBreadcrumb, LeftWrapper, - RightBody, RightNav, RightWrapper, SuccessDescriptionContainer -} from './styles' -import { history, useIntl, FormattedMessage, getLocale, Access, useAccess, useLocation } from 'umi' -import { runningTestPlan, creatTestPlan, queryTestPlanDetails, updateTestPlan } from '@/pages/WorkSpace/TestPlan/services' -import styles from './index.less' -import { requestCodeMessage, AccessTootip } from '@/utils/utils' -import _ from 'lodash' -import { stringify } from 'querystring' + CreateContainer, + ContainerBody, + ContainerBreadcrumb, + LeftWrapper, + RightBody, + RightNav, + RightWrapper, + SuccessDescriptionContainer, +} from './styles'; +import { history, useIntl, FormattedMessage, getLocale, Access, useAccess, useLocation } from 'umi'; +import { + runningTestPlan, + creatTestPlan, + queryTestPlanDetails, + updateTestPlan, +} from '@/pages/WorkSpace/TestPlan/services'; +import styles from './index.less'; +import { requestCodeMessage, AccessTootip } from '@/utils/utils'; +import _ from 'lodash'; +import { stringify } from 'querystring'; -/** +/** * 计划管理/新建计划(新) * */ const TestPlan = (props: any) => { - const { formatMessage } = useIntl() - const enLocale = getLocale() === 'en-US' + const { formatMessage } = useIntl(); + const enLocale = getLocale() === 'en-US'; const access = useAccess(); - const { height: layoutHeight } = useClientSize() - const { state } = useLocation() as any + const { height: layoutHeight } = useClientSize(); + const { state } = useLocation() as any; - const { route } = props + const { route } = props; // console.log('route.name:', route.name) - writeDocumentTitle(`Workspace.TestPlan.${route.name}`) - const { ws_id, plan_id } = props.match.params - const [current, setCurrent] = useState(0) + const { ws_id, plan_id } = props.match.params; + const [current, setCurrent] = useState(0); - const [loading, setLoading] = useState(route.name !== 'Create') - const [isFormStep, setIsFormStep] = useState(true) //填表阶段 - const [pedding, setPedding] = useState(false) + const [loading, setLoading] = useState(route.name !== 'Create'); + const [isFormStep, setIsFormStep] = useState(true); //填表阶段 + const [pedding, setPedding] = useState(false); - const basicSettingRef: any = useRef() - const touchSettingRef: any = useRef() - const reportSettingRef: any = useRef() + const basicSettingRef: any = useRef(); + const touchSettingRef: any = useRef(); + const reportSettingRef: any = useRef(); - const [dataSource, setDataSource] = useState({ basic: {}, pipline: {}, touch: {} }) - const [template, setTemplate] = useState(null) - const [successData, setSuccessData] = useState(null) + const [dataSource, setDataSource] = useState({ basic: {}, pipline: {}, touch: {} }); + const [template, setTemplate] = useState(null); + const [successData, setSuccessData] = useState(null); // 查询 const getEditTestPlanData = async () => { - const { data, code } = await queryTestPlanDetails({ ws_id, id: plan_id }) + const { data, code } = await queryTestPlanDetails({ ws_id, id: plan_id }); if (code === 200) { - setTemplate(data) + setTemplate(data); // setSuccessData( data ) - setLoading(false) + setLoading(false); } - } + }; useEffect(() => { - const { name } = route - if (name !== 'Create') - getEditTestPlanData() - }, [route]) + const { name } = route; + if (name !== 'Create') getEditTestPlanData(); + }, [route]); const handleStepPre = () => { - setCurrent(current - 1) - } + setCurrent(current - 1); + }; // 校验数据 const checkDataSource = () => { return new Promise((resolve: any, reject: any) => { - const pipline = dataSource.pipline - const { env_prep = {}, test_config = [] } = pipline || {} - const localStr = formatMessage({ id: 'plan.cannot.be.empty' }) + const pipline = dataSource.pipline; + const { env_prep = {}, test_config = [] } = pipline || {}; + const localStr = formatMessage({ id: 'plan.cannot.be.empty' }); if (env_prep.machine_info && !env_prep.machine_info.length) { - message.error(`${env_prep.name}${localStr}`) - reject() + message.error(`${env_prep.name}${localStr}`); + reject(); } if (test_config.length) { test_config.forEach((item: any) => { - const { name, template: $template = [] } = item + const { name, template: $template = [] } = item; if (!$template.length) { - message.error(`${name}${localStr}`) - reject() + message.error(`${name}${localStr}`); + reject(); } - }) + }); } else { - message.error(`${env_prep.name}${localStr}`) - reject() + message.error(`${env_prep.name}${localStr}`); + reject(); } - resolve() - }) - } + resolve(); + }); + }; const hanleStepNext = async (stepKey: any) => { - if (pedding) return - setPedding(true) + if (pedding) return; + setPedding(true); try { if (current === 0) { - const basicFormValue = await basicSettingRef.current.validate() - const { headers, devel, hotfix_install, scripts, kernel, build_config, build_machine, code_branch, code_repo, - commit_id, compile_branch, cpu_arch, product_name, ...formValue - } = basicFormValue + const basicFormValue = await basicSettingRef.current.validate(); + const { + headers, + devel, + hotfix_install, + scripts, + kernel, + build_config, + build_machine, + code_branch, + code_repo, + commit_id, + compile_branch, + cpu_arch, + product_name, + ...formValue + } = basicFormValue; setDataSource({ ...dataSource, basic: { ...formValue, kernel_info: { headers, devel, hotfix_install, kernel, scripts }, build_pkg_info: { - build_config, build_machine, code_branch, code_repo, - commit_id, compile_branch, cpu_arch, name: product_name - } - } - }) + build_config, + build_machine, + code_branch, + code_repo, + commit_id, + compile_branch, + cpu_arch, + name: product_name, + }, + }, + }); } if (current === 1) { // 校验各阶段都添加了模版,才能跳到下一步 - await checkDataSource() + await checkDataSource(); } else if (current === 2) { // 校验表单必填项填写了没有 - await reportSettingRef.current.validate() + await reportSettingRef.current.validate(); } // 编辑时通过校验后,可随意跳步骤。 - (route.name === 'Edit' && stepKey !== 'NextStep') ? setCurrent(stepKey) : setCurrent(current + 1) - } - catch (err) { - console.log(err, 8888) + route.name === 'Edit' && stepKey !== 'NextStep' + ? setCurrent(stepKey) + : setCurrent(current + 1); + } catch (err) { + console.log(err, 8888); } - setPedding(false) - } + setPedding(false); + }; const handleStepChange = async (key: number) => { if (key > current) { - await hanleStepNext(key) - } - else setCurrent(key) - } + await hanleStepNext(key); + } else setCurrent(key); + }; const hanldePrepDataChange = ({ test_config, env_prep }: any) => { setDataSource({ @@ -147,310 +175,427 @@ const TestPlan = (props: any) => { pipline: { env_prep: env_prep.visible ? env_prep : {}, test_config: test_config.map((i: any) => { - return { ...i, template: i.template.map((t: any) => t.id) } - }) + return { ...i, template: i.template.map((t: any) => t.id) }; + }), }, - }) + }); // 将步骤2产生的数据,传给步骤3中 reportSettingRef.current.refreshData(test_config); - } + }; const trhowErrorMsg = (err: any) => { if (err) { - const { errorFields } = err + const { errorFields } = err; if (errorFields && _.isArray(errorFields) && errorFields.length > 0) { - const { errors } = errorFields[0] - message.error(errors.toString()) + const { errors } = errorFields[0]; + message.error(errors.toString()); } } - } + }; const formatterData = async () => { - const basic = await basicSettingRef.current.validate() - const report = await reportSettingRef.current.validate() - const touch = await touchSettingRef.current.validate() - const pipline = dataSource.pipline + const basic = await basicSettingRef.current.validate(); + const report = await reportSettingRef.current.validate(); + const touch = await touchSettingRef.current.validate(); + const pipline = dataSource.pipline; const { - headers, devel, hotfix_install, scripts, kernel, build_config, - build_machine, code_branch, code_repo, kernel_packages, - commit_id, compile_branch, cpu_arch, product_name, ...formValue + headers, + devel, + hotfix_install, + scripts, + kernel, + build_config, + build_machine, + code_branch, + code_repo, + kernel_packages, + commit_id, + compile_branch, + cpu_arch, + product_name, + ...formValue } = basic; const { base_group_job, base_group_stage, ...reportOther } = report; // *根据“分组方式”区分“选择基准组”字段的表单值的来源。 - const { group_method } = report - let reportValues = { ...reportOther } - if (group_method === "job") { - const [stage_id, base_group] = base_group_job - reportValues = Object.assign(reportValues, { stage_id, base_group }) + const { group_method } = report; + let reportValues = { ...reportOther }; + if (group_method === 'job') { + const [stage_id, base_group] = base_group_job; + reportValues = Object.assign(reportValues, { stage_id, base_group }); } - if (group_method === "stage") - reportValues = Object.assign(reportValues, { base_group: base_group_stage }) + if (group_method === 'stage') + reportValues = Object.assign(reportValues, { base_group: base_group_stage }); return { ...formValue, kernel_info: { headers, devel, hotfix_install, scripts, kernel, kernel_packages }, build_pkg_info: { - build_config, build_machine, code_branch, code_repo, - commit_id, compile_branch, cpu_arch, name: product_name + build_config, + build_machine, + code_branch, + code_repo, + commit_id, + compile_branch, + cpu_arch, + name: product_name, }, ...reportValues, ...touch, cron_info: touch?.cron_info ?? dataSource?.cron_info, - ...pipline - } - } + ...pipline, + }; + }; const hanldePushTestPlan = async () => { - if (pedding) return - setPedding(true) - const formData = await formatterData() - const { code, msg, data } = await creatTestPlan({ ...formData, ws_id }) + if (pedding) return; + setPedding(true); + const formData = await formatterData(); + const { code, msg, data } = await creatTestPlan({ ...formData, ws_id }); if (code !== 200) { - requestCodeMessage(code, msg) - setPedding(false) - return + requestCodeMessage(code, msg); + setPedding(false); + return; } - setSuccessData(data) - setIsFormStep(false) - } + setSuccessData(data); + setIsFormStep(false); + }; const handleTestPlanOption = async (is_save: boolean = false) => { - if (pedding) return - setPedding(true) + if (pedding) return; + setPedding(true); try { - const formData = await formatterData() - const { code, msg } = await runningTestPlan({ ...formData, is_save, ws_id, plan_id }) + const formData = await formatterData(); + const { code, msg } = await runningTestPlan({ ...formData, is_save, ws_id, plan_id }); if (code !== 200) { - requestCodeMessage(code, msg) - setPedding(false) + requestCodeMessage(code, msg); + setPedding(false); return; } - history.push(`/ws/${ws_id}/test_plan?${stringify(state)}`) - } - catch (err) { - console.log(err) - setPedding(false) - trhowErrorMsg(err) + history.push(`/ws/${ws_id}/test_plan?${stringify(state)}`); + } catch (err) { + console.log(err); + setPedding(false); + trhowErrorMsg(err); } - } + }; - const handleBackPlanManage = () => history.push(`/ws/${ws_id}/test_plan?${stringify(state)}`) + const handleBackPlanManage = () => history.push(`/ws/${ws_id}/test_plan?${stringify(state)}`); const hanldeUpdatePlan = async () => { - if (pedding) return - setPedding(true) + if (pedding) return; + setPedding(true); try { - const formData = await formatterData() - const { code, msg } = await updateTestPlan({ ...formData, ws_id, plan_id }) + const formData = await formatterData(); + const { code, msg } = await updateTestPlan({ ...formData, ws_id, plan_id }); if (code !== 200) { - requestCodeMessage(code, msg) - setPedding(false) + requestCodeMessage(code, msg); + setPedding(false); return; } - history.push(`/ws/${ws_id}/test_plan?${stringify(state)}`) + history.push(`/ws/${ws_id}/test_plan?${stringify(state)}`); + } catch (err) { + console.log(err); + setPedding(false); + trhowErrorMsg(err); } - catch (err) { - console.log(err) - setPedding(false) - trhowErrorMsg(err) - } - } - + }; return ( - + + - + - + + + - + - { - isFormStep ? - <> - {/* 左侧 步骤 === 进度显示部分 ==== onChange={setCurrent} */} - - - } key={0} className={styles[['Run', 'Edit'].includes(route.name) ? 'stepsWrapper_1' : 'stepsWrapper']} /> - } key={1} className={styles[['Run', 'Edit'].includes(route.name) ? 'stepsWrapper_2' : 'stepsWrapper']} /> - } key={2} /> - } key={3} /> - - - - {/* 右侧 导航 === 操作部分 ==== */} - - -
- { - current === 0 ? null : ( - <> - - - - ) - } -
- - { - route.name === 'Run' && - <> - - - AccessTootip()} type="primary">} + {isFormStep ? ( + <> + {/* 左侧 步骤 === 进度显示部分 ==== onChange={setCurrent} */} + + + } + key={0} + className={ + styles[ + ['Run', 'Edit'].includes(route.name) + ? 'stepsWrapper_1' + : 'stepsWrapper' + ] + } + /> + } + key={1} + className={ + styles[ + ['Run', 'Edit'].includes(route.name) + ? 'stepsWrapper_2' + : 'stepsWrapper' + ] + } + /> + } + key={2} + /> + } + key={3} + /> + + + + {/* 右侧 导航 === 操作部分 ==== */} + + +
+ {current === 0 ? null : ( + <> + + + + + + )} +
+ + {route.name === 'Run' && ( + <> + + + AccessTootip()} + type="primary" + > + + + } + > + - + + - - } - { - route.name === 'Edit' && - <> - - - AccessTootip()}>} + + + )} + {route.name === 'Edit' && ( + <> + + + AccessTootip()} + > + + + } + > + - + + - - } -
hanleStepNext('NextStep')}> - { - current < 3 && - <> - - - - } -
- { - current === 3 && +
+ + )} +
hanleStepNext('NextStep')} + > + {current < 3 && ( <> - { - route.name === 'Create' && - - } + + + + - } - - - - {/* 右侧 === 表单部分 === */} - - - + )} +
+ {current === 3 && ( + <> + {route.name === 'Create' && ( + + )} + + )} +
+
+
+ {/* 右侧 === 表单部分 === */} + + + - - - -
- - : -
- {/* 计划成功部分 */} - } - subTitle={} - extra={[ - - ]} - /> - -
- - - { - successData?.name && - - - - {successData?.name} - - - } - { - successData?.cron_info && - - - - {successData?.cron_info} - - - } - { - successData?.enable && - - - - - { - successData?.enable ? - } /> : - } /> - } - - - - } - { - successData?.next_time && - - - - {successData?.next_time} - - - } - - - - - } + + + + + + ) : ( +
+ {/* 计划成功部分 */} + } + subTitle={} + extra={[ + , + ]} + /> + +
+ + + + + + + {successData?.name && ( + + + + : + + {successData?.name} + + + )} + {successData?.cron_info && ( + + + + + : + + {successData?.cron_info} + + + )} + {successData?.enable && ( + + + + + : + + + {successData?.enable ? ( + + } + /> + ) : ( + + } + /> + )} + + + + )} + {successData?.next_time && ( + + + + : + + {successData?.next_time} + + + )} + + + + + )} - ) -} + ); +}; -export default TestPlan +export default TestPlan; diff --git a/src/pages/WorkSpace/TestPlan/PlanView/ViewDetail/index.tsx b/src/pages/WorkSpace/TestPlan/PlanView/ViewDetail/index.tsx index 46a53d700cbfbcb4425a1c69d479deee8418f06e..36c4f5f4aa9e11a074f7e3a63c98375040a1e3b2 100644 --- a/src/pages/WorkSpace/TestPlan/PlanView/ViewDetail/index.tsx +++ b/src/pages/WorkSpace/TestPlan/PlanView/ViewDetail/index.tsx @@ -1,154 +1,161 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useState, useEffect, useCallback, useRef } from 'react' -import { Breadcrumb, Typography, Row, Tag, Button, Spin, Popover } from 'antd' +import { useState, useEffect, useCallback, useRef } from 'react'; +import { Breadcrumb, Typography, Row, Tag, Button, Spin, Popover } from 'antd'; import { QuestionCircleOutlined } from '@ant-design/icons'; -import styled from 'styled-components' -import { FormattedMessage, history } from 'umi' -import { useClientSize, writeDocumentTitle } from '@/utils/hooks' -import BasicInfoDrawer from './components/BasicInfoDrawer' -import PipLine from './components/PipLine' -import ConsoleDrawer from './components/ConsoleDrawer' -import { queryPlanResultDetail } from './services' -import { requestCodeMessage } from '@/utils/utils' -import ViewReport from '@/pages/WorkSpace/TestResult/CompareBar/ViewReport' +import styled from 'styled-components'; +import { FormattedMessage, history } from 'umi'; +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; +import BasicInfoDrawer from './components/BasicInfoDrawer'; +import PipLine from './components/PipLine'; +import ConsoleDrawer from './components/ConsoleDrawer'; +import { queryPlanResultDetail } from './services'; +import { requestCodeMessage } from '@/utils/utils'; +import ViewReport from '@/pages/WorkSpace/TestResult/CompareBar/ViewReport'; interface ContainerProps { width: number; height: number; } const SummaryContainer = styled.div` - width:${props => props.width}px; - height:${props => props.height}px; + width: ${(props) => props.width}px; + height: ${(props) => props.height}px; /* overflow:auto; */ background: #f5f5f5; -` +`; const SummaryHeader = styled.div` height: 150px; - width:100%; - background:#fff; + width: 100%; + background: #fff; padding: 20px 20px 12px; -` +`; const SummaryBody = styled.div` margin-top: 10px; width: 100%; - height: calc( 100% - 100px - 20px - 40px); - background:#fff; + height: calc(100% - 100px - 20px - 40px); + background: #fff; overflow: auto; padding: 29px 20px 20px 20px; display: flex; -` +`; const LinkSpan = styled.span` - cursor:pointer; + cursor: pointer; &:hover { - color : #1890FF; + color: #1890ff; } -` +`; const ViewDetail = (props: any) => { - const { ws_id, plan_id } = props.match.params - const { route } = props - - writeDocumentTitle(`Workspace.TestPlan.${route.name}`) + const { ws_id, plan_id } = props.match.params; + const { route } = props; - const { height: layoutHeight, width: layoutWidth } = useClientSize() + const { height: layoutHeight, width: layoutWidth } = useClientSize(); - const [loading, setLoading] = useState(false) - const [dataSet, setDataSet] = useState({}) + const [loading, setLoading] = useState(false); + const [dataSet, setDataSet] = useState({}); // - const basicInfoRef: any = useRef(null) - const consoleRef: any = useRef(null) - + const basicInfoRef: any = useRef(null); + const consoleRef: any = useRef(null); // 1.查询step数据 const getDetailData = async (query: any) => { try { - setLoading(true) - const res = await queryPlanResultDetail(query) || {} - const { data = {} } = res + setLoading(true); + const res = (await queryPlanResultDetail(query)) || {}; + const { data = {} } = res; // mock // setDataSet(mockData2.data) if (res.code === 200) { - setDataSet(data) + setDataSet(data); } else { - requestCodeMessage(res.code, res.msg) + requestCodeMessage(res.code, res.msg); } - setLoading(false) + setLoading(false); } catch (e) { - setLoading(false) + setLoading(false); } - } + }; useEffect(() => { if (plan_id) { - getDetailData({ ws_id, plan_instance_id: plan_id }) + getDetailData({ ws_id, plan_instance_id: plan_id }); } - }, [plan_id]) + }, [plan_id]); const TagInfo = ({ data }: any) => { - const { state, start_time, end_time, error_info } = data - const rowStyle = { display: 'flex', alignItems: 'center' } - const normalStyle = { background: '#F2F4F6', border: '1px solid #F2F4F6' } + const { state, start_time, end_time, error_info } = data; + const rowStyle = { display: 'flex', alignItems: 'center' }; + const normalStyle = { background: '#F2F4F6', border: '1px solid #F2F4F6' }; return (
{state === 'pending' && ( - Pending + + Pending + )} {state === 'running' && ( - Running + + Running + )} {state === 'fail' && (
- Fail - - + + Fail + + +
)} {state === 'success' && ( - Complete + + Complete + )} {!!start_time && ( - :{start_time} + + :{start_time} + )} {!!end_time && ( - :{end_time} + + :{end_time} + )}
- ) - } - + ); + }; // 基础配置 const BasicSettingClick = useCallback((data) => { - basicInfoRef.current?.show('add', data) - }, []) + basicInfoRef.current?.show('add', data); + }, []); // 回调函数 function prepareCallback(info: any) { - const { data } = info - consoleRef.current?.show('show', data) + const { data } = info; + consoleRef.current?.show('show', data); } function basicInfoCallback(info: any) { - const { data, editFn } = info - editFn({ ws_id, plan_instance_id: plan_id, note: data }) + const { data, editFn } = info; + editFn({ ws_id, plan_instance_id: plan_id, note: data }); } return ( - + + - history.push(`/ws/${ws_id}/test_plan/view`)}> + history.push(`/ws/${ws_id}/test_plan/view`)} + > @@ -157,12 +164,25 @@ const ViewDetail = (props: any) => { - {dataSet.name} + + {dataSet.name} +
- - + +
@@ -174,7 +194,7 @@ const ViewDetail = (props: any) => {
- ) -} + ); +}; -export default ViewDetail +export default ViewDetail; diff --git a/src/pages/WorkSpace/TestPlan/PlanView/ViewSummary/index.tsx b/src/pages/WorkSpace/TestPlan/PlanView/ViewSummary/index.tsx index e641f106c85d0e4793e246d1b275dcab11341f4f..36de7d2a996031d6e86f4a6c8c70fb046ac1b398 100644 --- a/src/pages/WorkSpace/TestPlan/PlanView/ViewSummary/index.tsx +++ b/src/pages/WorkSpace/TestPlan/PlanView/ViewSummary/index.tsx @@ -1,73 +1,71 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { Breadcrumb, Row, Typography } from 'antd' -import { useState, useEffect } from 'react' -import styled from 'styled-components' -import { FormattedMessage, history } from 'umi' -import { RenderDataRow } from '../components' -import ViewTable from '../components/ViewTable' -import { useClientSize, writeDocumentTitle } from '@/utils/hooks' -import { requestCodeMessage } from '@/utils/utils' -import { queryPlanViewList } from '@/pages/WorkSpace/TestPlan/PlanView/services' +import { Breadcrumb, Row, Typography } from 'antd'; +import { useState, useEffect } from 'react'; +import styled from 'styled-components'; +import { FormattedMessage, history } from 'umi'; +import { RenderDataRow } from '../components'; +import ViewTable from '../components/ViewTable'; +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; +import { requestCodeMessage } from '@/utils/utils'; +import { queryPlanViewList } from '@/pages/WorkSpace/TestPlan/PlanView/services'; interface ContainerProps { - width: number - height: number + width: number; + height: number; } const DetailContainer = styled.div` - width:${props => props.width}px; - min-height:${props => props.height}px; - overflow:auto; + width: ${(props) => props.width}px; + min-height: ${(props) => props.height}px; + overflow: auto; background-color: #f5f5f5; - min-height:100%; -` + min-height: 100%; +`; const DetailHeader = styled.div` // height:182px; - width:100%; - background:#fff; - padding:20px; -` + width: 100%; + background: #fff; + padding: 20px; +`; const DescriptionRow = styled(Row)` - margin-top:8px; -` + margin-top: 8px; +`; const DetailBody = styled.div` - background:#fff; - padding:20px; - margin-top:10px; -` + background: #fff; + padding: 20px; + margin-top: 10px; +`; const LinkSpan = styled.span` - cursor:pointer; + cursor: pointer; &:hover { - color : #1890FF; + color: #1890ff; } -` +`; const ViewDetail = (props: any) => { - const { route, match } = props - const { plan_id, ws_id } = match.params - const [data, setData] = useState({}) - writeDocumentTitle(`Workspace.TestPlan.${route.name}`) + const { route, match } = props; + const { plan_id, ws_id } = match.params; + const [data, setData] = useState({}); - const { height: layoutHeight, width: layoutWidth } = useClientSize() + const { height: layoutHeight, width: layoutWidth } = useClientSize(); const queryPlanViewListData = async (param: any = { ws_id, plan_id }) => { - const { data: $data, code, msg } = await queryPlanViewList(param) - if (code === 200) - setData($data) - else - requestCodeMessage(code, msg) - } + const { data: $data, code, msg } = await queryPlanViewList(param); + if (code === 200) setData($data); + else requestCodeMessage(code, msg); + }; useEffect(() => { - queryPlanViewListData() - }, []) + queryPlanViewListData(); + }, []); return ( - + + @@ -81,19 +79,20 @@ const ViewDetail = (props: any) => { - - {data?.name} - + {data?.name} - - {data?.description} - + {data?.description} - - + + - ) -} + ); +}; -export default ViewDetail \ No newline at end of file +export default ViewDetail; diff --git a/src/pages/WorkSpace/TestPlan/PlanView/index.tsx b/src/pages/WorkSpace/TestPlan/PlanView/index.tsx index d103a39d9f95e90106b67a50f8d1416c20e56d22..47816bbfbecae72fa28640ecf3e094a65efd5056 100644 --- a/src/pages/WorkSpace/TestPlan/PlanView/index.tsx +++ b/src/pages/WorkSpace/TestPlan/PlanView/index.tsx @@ -1,34 +1,35 @@ -import { Tabs } from 'antd' -import { FormattedMessage } from 'umi' -import { ViewContent, ViewLayout, TabContainer } from './styled' -import ViewCollapse from './components/ViewCollapse' +import { Tabs } from 'antd'; +import { FormattedMessage } from 'umi'; +import { ViewContent, ViewLayout, TabContainer } from './styled'; +import ViewCollapse from './components/ViewCollapse'; -import { useClientSize, writeDocumentTitle } from '@/utils/hooks' +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; const PalnView = (props: any) => { - const { route, match } = props - const { ws_id } = match.params + const { route, match } = props; + const { ws_id } = match.params; - const { height: layoutHeight, width: layoutWidth } = useClientSize() - - writeDocumentTitle(`Workspace.TestPlan.${route.name}`) + const { height: layoutHeight, width: layoutWidth } = useClientSize(); return ( - + + - }> + } + > - ) -} + ); +}; -export default PalnView \ No newline at end of file +export default PalnView; diff --git a/src/pages/WorkSpace/TestPlan/index.tsx b/src/pages/WorkSpace/TestPlan/index.tsx index 26cc4c259630dc7a93e067622f70ae13bcda6f8b..e5533c679eed5937218aca19b99e36d7c373e921 100644 --- a/src/pages/WorkSpace/TestPlan/index.tsx +++ b/src/pages/WorkSpace/TestPlan/index.tsx @@ -1,188 +1,208 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react'; -import { useClientSize, writeDocumentTitle } from '@/utils/hooks' -import { Space, Tabs, Button, message, Popconfirm, Spin } from 'antd' -import { history, useIntl, FormattedMessage, Access, useAccess, useParams, useLocation } from 'umi' -import CommonPagination from '@/components/CommonPagination' +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; +import { Space, Tabs, Button, message, Popconfirm, Spin } from 'antd'; +import { history, useIntl, FormattedMessage, Access, useAccess, useParams, useLocation } from 'umi'; +import CommonPagination from '@/components/CommonPagination'; -import styled from 'styled-components' -import PlanSettingDrawer from './components/PlanSettingDrawer' -import { queryPlanManageList, deleteTestPlan, copyTestPlan } from './services' -import { getUserFilter, getSearchFilter, getRadioFilter } from '@/components/TableFilters' -import { requestCodeMessage, AccessTootip } from '@/utils/utils' -import { ResizeHooksTable } from '@/utils/table.hooks' -import { tooltipTd } from '../TestResult/Details/components' +import styled from 'styled-components'; +import PlanSettingDrawer from './components/PlanSettingDrawer'; +import { queryPlanManageList, deleteTestPlan, copyTestPlan } from './services'; +import { getUserFilter, getSearchFilter, getRadioFilter } from '@/components/TableFilters'; +import { requestCodeMessage, AccessTootip } from '@/utils/utils'; +import { ResizeHooksTable } from '@/utils/table.hooks'; +import { tooltipTd } from '../TestResult/Details/components'; interface OptionBtnProp { - disabled?: boolean + disabled?: boolean; } const OptButton = styled.span` - color: #1890FF; - margin-right: 10 ; - cursor: pointer ; - ${props => props.disabled && 'opacity: 0.25;'} -` + color: #1890ff; + margin-right: 10; + cursor: pointer; + ${(props) => props.disabled && 'opacity: 0.25;'} +`; const TestPlanManage = (props: any) => { - const { formatMessage } = useIntl() - const { route } = props - const { ws_id } = useParams() as any - const { query } = useLocation() as any - const access = useAccess() - writeDocumentTitle(`Workspace.TestPlan.${route.name}`) - - const { height: layoutHeight } = useClientSize() - const viewSettingRef: any = useRef() - const [data, setData] = useState([]) - const [loading, setLoading] = useState(true) - const [pageParams, setPageParams] = useState(query && JSON.stringify(query) !== "{}" ? query : { ws_id, page_num: 1, page_size: 10 }) + const { formatMessage } = useIntl(); + const { route } = props; + const { ws_id } = useParams() as any; + const { query } = useLocation() as any; + const access = useAccess(); + + const { height: layoutHeight } = useClientSize(); + const viewSettingRef: any = useRef(); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [pageParams, setPageParams] = useState( + query && JSON.stringify(query) !== '{}' ? query : { ws_id, page_num: 1, page_size: 10 }, + ); const queryPlanList = async () => { - const { code, msg, ...rest } = await queryPlanManageList(pageParams) + const { code, msg, ...rest } = await queryPlanManageList(pageParams); if (code === 200) { - setData(rest) + setData(rest); } else { - requestCodeMessage(code, msg) + requestCodeMessage(code, msg); } - setLoading(false) - } + setLoading(false); + }; useEffect(() => { - queryPlanList() - }, [pageParams]) + queryPlanList(); + }, [pageParams]); const handleRun = async (row: any) => { if (!row.enable) return; - history.push(`/ws/${ws_id}/test_plan/${row.id}/run`, pageParams) - } + history.push(`/ws/${ws_id}/test_plan/${row.id}/run`, pageParams); + }; const handleView = (row: any) => { - viewSettingRef.current.show(row) - } + viewSettingRef.current.show(row); + }; const handleCopy = async (row: any) => { - const { code, msg } = await copyTestPlan({ plan_id: row.id, ws_id }) + const { code, msg } = await copyTestPlan({ plan_id: row.id, ws_id }); if (code !== 200) { - requestCodeMessage(code, msg) - return + requestCodeMessage(code, msg); + return; } - message.success(formatMessage({ id: 'request.copy.success' })) - queryPlanList() - } + message.success(formatMessage({ id: 'request.copy.success' })); + queryPlanList(); + }; const handleEdit = (row: any) => { - history.push(`/ws/${ws_id}/test_plan/${row.id}/edit`, pageParams) - } + history.push(`/ws/${ws_id}/test_plan/${row.id}/edit`, pageParams); + }; const handleDelete = async (row: any) => { - const { code, msg } = await deleteTestPlan({ plan_id: row.id, ws_id }) - if (code === 200) queryPlanList() - else requestCodeMessage(code, msg) - } + const { code, msg } = await deleteTestPlan({ plan_id: row.id, ws_id }); + if (code === 200) queryPlanList(); + else requestCodeMessage(code, msg); + }; const columns: any = [ { dataIndex: 'name', title: , - fixed: "left", + fixed: 'left', width: 400, ...getSearchFilter(pageParams, setPageParams, 'name'), - ...tooltipTd("-"), + ...tooltipTd('-'), }, { dataIndex: 'cron_info', title: , width: 120, - ...tooltipTd("-"), + ...tooltipTd('-'), }, { dataIndex: 'enable', title: , width: 120, ellipsis: { - showTitle: false + showTitle: false, }, - render: (_: any) => ( - _ ? : - // : - // - ), + render: (_: any) => + _ ? ( + + ) : ( + + ), + // : + // ...getRadioFilter( pageParams, setPageParams, [ { name: , value: 'True' }, - { name: , value: 'False' } + { name: , value: 'False' }, ], - 'enable' - ) + 'enable', + ), }, { dataIndex: 'creator_name', title: , width: 120, - ...tooltipTd("-"), - ...getUserFilter(pageParams, setPageParams, 'creator_name') + ...tooltipTd('-'), + ...getUserFilter(pageParams, setPageParams, 'creator_name'), }, { dataIndex: 'gmt_created', title: , width: 170, - ...tooltipTd("-"), + ...tooltipTd('-'), }, { dataIndex: 'gmt_modified', title: , - ...tooltipTd("-"), + ...tooltipTd('-'), width: 170, }, { title: , - fixed: "right", - key: "operation", + fixed: 'right', + key: 'operation', width: 220, render: (row: any, record: any) => ( - handleRun(row)}> - handleView(row)}> - handleCopy(row)}> + handleRun(row)}> + + + handleView(row)}> + + + handleCopy(row)}> + + - AccessTootip()}> - AccessTootip()}> + AccessTootip()}> + + + AccessTootip()}> + + } > - handleEdit(row)}> - } + handleEdit(row)}> + + + } onConfirm={() => handleDelete(row)} okText={} cancelText={} > - + + + - ) - } - ] + ), + }, + ]; return ( - + +
{ } > - } > + } + >
record.id} + rowKey={(record) => record.id} size={'small'} pagination={false} /> { - const params = { ...pageParams, page_num, page_size } - setPageParams(params) - } - } + onPageChange={(page_num, page_size) => { + const params = { ...pageParams, page_num, page_size }; + setPageParams(params); + }} currentPage={data.page_num} total={data.total} /> @@ -223,7 +244,7 @@ const TestPlanManage = (props: any) => {
- ) -} + ); +}; -export default TestPlanManage \ No newline at end of file +export default TestPlanManage; diff --git a/src/pages/WorkSpace/TestReport/index.tsx b/src/pages/WorkSpace/TestReport/index.tsx index 1b671ab786a4444c3519cd47addf2692b73bca88..e901f4d51075747809662691a62077c6572ef282 100644 --- a/src/pages/WorkSpace/TestReport/index.tsx +++ b/src/pages/WorkSpace/TestReport/index.tsx @@ -1,67 +1,80 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { useCallback, useState } from 'react'; -import { writeDocumentTitle, useClientSize } from '@/utils/hooks'; +import { DocTitleWrite, useClientSize } from '@/utils/hooks'; import { Button, Layout, Tabs } from 'antd'; import { history, useAccess, FormattedMessage } from 'umi'; -import { ReportBody } from './styled' -import ReportListTable from './components/ReportListTable' -import ReportTemplateTable from './components/ReportTemplateTable' +import { ReportBody } from './styled'; +import ReportListTable from './components/ReportListTable'; +import ReportTemplateTable from './components/ReportTemplateTable'; export default (props: any) => { - const { match, location } = props - const { ws_id } = match.params - const intalMessage = `menu.Workspace.TestReport.${props.route.name}` - const access = useAccess() - writeDocumentTitle(intalMessage) + const { match, location } = props; + const { ws_id } = match.params; + const intalMessage = `menu.Workspace.TestReport.${props.route.name}`; + const access = useAccess(); - const { height: layoutHeight, width: layoutWidth } = useClientSize() + const { height: layoutHeight, width: layoutWidth } = useClientSize(); - const [tab, setTab] = useState(location.query.t || 'list') + const [tab, setTab] = useState(location.query.t || 'list'); const handleTabClick = useCallback((t) => { - setTab(t) - history.push(location.pathname + `?t=${t}`) - }, []) + setTab(t); + history.push(location.pathname + `?t=${t}`); + }, []); const handleCreateReport = useCallback(() => { - history.push(`/ws/${ws_id}/test_report/compare`) + history.push(`/ws/${ws_id}/test_report/compare`); // window.sessionStorage.setItem(`${ws_id}-compareData`, JSON.stringify([])) // window.sessionStorage.setItem(`${ws_id}-noGroupJobData`, JSON.stringify([])) // window.sessionStorage.setItem('originType', 'test_report') - }, []) + }, []); const hanldeCreateTemplate = useCallback(() => { - history.push(`/ws/${ws_id}/test_report/template`) - }, []) + history.push(`/ws/${ws_id}/test_report/template`); + }, []); return ( + - ) : ( - access.IsWsSetting() && - ) + tab === 'list' + ? access.IsWsSetting() && ( + + ) + : access.IsWsSetting() && ( + + ) } > }> - {access.IsWsSetting() && - }> - + {access.IsWsSetting() && ( + } + > + - } + )} - ) -} \ No newline at end of file + ); +}; diff --git a/src/pages/WorkSpace/TestSuiteSearch/List/DefaultPageList/index.tsx b/src/pages/WorkSpace/TestSuiteSearch/List/DefaultPageList/index.tsx index f1c6ccdbd2ad317fe04bb7e54422d623abe38641..d453df591c5208b5efa063c3db90b21af3e0b52c 100644 --- a/src/pages/WorkSpace/TestSuiteSearch/List/DefaultPageList/index.tsx +++ b/src/pages/WorkSpace/TestSuiteSearch/List/DefaultPageList/index.tsx @@ -1,12 +1,12 @@ /* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, useEffect, useRef } from 'react'; -import { history, useIntl, FormattedMessage, useParams } from 'umi' +import { history, useIntl, FormattedMessage, useParams } from 'umi'; import { Input, Space } from 'antd'; import { SearchOutlined, UpOutlined } from '@ant-design/icons'; import DefaultPageList from './component/DefaultPageTable'; import { queryTotalNum } from '../../service'; import styles from './index.less'; -import { useClientSize, writeDocumentTitle } from '@/utils/hooks'; +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; const { Search } = Input; /** @@ -14,101 +14,127 @@ const { Search } = Input; * @param props */ const TestSuiteSearch: React.FC = (props) => { - const { formatMessage } = useIntl() - writeDocumentTitle(`Workspace.${props.route.name}`) - const { ws_id } = useParams() as any - // 滚动区域可视高度 - // 总数 - const [itemSelected, setItemSelected] = useState('performance'); - const [totalNum, setTotalNum] = useState({ performance_num: 0, functional_num: 0 }) - // 页面滚动到顶部 - const [listScrollTop, setListScrollTop] = useState(0) - const testSuiteSearch_wrapper = useRef(null) + const { formatMessage } = useIntl(); + const { ws_id } = useParams() as any; + // 滚动区域可视高度 + // 总数 + const [itemSelected, setItemSelected] = useState('performance'); + const [totalNum, setTotalNum] = useState({ performance_num: 0, functional_num: 0 }); + // 页面滚动到顶部 + const [listScrollTop, setListScrollTop] = useState(0); + const testSuiteSearch_wrapper = useRef(null); - // 1.获取数量 - const getTotalNum = async () => { - const res = await queryTotalNum({ ws_id, total_num: true }) || {} - if (res.code === 200 && res.data) { - const { functional_num, performance_num } = res.data || {} - setTotalNum({ functional_num, performance_num }) - } - } + // 1.获取数量 + const getTotalNum = async () => { + const res = (await queryTotalNum({ ws_id, total_num: true })) || {}; + if (res.code === 200 && res.data) { + const { functional_num, performance_num } = res.data || {}; + setTotalNum({ functional_num, performance_num }); + } + }; - useEffect(() => { - getTotalNum() - }, [ws_id]) + useEffect(() => { + getTotalNum(); + }, [ws_id]); - // 获取页面伸缩变化后尺寸 - const { height } = useClientSize() + // 获取页面伸缩变化后尺寸 + const { height } = useClientSize(); - // 搜索 - const onSearch = (value: string) => { - if (value) { - history.push({ - pathname: `/ws/${ws_id}/suite_search/key`, - query: { - keyword: value, + // 搜索 + const onSearch = (value: string) => { + if (value) { + history.push({ + pathname: `/ws/${ws_id}/suite_search/key`, + query: { + keyword: value, + }, + }); } - }); - } - } + }; - // 发起回调 - const handleClick = (params: string) => { - return () => { - setItemSelected(params) - } - }; + // 发起回调 + const handleClick = (params: string) => { + return () => { + setItemSelected(params); + }; + }; - const handleScroll = () => { - const top = testSuiteSearch_wrapper.current.scrollTop - setListScrollTop(top) - } - const onScrollBackTop = () => { - // console.log('scrollTop:'); - testSuiteSearch_wrapper.current.scrollTo(0, 0); - } + const handleScroll = () => { + const top = testSuiteSearch_wrapper.current.scrollTop; + setListScrollTop(top); + }; + const onScrollBackTop = () => { + // console.log('scrollTop:'); + testSuiteSearch_wrapper.current.scrollTo(0, 0); + }; - return ( -
-
- } - placeholder={formatMessage({ id: 'test.suite.search.placeholder' })} - allowClear - enterButton={formatMessage({ id: 'test.suite.search' })} - onSearch={onSearch} - /> -
- -
- - ({totalNum.performance_num}) - + return ( +
+ +
+ + } + placeholder={formatMessage({ id: 'test.suite.search.placeholder' })} + allowClear + enterButton={formatMessage({ id: 'test.suite.search' })} + onSearch={onSearch} + /> +
+ +
+ + ( + {totalNum.performance_num}) + +
+
+ + ({totalNum.functional_num}) + +
+
+
+ +
+
-
- - ({totalNum.functional_num}) - +
200 ? 'block' : 'none', paddingTop: 7 }} + > +
- -
- -
-
-
200 ? 'block' : 'none', paddingTop: 7 }}> - -
-
- ); + ); }; export default TestSuiteSearch; diff --git a/src/pages/WorkSpace/TestSuiteSearch/index.tsx b/src/pages/WorkSpace/TestSuiteSearch/index.tsx index 62b50af7313238601642b7eb0909e50dd25864e6..0942e6088e0a6db65f2cff6e917726880fd2d33b 100644 --- a/src/pages/WorkSpace/TestSuiteSearch/index.tsx +++ b/src/pages/WorkSpace/TestSuiteSearch/index.tsx @@ -2,13 +2,13 @@ import React, { useState, useEffect, useRef } from 'react'; import { Spin, Input, Tabs, Space, Badge } from 'antd'; import { SearchOutlined, UpOutlined } from '@ant-design/icons'; -import { useIntl, FormattedMessage, useParams, history } from 'umi' +import { useIntl, FormattedMessage, useParams, history } from 'umi'; import DefaultPageList from './List/DefaultPageList/component/DefaultPageTable'; import SearchPageList from './List/SearchPageList/SearchPageTable'; import { queryTotalNum, querySearchListQuantity } from './service'; import styles from './index.less'; -import { useClientSize, writeDocumentTitle } from '@/utils/hooks' +import { useClientSize, DocTitleWrite } from '@/utils/hooks'; const { Search } = Input; const { TabPane } = Tabs; @@ -17,158 +17,214 @@ const { TabPane } = Tabs; * @param props */ const TestSuiteSearch: React.FC = (props) => { - const { formatMessage } = useIntl() - writeDocumentTitle(`Workspace.${props.route.name}`) - const { ws_id } = useParams() as any - // 滚动区域可视高度 - // 总数 - const [itemSelected, setItemSelected] = useState('performance'); - const [totalNum, setTotalNum] = useState({ performance_num: 0, functional_num: 0 }) - const [initialStyle, setInitialStyle] = useState({ marginTop: 200, width: 700, show: false }); - const [showInitialList, setShowInitialList] = useState(true); - // 搜索栏 - const [loading, setLoading] = useState(false) - const [refresh, setRefresh] = useState(new Date().getTime()) - const [tabKey, setTabKey] = useState('all'); - const [itemTotal, setItemTotal] = useState({ - "total_num": 0, - "suite_num": 0, - "conf_num": 0, - "domain_num": 0, - }) - const [listScrollTop, setListScrollTop] = useState(0) - const testSuiteSearch_wrapper = useRef(null) + const { formatMessage } = useIntl(); + const { ws_id } = useParams() as any; + // 滚动区域可视高度 + // 总数 + const [itemSelected, setItemSelected] = useState('performance'); + const [totalNum, setTotalNum] = useState({ performance_num: 0, functional_num: 0 }); + const [initialStyle, setInitialStyle] = useState({ + marginTop: 200, + width: 700, + show: false, + }); + const [showInitialList, setShowInitialList] = useState(true); + // 搜索栏 + const [loading, setLoading] = useState(false); + const [refresh, setRefresh] = useState(new Date().getTime()); + const [tabKey, setTabKey] = useState('all'); + const [itemTotal, setItemTotal] = useState({ + total_num: 0, + suite_num: 0, + conf_num: 0, + domain_num: 0, + }); + const [listScrollTop, setListScrollTop] = useState(0); + const testSuiteSearch_wrapper = useRef(null); - // 1.获取数量 - const getTotalNum = async () => { - const res = await queryTotalNum({ ws_id, total_num: true }) || {} - if (res.code === 200 && res.data) { - const { functional_num, performance_num } = res.data || {} - setTotalNum({ functional_num, performance_num }) - } - } + // 1.获取数量 + const getTotalNum = async () => { + const res = (await queryTotalNum({ ws_id, total_num: true })) || {}; + if (res.code === 200 && res.data) { + const { functional_num, performance_num } = res.data || {}; + setTotalNum({ functional_num, performance_num }); + } + }; - // 2.获取搜索结果数量 - const getSearchListQuantity = async (query: any) => { - const res = await querySearchListQuantity(query) || {} - if (res.code === 200 && res.data && Object.keys(res.data).length) { - setItemTotal({ ...res.data }) - } - } + // 2.获取搜索结果数量 + const getSearchListQuantity = async (query: any) => { + const res = (await querySearchListQuantity(query)) || {}; + if (res.code === 200 && res.data && Object.keys(res.data).length) { + setItemTotal({ ...res.data }); + } + }; - useEffect(() => { - getTotalNum() - }, [ws_id]) + useEffect(() => { + getTotalNum(); + }, [ws_id]); - // 获取页面伸缩变化后尺寸 - const { height } = useClientSize() + // 获取页面伸缩变化后尺寸 + const { height } = useClientSize(); - // 搜索 - const onSearch = (value: string) => { - if (value) { - if (!initialStyle.show) { - setInitialStyle({ marginTop: 20, minWidth: 800, width: '70%', show: true }); - setShowInitialList(false); - } - getSearchListQuantity({ search_key: value, ws_id }) - setRefresh(new Date().getTime()) - history.push({ - pathname: `/ws/${ws_id}/suite_search/key`, - query: { - keyword: value, + // 搜索 + const onSearch = (value: string) => { + if (value) { + if (!initialStyle.show) { + setInitialStyle({ marginTop: 20, minWidth: 800, width: '70%', show: true }); + setShowInitialList(false); + } + getSearchListQuantity({ search_key: value, ws_id }); + setRefresh(new Date().getTime()); + history.push({ + pathname: `/ws/${ws_id}/suite_search/key`, + query: { + keyword: value, + }, + }); } - }); - } - } + }; - const onTabsChange = ($key: string) => { - setTabKey($key) - } + const onTabsChange = ($key: string) => { + setTabKey($key); + }; - // 发起回调 - const handleClick = (params: string) => { - return () => { - setItemSelected(params) - } - }; + // 发起回调 + const handleClick = (params: string) => { + return () => { + setItemSelected(params); + }; + }; - const handleScroll = () => { - const top = testSuiteSearch_wrapper.current.scrollTop - setListScrollTop(top) - } - const onScrollBackTop = () => { - // console.log('scrollTop:'); - testSuiteSearch_wrapper.current.scrollTo(0, 0); - } + const handleScroll = () => { + const top = testSuiteSearch_wrapper.current.scrollTop; + setListScrollTop(top); + }; + const onScrollBackTop = () => { + // console.log('scrollTop:'); + testSuiteSearch_wrapper.current.scrollTo(0, 0); + }; - const tabList = [ - { name: formatMessage({ id: 'test.suite.all' }), key: 'all', fieldName: 'total_num' }, - { name: 'Suite', key: 'suite', fieldName: 'suite_num' }, - { name: 'Conf', key: 'conf', fieldName: 'conf_num' }, - { name: formatMessage({ id: 'test.suite.domain' }), key: 'domain', fieldName: 'domain_num' }, - ] - const selectedStyle = { backgroundColor: '#E6F7FF', color: '#1890FF', marginTop: -3 } - const othersStyle = { backgroundColor: '#0000000a', color: '#000', marginTop: -3 } + const tabList = [ + { name: formatMessage({ id: 'test.suite.all' }), key: 'all', fieldName: 'total_num' }, + { name: 'Suite', key: 'suite', fieldName: 'suite_num' }, + { name: 'Conf', key: 'conf', fieldName: 'conf_num' }, + { + name: formatMessage({ id: 'test.suite.domain' }), + key: 'domain', + fieldName: 'domain_num', + }, + ]; + const selectedStyle = { backgroundColor: '#E6F7FF', color: '#1890FF', marginTop: -3 }; + const othersStyle = { backgroundColor: '#0000000a', color: '#000', marginTop: -3 }; - return ( -
-
-
- } - placeholder={formatMessage({ id: 'test.suite.search.placeholder' })} - allowClear - enterButton={formatMessage({ id: 'test.suite.search' })} - onSearch={onSearch} - /> - {showInitialList ? ( -
- -
- - ({totalNum.performance_num}) - -
-
- - ({totalNum.functional_num}) - -
-
-
- + return ( +
+ +
+
+ + } + placeholder={formatMessage({ id: 'test.suite.search.placeholder' })} + allowClear + enterButton={formatMessage({ id: 'test.suite.search' })} + onSearch={onSearch} + /> + {showInitialList ? ( +
+ +
+ + ( + {totalNum.performance_num}) + +
+
+ + ( + {totalNum.functional_num}) + +
+
+
+ +
+
+ ) : ( +
+ + {tabList.map((item) => ( + + {item.name}{' '} + + + } + disabled={loading} + /> + ))} + + + + +
+ )}
-
- ) : ( -
- - {tabList.map((item) => ( - {item.name} - - } disabled={loading} /> - ))} - - - - -
- ) - } -
-
200 ? 'block' : 'none', paddingTop: 7 }}> - -
-
- ); +
200 ? 'block' : 'none', paddingTop: 7 }} + > + +
+
+ ); }; export default TestSuiteSearch; diff --git a/src/utils/hooks.tsx b/src/utils/hooks.tsx index d06795f8ff610efd9853b0b32a451393438b85cd..b9013c03bf69e3d91a3ebca3f32323b0e0868c12 100644 --- a/src/utils/hooks.tsx +++ b/src/utils/hooks.tsx @@ -1,177 +1,186 @@ -import React, { useEffect, useState, useLayoutEffect, useCallback } from 'react' +import React, { useEffect, useState, useLayoutEffect, useCallback } from 'react'; import { ReactComponent as IconLink } from '@/assets/svg/Report/IconLink.svg'; -import { useIntl, useParams } from 'umi' -import { Tooltip, message } from 'antd' -import Clipboard from 'clipboard' +import { Helmet, useIntl, useParams } from 'umi'; +import { Tooltip, message } from 'antd'; +import Clipboard from 'clipboard'; import { enterWorkspaceHistroy } from '@/services/Workspace'; -import { queryWorkspaceHistory } from '@/services/Workspace' +import { queryWorkspaceHistory } from '@/services/Workspace'; -const { document, location }: any = window +const { document, location }: any = window; export const enterWsAndGetList = async (ws_id: any) => { - try { - const { data } = await enterWorkspaceHistroy({ ws_id }) || {} - const { code, ...historyWorkspaces } = await queryWorkspaceHistory({ - page_num: 1, - page_size: 20, - call_page: 'menu', - ws_id - }) - if (code !== 200) return - return { - wsList: { ...historyWorkspaces }, - first_entry: data?.first_entry - } - } - catch (err) { - console.log(err) - } - return -} + try { + const { data } = (await enterWorkspaceHistroy({ ws_id })) || {}; + const { code, ...historyWorkspaces } = await queryWorkspaceHistory({ + page_num: 1, + page_size: 20, + call_page: 'menu', + ws_id, + }); + if (code !== 200) return; + return { + wsList: { ...historyWorkspaces }, + first_entry: data?.first_entry, + }; + } catch (err) { + console.log(err); + } + return; +}; export const useDetectZoom = () => { - const [ratio, setRatio] = useState(1) - - const detectZoom = () => { - var ratio: number = 0, - screen: any = window.screen, - ua: any = navigator.userAgent.toLowerCase(); - - if (window.devicePixelRatio !== undefined) { - ratio = window.devicePixelRatio; - } - else if (~ua.indexOf('msie')) { - if (screen.deviceXDPI && screen.logicalXDPI) { - ratio = screen.deviceXDPI / screen.logicalXDPI; - } - } - else if (window.outerWidth !== undefined && window.innerWidth !== undefined) { - ratio = window.outerWidth / window.innerWidth; - } - - if (ratio) { - ratio = 1.5 / ratio - } - setRatio(ratio) + const [ratio, setRatio] = useState(1); + + const detectZoom = () => { + var ratio: number = 0, + screen: any = window.screen, + ua: any = navigator.userAgent.toLowerCase(); + + if (window.devicePixelRatio !== undefined) { + ratio = window.devicePixelRatio; + } else if (~ua.indexOf('msie')) { + if (screen.deviceXDPI && screen.logicalXDPI) { + ratio = screen.deviceXDPI / screen.logicalXDPI; + } + } else if (window.outerWidth !== undefined && window.innerWidth !== undefined) { + ratio = window.outerWidth / window.innerWidth; } - useEffect(() => { - detectZoom() - window.addEventListener('resize', detectZoom); - return () => { - window.removeEventListener('resize', detectZoom); - } - }, []); + if (ratio) { + ratio = 1.5 / ratio; + } + setRatio(ratio); + }; - return ratio -} + useEffect(() => { + detectZoom(); + window.addEventListener('resize', detectZoom); + return () => { + window.removeEventListener('resize', detectZoom); + }; + }, []); + + return ratio; +}; export const useClientSize = () => { - const [layout, setLayout] = useState({ height: innerHeight, width: innerWidth }) - const onResize = useCallback( - () => { - setLayout({ - height: innerHeight, - width: innerWidth - }) - }, [] - ) - - useEffect(() => { - window.addEventListener('resize', onResize) - return () => { - window.removeEventListener('resize', onResize) - } - }, []) - - return layout -} + const [layout, setLayout] = useState({ height: innerHeight, width: innerWidth }); + const onResize = useCallback(() => { + setLayout({ + height: innerHeight, + width: innerWidth, + }); + }, []); + + useEffect(() => { + window.addEventListener('resize', onResize); + return () => { + window.removeEventListener('resize', onResize); + }; + }, []); + + return layout; +}; export const useRemoveElementTitle = () => { - useLayoutEffect(() => { - document.querySelectorAll('td').forEach((el: any) => el.removeAttribute('title')) - document.querySelectorAll('label').forEach((el: any) => el.removeAttribute('title')) - document.querySelectorAll('a').forEach((el: any) => el.removeAttribute('title')) - document.querySelectorAll('svg title').forEach((el: any) => el.innerHTML = '') - }, [location.pathname]) -} - -export const writeDocumentTitle = function (title: string) { - const intl = useIntl() - document.title = `${intl.messages[title]} - T-One` -} + useLayoutEffect(() => { + document.querySelectorAll('td').forEach((el: any) => el.removeAttribute('title')); + document.querySelectorAll('label').forEach((el: any) => el.removeAttribute('title')); + document.querySelectorAll('a').forEach((el: any) => el.removeAttribute('title')); + document.querySelectorAll('svg title').forEach((el: any) => (el.innerHTML = '')); + }, [location.pathname]); +}; + +export const DocTitleWrite: any = ({ id }: any) => { + const intl = useIntl(); + return ( + + {intl.formatMessage({ id })} - T-One + + ); +}; type ListProps = { - name: string, - id: string -} + name: string; + id: string; +}; type ResultProps = { - job_id: string | number, - style?: React.CSSProperties; - ws_id?: string; -} - -export const listRender: React.FC = ({ name, id }) => {name} + job_id: string | number; + style?: React.CSSProperties; + ws_id?: string; +}; + +export const listRender: React.FC = ({ name, id }) => ( + + {name} + +); export const textRender = (name: any) => { - return {name} -} + return ( + + {name} + + ); +}; export const enumer = (name: any) => { - const list: any = { - system: '公共镜像', - self: '自定义镜像', - others: '共享镜像' - } - return list[name]; -} + const list: any = { + system: '公共镜像', + self: '自定义镜像', + others: '共享镜像', + PRIVATE_IMAGE: '私有镜像', + PUBLIC_IMAGE: '公共镜像', + SHARED_IMAGE: '共享镜像', + }; + return list[name]; +}; export const getTextByJs = (obj: any) => { - let str = ""; - let len = Object.keys(obj).length - Object.keys(obj).map((k: any, i: number) => { - if (i === len - 1) { - str += `${k}=${obj[k]}` - } else { - str += `${k}=${obj[k]}\n` - } - }) - return str; -} - -export const JumpResult: React.FC = ({ job_id, style, ws_id }) => { - const { ws_id: wsid } = useParams() as any - - const id = ws_id || wsid - if (job_id && id) { - return ( - - {} - - ) + let str = ''; + let len = Object.keys(obj).length; + Object.keys(obj).map((k: any, i: number) => { + if (i === len - 1) { + str += `${k}=${obj[k]}`; + } else { + str += `${k}=${obj[k]}\n`; } - return <> -} + }); + return str; +}; +export const JumpResult: React.FC = ({ job_id, style, ws_id }) => { + const { ws_id: wsid } = useParams() as any; + + const id = ws_id || wsid; + if (job_id && id) { + return ( + + {} + + ); + } + return <>; +}; export const useCopyText = (successText: string) => (text: string) => { - const ele = document.createElement("a") - ele.style.height = "0px" - ele.style.width = "0px" - ele.innerHTML = "" - ele.id = "currentCopyLinkEle" - document.body.appendChild(ele) - const cb = new Clipboard(ele, { - text: () => text - }) - - cb.on('success', function (e) { - message.success(successText) - }) - ele.click() - cb.destroy() -} \ No newline at end of file + const ele = document.createElement('a'); + ele.style.height = '0px'; + ele.style.width = '0px'; + ele.innerHTML = ''; + ele.id = 'currentCopyLinkEle'; + document.body.appendChild(ele); + const cb = new Clipboard(ele, { + text: () => text, + }); + + cb.on('success', function (e) { + message.success(successText); + }); + ele.click(); + cb.destroy(); +};