From ad96be936da58544647b959d026e7b5e4350563e Mon Sep 17 00:00:00 2001 From: wulibaibao <13366578180@163.com> Date: Wed, 13 Apr 2022 22:28:01 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=B5=8B=E8=AF=95=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/routes.ts | 16 +- src/components/Loading.tsx | 20 + src/locales/zh-CN/menu.ts | 2 + .../components/ContentTable/Suite.table.tsx | 89 ++-- .../components/ContentTable/Task.table.tsx | 86 ++-- src/pages/Plan/components/LeftList/index.tsx | 87 +++- .../components/SelectModal/Suite.selet.tsx | 150 ++++++- .../components/SelectModal/Task.select.tsx | 12 +- src/pages/Plan/hooks.ts | 15 + src/pages/Plan/index.tsx | 116 +++++- src/pages/Plan/services.ts | 36 +- src/pages/Suite/components/LeftList/index.tsx | 46 +-- .../Suite/components/LeftList/styled.tsx | 43 ++ .../components/RightContent/SuiteList.tsx | 2 +- .../Suite/components/RightContent/index.tsx | 389 +++++++++--------- src/pages/Suite/index.tsx | 4 +- src/pages/Suite/services.ts | 2 +- 17 files changed, 716 insertions(+), 399 deletions(-) create mode 100644 src/components/Loading.tsx create mode 100644 src/pages/Plan/hooks.ts create mode 100644 src/pages/Suite/components/LeftList/styled.tsx diff --git a/config/routes.ts b/config/routes.ts index 9881f9c..647b79f 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -12,7 +12,20 @@ { path: "/plan", name: "plan", - component: "./Plan", + routes: [ + { + path: "/plan/create", + name: "create", + hideInMenu: true, + component: "./Plan", + }, + { + path: "/plan/:plan_id?", + hideInMenu: true, + name: "plan", + component: "./Plan", + }, + ] }, { path: "/cases", @@ -20,6 +33,7 @@ routes: [ { path: "/cases/:mod_id?", + hideInMenu: true, component: "./Suite", }, ] diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx new file mode 100644 index 0000000..ddb61fa --- /dev/null +++ b/src/components/Loading.tsx @@ -0,0 +1,20 @@ +import React from "react" +import { Row, Spin } from "antd" +import styled from "styled-components" + +const LoadingWrapper = styled(Row)` + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + background: #fff; + padding-top: 300px; +` +const Loading: React.FC = () => ( + + + +) + +export default Loading \ No newline at end of file diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index 0e122e7..7a27b20 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -3,6 +3,8 @@ export default { "menu.outline": "测试大纲", "menu.demand": "测试需求", "menu.plan": "测试方案", + "menu.plan.plan": "测试方案", + "menu.plan.create": "创建方案", "menu.suite": "测试用例", "menu.task": "测试任务", "menu.server": "测试设备", diff --git a/src/pages/Plan/components/ContentTable/Suite.table.tsx b/src/pages/Plan/components/ContentTable/Suite.table.tsx index ae83538..f0373cc 100644 --- a/src/pages/Plan/components/ContentTable/Suite.table.tsx +++ b/src/pages/Plan/components/ContentTable/Suite.table.tsx @@ -1,71 +1,64 @@ import React from "react" -import { Table , Space, Typography, Row, Button } from "antd" -import { queryTableList } from "./services" -import { useRequest } from "ahooks" +import { Table, Space, Typography, Row, Button, TableColumnType } from "antd" +import SuiteSelet from "../SelectModal/Suite.selet" -const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 } - -const TableList: React.FC = () => { - const [pageQuery, setPageQuery] = React.useState(DEFAULT_PAGE_QUERY) - - const { data: dataSource, loading , refresh } = useRequest( - (params = pageQuery) => queryTableList(params), - { refreshDeps: [pageQuery] } - ) - - const addModalRef = React.useRef(null) as any +const TableList: React.FC = (porps) => { + // const { dataSource } = props + const [source, setSource] = React.useState([]) + const ref = React.useRef(null) as any const handleAdd = async () => { - addModalRef.current?.show() + ref.current?.show() } - const handleEdit = async (row: any) => { - addModalRef.current?.show(row) + const handleDelete = (row: any) => { + setSource(source.filter((i: any) => i.id !== row.id)) } - const handleDelete = async (row: any) => { - - } - - const columns = [{ - title: "", - dataIndex: "", - }, { - title: "操作", - render(row: any) { - return ( - - handleEdit(row)}> - 编辑 - + const columns: TableColumnType[] = [ + { + title: "用例名称", + dataIndex: "name", + }, + { + title: "用例名称", + dataIndex: "name", + }, + { + title: "用例名称", + dataIndex: "name", + }, + { + title: "用例名称", + dataIndex: "name", + }, + { + title: "操作", + render(row) { + return ( handleDelete(row)}> - 删除 + + 删除 + - - ) - } - }] + ) + } + }, + ] return ( 用例和机器 - + + ) } diff --git a/src/pages/Plan/components/ContentTable/Task.table.tsx b/src/pages/Plan/components/ContentTable/Task.table.tsx index 97ef842..5bc37f1 100644 --- a/src/pages/Plan/components/ContentTable/Task.table.tsx +++ b/src/pages/Plan/components/ContentTable/Task.table.tsx @@ -1,71 +1,63 @@ import React from "react" -import { Table, Space, Typography, Row, Button } from "antd" -import { queryTableList } from "./services" -import { useRequest } from "ahooks" - -const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 } +import { Table, Space, Typography, Row, Button, TableColumnType } from "antd" +import TaskSelect from "../SelectModal/Task.select" const TableList: React.FC = () => { - const [pageQuery, setPageQuery] = React.useState(DEFAULT_PAGE_QUERY) - - const { data: dataSource, loading, refresh } = useRequest( - (params = pageQuery) => queryTableList(params), - { refreshDeps: [pageQuery] } - ) - - const addModalRef = React.useRef(null) as any + const ref = React.useRef(null) as any + const [source, setSource] = React.useState([]) const handleAdd = async () => { - addModalRef.current?.show() + ref.current?.show() } - const handleEdit = async (row: any) => { - addModalRef.current?.show(row) + const handleDelete = (row: any) => { + setSource(source.filter((i: any) => i.id !== row.id)) } - const handleDelete = async (row: any) => { - - } - - const columns = [{ - title: "", - dataIndex: "", - }, { - title: "操作", - render(row: any) { - return ( - - handleEdit(row)}> - 编辑 - + const columns: TableColumnType[] = [ + { + title: "任务名称", + dataIndex: "name", + }, + { + title: "运行方式", + dataIndex: "name", + }, + { + title: "测试设备", + dataIndex: "name", + }, + { + title: "备注", + dataIndex: "desc", + }, + { + title: "操作", + render(row) { + return ( handleDelete(row)}> - 删除 + + 删除 + - - ) - } - }] + ) + } + }, + ] return ( 测试任务 - +
+ ) } diff --git a/src/pages/Plan/components/LeftList/index.tsx b/src/pages/Plan/components/LeftList/index.tsx index 57ee602..b710f29 100644 --- a/src/pages/Plan/components/LeftList/index.tsx +++ b/src/pages/Plan/components/LeftList/index.tsx @@ -1,33 +1,88 @@ import React from "react"; import { Button, Input, Row, Space, Tooltip, Typography } from "antd" import StateTag from "@/components/Public/StateTag"; +import { history, useParams } from "umi" +import { usePlanProvider } from "../../hooks"; +import styled from "styled-components" + +type PlanItemProps = { + is_active?: number; +} + +const activeCls = ` + background-color: #E6F7FF; + color: rgba(24,144,255,1); + .ant-typography { + color: rgba(24,144,255,1); + } +` +const PlanItem = styled(Row) ` + cursor: pointer; + height: 32px; + padding: 4px 12px; + border-bottom: 1px solid #D9D9D9; + ${({ is_active }) => is_active ? activeCls : ""} +` type IProps = { [k: string]: any; } const PlanLeftContent: React.FC = () => { + const { plan_id } = useParams() as any + const { planList } = usePlanProvider() return ( - - - - - +
+ + {`测试方案 (${planList.length})`} + + console.log(e)} + // suffix + /> + + + - -
- - - {"测试方案名称测试方案名称测试方案名称测试方案名称测试方案名称测试方案名称"} - - -
- -
+ { + planList.map((plan: any) => ( + history.push(`/plan/${plan.id}`)} + is_active={plan.id === +plan_id ? 1 : 0} + > +
+ + + {plan.title} + + +
+ +
+ )) + }
- +
) } diff --git a/src/pages/Plan/components/SelectModal/Suite.selet.tsx b/src/pages/Plan/components/SelectModal/Suite.selet.tsx index cf65c1c..3565053 100644 --- a/src/pages/Plan/components/SelectModal/Suite.selet.tsx +++ b/src/pages/Plan/components/SelectModal/Suite.selet.tsx @@ -1,8 +1,11 @@ import React from "react" -import { Modal, Form, Input, Space, Button } from "antd" +import { Modal, Form, Input, Space, Button, Typography, Row, Radio, Checkbox, Empty, Spin } from "antd" +import { useRequest } from "ahooks"; +import { queryModalCases, queryModules } from "@/pages/Suite/services"; +import { ModuleItem, ModulesContainer, FuncModuleIcon } from "@/pages/Suite/components/LeftList/styled" type IProps = { - onOk: () => void; + onOk: (conf: any) => void; onCancel?: () => void; } @@ -13,6 +16,25 @@ type IRefs = { const ReactComponent: React.ForwardRefRenderFunction = (props, ref) => { const { onOk } = props + const [activeModule, setActiveModule] = React.useState(null) + + const { data: modResponse } = useRequest(() => queryModules(0), {}) + const { data: caseResponse, run, loading: caseLoading } = useRequest((params = { mod_id: null }) => queryModalCases(params)) + + React.useEffect(() => { + run({ mod_id: activeModule }) + }, [activeModule]) + + const modules = React.useMemo(() => { + if (modResponse) return modResponse.data + return [] + }, [modResponse]) + + const cases = React.useMemo(() => { + if (caseResponse) return caseResponse.data + return [] + }, [caseResponse]) + const [visible, setVisible] = React.useState(false) const [loading, setLoading] = React.useState(false) const [source, setSource] = React.useState(undefined) @@ -25,42 +47,138 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re } })) + const allCaseId = React.useMemo(() => cases.map((x: any) => x.id), [cases]) + + const [indeterminate, setIndeterminate] = React.useState(false) + const [checkAll, setCheckAll] = React.useState(false) + const [selectedKeys, setSelectedKeys] = React.useState([]) + + const onCheckAllChange = ({ target }: any) => { + setCheckAll(target.checked) + setIndeterminate(false) + setSelectedKeys(target.checked ? allCaseId : []) + } + + const onChange = (list: any) => { + setSelectedKeys(list) + setIndeterminate(!!list.length && list.length < allCaseId.length); + setCheckAll(list.length === allCaseId.length); + } + const [form] = Form.useForm() const handleCancel = () => { setVisible(false) setLoading(false) setSource(undefined) + setSelectedKeys([]) + setIndeterminate(false) + setCheckAll(false) + setActiveModule(null) } const handleOk = async () => { if (loading) return setLoading(true) - onOk() + onOk(cases.filter((i: any) => selectedKeys.includes(i.id))) handleCancel() } + const onModuleChange = (mod_id: any) => { + setActiveModule(mod_id) + setSelectedKeys([]) + } + return ( - - -
+ + + {`共${allCaseId.length}条 已选中${selectedKeys.length}条`} + + + + + + } onCancel={handleCancel} - onOk={handleOk} > -
- - - - + + + +
+ onModuleChange(null)} + > + + + 所有用例 + + + + { + modules.map((mod: any) => ( + onModuleChange(mod.id)} + is_child + > + + + + {mod.name} + + + {`(${mod.count || 0})`} + + + + )) + } + +
+
+ + { + cases.length === 0 ? + : + + + 全选 + + + + { + cases.map((i: any) => ( + {i.name} + )) + } + + + + } + +
+
+
) } diff --git a/src/pages/Plan/components/SelectModal/Task.select.tsx b/src/pages/Plan/components/SelectModal/Task.select.tsx index cf65c1c..da78b8f 100644 --- a/src/pages/Plan/components/SelectModal/Task.select.tsx +++ b/src/pages/Plan/components/SelectModal/Task.select.tsx @@ -1,8 +1,9 @@ import React from "react" import { Modal, Form, Input, Space, Button } from "antd" +import { useRequest } from "umi"; type IProps = { - onOk: () => void; + onOk: (conf: any) => void; onCancel?: () => void; } @@ -17,6 +18,8 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re const [loading, setLoading] = React.useState(false) const [source, setSource] = React.useState(undefined) + // const { data } = useRequest(queryTaskList) + React.useImperativeHandle(ref, () => ({ show(_: any) { setSource(_) @@ -36,23 +39,22 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re const handleOk = async () => { if (loading) return setLoading(true) - onOk() + onOk([]) handleCancel() } return ( - + } onCancel={handleCancel} - onOk={handleOk} >
( + { + state: { + planList: [], + }, + dispatch: () => null + } +) + +export const usePlanProvider = () => { + const provider = React.useContext(Provider) + return provider +} \ No newline at end of file diff --git a/src/pages/Plan/index.tsx b/src/pages/Plan/index.tsx index b591934..f994ca6 100644 --- a/src/pages/Plan/index.tsx +++ b/src/pages/Plan/index.tsx @@ -1,42 +1,116 @@ import React from "react" -import { Table, Space, Typography, Row, Button, Col } from "antd" -import { queryTableList } from "./services" +import { Table, Space, Typography, Row, Button, Col, Spin, Form, message, Input, Select } from "antd" +import { createTestPlan, queryTestPlanList } from "./services" import { useRequest } from "ahooks" import AddModal from "./components/AddModal" import LeftList from "./components/LeftList" import RightContent from "./components/RightContent" import EditContent from "./components/RightContent/EditContent" +import { Provider as PlanPageProvider } from "./hooks" +import Loading from "@/components/Loading" +import { history, request, useParams } from "umi" +import RichTextEditor from "@/components/RichTextEditor" +import SuiteTable from "@/pages/Plan/components/ContentTable/Suite.table" +import TaskTable from "@/pages/Plan/components/ContentTable/Task.table" /** * * 测试方案 * */ -const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 } -const TestPlan: React.FC = () => { - const [pageQuery, setPageQuery] = React.useState(DEFAULT_PAGE_QUERY) +type IProps = { + [k: string]: any; +} + +const TestPlan: React.FC = (props) => { + const [form] = Form.useForm() + const { data: requirement } = useRequest(() => request(`/api/requirement`), {}) + + const handleSave = () => { + form.validateFields() + .then(async (values) => { + const { code, msg } = await createTestPlan(values) + if (code !== 200) { + message.error(msg) + return + } + }) + } - // const { data, loading } = useRequest(queryTableList, {}) - // if (!data) return <> + const requirementList = React.useMemo(() => { + if (requirement) { + const { data } = requirement + if (data) return data.map((i: any) => ({ label: i.title, value: i.id })) + } + return [] + }, [requirement]) return ( - - - 测试方案 - - -
- - - - {/* */} - - - - +
+ + + 新建方案 + + + 基础信息 + + + + + + - - + + + + { + taskList.map((i: any) => ( + {i.name} + )) + } + + + ) } -- Gitee From 69864897c15c6c38996685bcca1e66c052a34268 Mon Sep 17 00:00:00 2001 From: wulibaibao <13366578180@163.com> Date: Thu, 14 Apr 2022 09:46:49 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E6=B5=8B=E8=AF=95=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CustomStyled.tsx | 20 +++++++++++ .../components/ContentTable/Suite.table.tsx | 5 +-- .../components/ContentTable/Task.table.tsx | 3 +- src/pages/Plan/index.tsx | 33 ++++++++++--------- src/pages/Suite/components/AddModal.tsx | 7 +--- src/pages/Task/components/AddTaskModal.tsx | 6 +--- 6 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 src/components/CustomStyled.tsx diff --git a/src/components/CustomStyled.tsx b/src/components/CustomStyled.tsx new file mode 100644 index 0000000..e319817 --- /dev/null +++ b/src/components/CustomStyled.tsx @@ -0,0 +1,20 @@ +import styled from "styled-components" +import { Table, Form } from "antd" + +export const CustomTable = styled(Table)` + .ant-table-small .ant-table-thead > tr > th { + background-color: transparent; + } + .ant-table-thead > tr > th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before { + background-color: transparent; + } + .ant-table-expanded-row.ant-table-expanded-row-level-1 > td { + padding: 0!important; + } +` + +export const CustomForm = styled(Form)` +.ant-form-item { + margin-bottom: 12px; +} +` \ No newline at end of file diff --git a/src/pages/Plan/components/ContentTable/Suite.table.tsx b/src/pages/Plan/components/ContentTable/Suite.table.tsx index f0373cc..5581b40 100644 --- a/src/pages/Plan/components/ContentTable/Suite.table.tsx +++ b/src/pages/Plan/components/ContentTable/Suite.table.tsx @@ -1,6 +1,7 @@ import React from "react" import { Table, Space, Typography, Row, Button, TableColumnType } from "antd" import SuiteSelet from "../SelectModal/Suite.selet" +import { CustomTable } from "@/components/CustomStyled" const TableList: React.FC = (porps) => { // const { dataSource } = props @@ -49,10 +50,10 @@ const TableList: React.FC = (porps) => { return ( - 用例和机器 + 用例 -
{ const ref = React.useRef(null) as any @@ -51,7 +52,7 @@ const TableList: React.FC = () => { 测试任务 -
= (props) => { const [form] = Form.useForm() - const { data: requirement } = useRequest(() => request(`/api/requirement`), {}) + const { data: requirement } = useRequest(() => request(`/api/requirement`), { initialData: [] }) const handleSave = () => { form.validateFields() @@ -40,22 +41,20 @@ const TestPlan: React.FC = (props) => { } const requirementList = React.useMemo(() => { - if (requirement) { - const { data } = requirement - if (data) return data.map((i: any) => ({ label: i.title, value: i.id })) - } + if (requirement) + return requirement.map((i: any) => ({ label: i.title, value: i.id })) return [] }, [requirement]) return ( -
+
- 新建方案 + 新建方案 - 基础信息 -
基础信息 + = (props) => { placeholder="请选择测试需求" /> - - 描述 -
+ + 描述 +
- -
+
+
) /* console.log(props) diff --git a/src/pages/Suite/components/AddModal.tsx b/src/pages/Suite/components/AddModal.tsx index 9884257..ca4fd6d 100644 --- a/src/pages/Suite/components/AddModal.tsx +++ b/src/pages/Suite/components/AddModal.tsx @@ -11,6 +11,7 @@ import { createModule } from "@/pages/Suite/services" import { ReactComponent as CancelIcon } from "@/assets/case/cancel.svg" import { ReactComponent as ConfirmIcon } from "@/assets/case/confirm.svg" import { ToneCaseItem } from "@/pages/Suite/components/ToneCase" +import { CustomForm } from "@/components/CustomStyled"; type IProps = { onOk: () => void; @@ -21,12 +22,6 @@ type IRefs = { [k: string]: any } -const CustomForm = styled(Form)` - .ant-form-item { - margin-bottom: 12px; - } -` - const ModuleSelect: React.FC = () => { const { state: { modules }, refreshModules } = useCaseProvider() diff --git a/src/pages/Task/components/AddTaskModal.tsx b/src/pages/Task/components/AddTaskModal.tsx index 073b006..ea8040c 100644 --- a/src/pages/Task/components/AddTaskModal.tsx +++ b/src/pages/Task/components/AddTaskModal.tsx @@ -5,12 +5,8 @@ import { request } from "umi"; import { runMethodOptions } from "@/pages/Suite/utils" import styled from "styled-components" import { createTask } from "../services"; +import { CustomForm } from "@/components/CustomStyled"; -const CustomForm = styled(Form)` - .ant-form-item { - margin-bottom: 12px; - } -` type IProps = { onOk: () => void; onCancel?: () => void; -- Gitee