From 13aa8e30083018c58134ae96a05f2ff591ef6c42 Mon Sep 17 00:00:00 2001 From: wulibaibao <13366578180@163.com> Date: Tue, 12 Apr 2022 16:20:18 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E7=94=A8=E4=BE=8B=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Suite/components/AddModal.tsx | 2 +- src/pages/Suite/components/Case/Edit.tsx | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/pages/Suite/components/AddModal.tsx b/src/pages/Suite/components/AddModal.tsx index dde56ac..5360c50 100644 --- a/src/pages/Suite/components/AddModal.tsx +++ b/src/pages/Suite/components/AddModal.tsx @@ -188,7 +188,7 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re /* if (!richEditerRef.current) return setLoading(false) - const text = .getText() + const text = richEditerRef.current.getText() const descArr = text.split("\n").filter(Boolean) const base_fields = descArr.reduce((pre: any, cur: any) => { diff --git a/src/pages/Suite/components/Case/Edit.tsx b/src/pages/Suite/components/Case/Edit.tsx index a1bf6aa..74b9d5f 100644 --- a/src/pages/Suite/components/Case/Edit.tsx +++ b/src/pages/Suite/components/Case/Edit.tsx @@ -77,6 +77,21 @@ const EditChild: React.ForwardRefRenderFunction<{}, IProps> = (props, ref) => { vm.current = editor } + React.useImperativeHandle(ref, () => { + return { + ...form, + } + }) + + console.log(currentCase) + React.useEffect(() => { + if (JSON.stringify(currentCase) !== "{}") + form.setFieldsValue(currentCase) + return () => { + form.resetFields() + } + }, [currentCase]) + return ( Date: Thu, 14 Apr 2022 19:56:06 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E9=9C=80=E6=B1=82=E3=80=81=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 + src/assets/png/picker.png | Bin 0 -> 1423 bytes src/assets/svg/Delete.svg | 14 + src/components/ColorPicker/index.tsx | 184 +++++++++++++ src/components/ColorPicker/style.less | 38 +++ src/components/CustomPagination.tsx | 36 +++ src/components/Public/DelModal.tsx | 71 +++++ src/components/Public/StatusTag.tsx | 51 ++++ src/components/RichTextEditor/index.tsx | 6 +- src/components/RichTextEditor/services.ts | 11 + src/global.less | 4 + src/pages/Demand/components/AddDemand.tsx | 242 ++++++++++++++---- src/pages/Demand/index.tsx | 131 +++++++--- src/pages/Demand/services.ts | 56 +++- .../Server/components/AddServerModal.tsx | 115 +++++++-- .../components/CheckboxSearchSelect.tsx | 170 ++++++++++++ src/pages/Server/index.less | 51 ++++ src/pages/Server/index.tsx | 155 ++++++++--- src/pages/Server/services.ts | 75 +++++- src/utils/index.ts | 78 ++++++ 20 files changed, 1336 insertions(+), 156 deletions(-) create mode 100644 src/assets/png/picker.png create mode 100644 src/assets/svg/Delete.svg create mode 100644 src/components/ColorPicker/index.tsx create mode 100644 src/components/ColorPicker/style.less create mode 100644 src/components/CustomPagination.tsx create mode 100644 src/components/Public/DelModal.tsx create mode 100644 src/components/Public/StatusTag.tsx create mode 100644 src/components/RichTextEditor/services.ts create mode 100644 src/pages/Server/components/CheckboxSearchSelect.tsx create mode 100644 src/pages/Server/index.less diff --git a/package.json b/package.json index 22a7884..26cf561 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,8 @@ "@antv/l7": "^2.3.7", "@antv/l7-maps": "^2.3.7", "@antv/l7-react": "^2.1.9", + "@types/react-color": "^3.0.6", + "@types/reactcss": "^1.2.6", "@types/styled-components": "^5.1.24", "@umijs/route-utils": "^1.0.36", "@wangeditor/editor": "^0.15.15", @@ -73,11 +75,13 @@ "nzh": "^1.0.3", "omit.js": "^2.0.2", "react": "^17.0.0", + "react-color": "^2.19.3", "react-dev-inspector": "^1.1.1", "react-dom": "^17.0.0", "react-fittext": "^1.0.0", "react-helmet-async": "^1.0.4", "react-router": "^4.3.1", + "reactcss": "^1.2.3", "styled-components": "^5.3.5", "umi": "^3.5.0", "umi-serve": "^1.9.10" diff --git a/src/assets/png/picker.png b/src/assets/png/picker.png new file mode 100644 index 0000000000000000000000000000000000000000..425925256a4251232999622c041bc932b019c30a GIT binary patch literal 1423 zcmV;A1#tR_P)Px)Nl8ROR9FesnLTnGHxPvzz|8LQM^!c*v5K)n^9syVI)V>iWmd==IAJK46Zrr- zf>K?7(6^QdU`OyNY{T}U(EZzZ&FyUMqAFK zWm|>4Nv^|!Tx=5$mXOk?knPxtTt_7OlPWio^f~3a`VZ4&an2U*XA6hL(!*>a_tsX!?J&}hbh(lx&_ zqY$7y3P_FA13B(WJ7X0z0T$8$$BE9U!nB;(_VH$zEnVqGONflNROZS(4V}hQ5`6U( zAtJ_{VF0r#k+M{+ZEI2*sz&ltaA zAms4jXtjF%(>FJ_<@V3>&+=c))BYVc{mQ{=MHV}cFbu2OHYFQiZ=M;fDAEQ?tMRpi zZ44vhJ|5Ym9M)Lx%6{$uY+F--c3T&_)#e8|Eo}(jQf`;6c_;EScE$EEg<}A-*`j=b zu6CB>Jp>DnqKzgm;T#vI)frg9`8UJ6xN(Mu{C{CM4m>FEulPU=qd$t}_%VgX^spr= z@Mk`cn&SRD!~D4o!>8Nu;sMUcL!)Dc+os#aQ)l+DOnoxEttKHqEH6>~a3)ZLVTDJ% zCE2iGDGTsJeBsQdOnmCr-5KKh0`v*6!g2v^;a_KPpP2P9Q4DV@!&B4rhWN2EtW8H6 z7W}Z{&}Pd;-O+#DY;LV$_})$&UFy+(g{(1L{Q+J=uROW}{2oC!w^kvBu+zIpts(L{ zIsBq~{{K9F$>NZghOf!z2l)kpgWe@SrD1$16}|Z?O+|F`pVwa(OZ9ht&M8857Y?|B z?gp)Z#F0OM>5UqYL#n&5SHOAmRRO&4j0W=%{Gfq})M!YP>MAvc34(=q7~8YDFy0kb zfyrwzj1!)Df?yVd%5DxVvhNLY!iE*6L#Tj1*>Gx)z|H{Y)e8q|hsv;f37JDJ_<#Opezwxv9hKiM#KcW<3BY>9Xs8_paF7aOh&b5QI|_lDP!*cB=P zX72?~R+NXDzk8c6dI6nZ{d$pyW6R?)NH?Bil>OGM@@}$sJ%AS2X+FHu!`RtPFr0Y* z{Pvgp2PSeZHz(kcfLL;%t_iG=33*iOM_l*1Rs{VC(Vk1Z4d-5V%5|4Jb~I~wY!7^S zI@oc%XHjRq_Z4}gH`A%&<3Ygmo zQf1}FdXnALtg=7TkxzP5ilek)pF&i%BUM4E9MP1zT9>2q@^YrvUydXB8YO+%N`e?D z7oGIlbRou%1D0{U-`h(jbS9AU#~OHr*AOT%Ek?)6@J4#IYmyN(LH~xQA?c&sl8$JU zbzrXHwZy6)9E9F*bS|y_Ye4I3AIE02mejQNe8!MWWB6;8(slg(O}HHwy9DHW@*V98 zyBd+QGfB}w2}sS-3%DoB0_kssy8e`0Q)?<0rb{>NdO?v6Nk8cYRk|W!KZHA`@qUxi dW%%&J?LSbdsx?0bL}vg1002ovPDHLkV1kgvuyg + + Icon/Warning + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/ColorPicker/index.tsx b/src/components/ColorPicker/index.tsx new file mode 100644 index 0000000..91c1b75 --- /dev/null +++ b/src/components/ColorPicker/index.tsx @@ -0,0 +1,184 @@ +import React, { useState, useEffect } from 'react'; +import reactCSS from 'reactcss'; +import { SketchPicker } from 'react-color'; +import { Divider } from 'antd'; +import { CheckOutlined } from '@ant-design/icons'; +import CustomStyle from './style.less'; +import Picker from '@/assets/png/picker.png' + +const colorPicker: React.FC = ({ value, onChange }) => { + + const [displayColorPicker, setDisplayColorPicker] = useState(false) + const [show, setShow] = useState(false) + const [color, setColor] = useState({ r: 255, g: 157, b: 78, a: 1 }) + const colors = [ + { r: 255, g: 157, b: 78, a: 1 }, + { r: 91, g: 216, b: 166, a: 1 }, + { r: 91, g: 143, b: 249, a: 1 }, + { r: 247, g: 102, b: 78, a: 1 }, + { r: 255, g: 134, b: 183, a: 1 }, + { r: 43, g: 158, b: 157, a: 1 }, + { r: 146, g: 112, b: 202, a: 1 }, + { r: 109, g: 200, b: 236, a: 1 }, + { r: 102, g: 119, b: 150, a: 1 }, + { r: 245, g: 188, b: 22, a: 1 }, + { r: 231, g: 107, b: 242, a: 1 }, + ] + + const handleClick = () => { + setDisplayColorPicker(!displayColorPicker) + }; + + const handleClose = () => { + setDisplayColorPicker(false) + }; + + const handleChange = (color: any) => { + const rgb = color.rgb + setColor(rgb) + const rgba = `rgb(${rgb.r},${rgb.g},${rgb.b},${rgb.a})` + if (onChange) { + onChange(rgba); + } + }; + + useEffect(() => { + let rgb = { r: 255, g: 157, b: 78, a: 1 } + if (value) { + const init = value.slice(4, value.length - 1).split(',') + rgb = { + r: Number(init[0]), + g: Number(init[1]), + b: Number(init[2]), + a: Number(init[3]) + } + } + setColor(rgb) + }, [value]); + + const onselect = (item: any) => { + setColor(item) + setShow(false) + const rgba = `rgb(${item.r},${item.g},${item.b},${item.a})` + if (onChange) { + onChange(rgba); + } + } + + const styles = reactCSS({ + 'default': { + color: { + position: 'relative', + width: '22px', + height: '22px', + borderRadius: '2px', + background: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`, + }, + swatch: { + // padding: '5px', + transform: 'translateY(5px)', + background: '#fff', + borderRadius: '1px', + boxShadow: '0 0 0 1px rgba(0,0,0,.1)', + display: 'inline-block', + cursor: 'pointer', + }, + popover: { + position: 'absolute', + zIndex: '2', + }, + cover: { + position: 'fixed', + top: '0px', + right: '0px', + bottom: '0px', + left: '0px', + }, + warp: { + position: 'absolute', + paddingTop: '16px', + paddingBottom: '6px', + background: '#fff', + boxShadow: '-12px 0 48px 16px rgba(0,0,0,0.03), -9px 0 28px 0 rgba(0,0,0,0.05), -6px 0 16px -8px rgba(0,0,0,0.08)', + borderRadius: '2px', + zIndex: 9999, + marginTop: 5, + } + }, + }); + + return ( +
+
+
+
+ {displayColorPicker ? +
+
+
+
+ { + colors.map((item, index) => { + return
+
onselect(item)} + > + {JSON.stringify(color) == JSON.stringify(item) && } +
+
+ }) + } +
+
setShow(!show)} + > + +
+
+
+
+ {show && } + {show &&
+ +
+ } +
+
: null + } +
+ ) +} + +export default colorPicker \ No newline at end of file diff --git a/src/components/ColorPicker/style.less b/src/components/ColorPicker/style.less new file mode 100644 index 0000000..6fd5418 --- /dev/null +++ b/src/components/ColorPicker/style.less @@ -0,0 +1,38 @@ +.picker{ + box-shadow: none !important; + border: none; +} + +.waves:active{ + opacity: 0.85; + transform: scale(.9, .9); + box-shadow:0 1px 2px 0 rgba(0,0,0,0.2), 0 1px 3px 0 rgba(0,0,0,0.19) +} +.waves:hover{ + opacity: 0.8; + box-shadow:0 1px 2px 0 rgba(0,0,0,0.2), 0 1px 3px 0 rgba(0,0,0,0.19) +} + +.waves:after { + content: ""; + display: block; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + pointer-events: none; + background-image: radial-gradient(circle, #aaa 10%, transparent 10%); + background-repeat: no-repeat; + background-position: 50%; + transform: scale(10, 10); + opacity: 0; + transition: .3s, opacity .5s; +} + +.waves:active:after { + transform: scale(0, 0); + opacity: 0.3; + transition: 0s; +} + diff --git a/src/components/CustomPagination.tsx b/src/components/CustomPagination.tsx new file mode 100644 index 0000000..e20e16d --- /dev/null +++ b/src/components/CustomPagination.tsx @@ -0,0 +1,36 @@ +import { Pagination , Row , Col } from 'antd' +import styled from 'styled-components' + +const CommonPagination = styled(Row)` + margin-top:16px; + width : 100%; +` + +interface PaginationProps { + total : number, + pageSize : number , + current : number, + onPageChange : ( page : number , size? : number ) => void, + style?:any, +} + +export default ( props : PaginationProps ) => { + const { total = 0 , pageSize = 10 , current = 1 , onPageChange , ...rest } = props + if ( total === 0 ) return + return ( + + + `共${ total }条`} + // hideOnSinglePage + onChange={ onPageChange } + /> + + + ) +} \ No newline at end of file diff --git a/src/components/Public/DelModal.tsx b/src/components/Public/DelModal.tsx new file mode 100644 index 0000000..5fb0cd7 --- /dev/null +++ b/src/components/Public/DelModal.tsx @@ -0,0 +1,71 @@ +import React from 'react' +import { Modal, Typography, Row } from "antd" +import { ReactComponent as Delete } from '@/assets/svg/Delete.svg'; +import styled from 'styled-components' +import { isQuestRight,requestCodeMessage, promiseRequest } from "@/utils" + +const ModalComponent = styled(Modal)` + .ant-typography-danger{ + margin-left: 4px; + color: #F5222D; + } + .ant-btn-primary{ + border-color: #FF4E50; + background: #FF4E50; + } + .ant-modal-title{ + font-weight: 700; + } +`; +type IProps = { + onOk: () => void; + deleteQuest: promiseRequest; +} + +type IRefs = { + [k: string]: any +} +const DelModal: React.ForwardRefRenderFunction = (props, ref) => { + const { onOk, deleteQuest } = props + const [visible, setVisible] = React.useState(false) + const [loading, setLoading] = React.useState(false) + const [operateId, setOperateId] = React.useState(undefined) + React.useImperativeHandle(ref, () => ({ + show(_: any) { + setOperateId(_) + setVisible(true) + } + })) + const handleOk = async () => { + if (loading) return + setLoading(true) + const {code,msg} = await deleteQuest(operateId) + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + if (isQuestRight(code)) { + hanldeCancel() + onOk() + } + } + const hanldeCancel = () => { + setVisible(false) + setLoading(false) + setOperateId(undefined) + } + return ( + + 该操作不可逆,请谨慎操作 + + ) +} + +export default React.forwardRef(DelModal) \ No newline at end of file diff --git a/src/components/Public/StatusTag.tsx b/src/components/Public/StatusTag.tsx new file mode 100644 index 0000000..39e2e55 --- /dev/null +++ b/src/components/Public/StatusTag.tsx @@ -0,0 +1,51 @@ +import React from "react" +import { Tag } from "antd" + +type IProps = { + state: string +} + +const stateWordsMap = new Map([ + ["init", "新建"], + ["analyzing", "分析中"], + ["running", "运行中"], + ["finish", "已完成"], + ["accepted", "已接受"], + ["refused", "已拒绝"], +]) + +const stateColorMap = new Map([ + ["init", "rgba(0,0,0,0.03)"], + ["analyzing", "rgba(250,173,21,0.1)"], + ["running", "rgba(24,144,255,0.1)"], + ["finish", "rgba(34,188,61,0.1)"], + ["accepted", "rgba(250,173,21,0.1)"], + ["refused", "rgba(255,77,79,0.1)"], +]) +const stateWordsColorMap = new Map([ + ["init", "rgba(0,0,0,0.45)"], + ["analyzing", "rgba(228,155,9,1)"], + ["running", "rgba(24,144,255,1)"], + ["finish", "rgba(34,188,61,1)"], + ["accepted", "rgba(228,155,9,1)"], + ["refused", "rgba(255,77,79,1)"], +]) + +const StateTag: React.FC = (props) => { + const { state } = props + return ( + + {stateWordsMap.get(state) || state} + + ) +} + +export default StateTag \ No newline at end of file diff --git a/src/components/RichTextEditor/index.tsx b/src/components/RichTextEditor/index.tsx index 388d0c8..7cb1a3b 100644 --- a/src/components/RichTextEditor/index.tsx +++ b/src/components/RichTextEditor/index.tsx @@ -27,11 +27,11 @@ const customUpload = async (file: File, insertFn: any) => { } const RichTextEditor: React.FC = (props) => { - const { defaultValue, onEditorChange, config } = props + const { defaultValue, onEditorChange, config = {} } = props const toolbar = React.useRef(null) const editor = React.useRef(null) as any - + React.useEffect(() => { const riceEditor = createEditor({ selector: editor.current, @@ -51,7 +51,7 @@ const RichTextEditor: React.FC = (props) => { selector: toolbar.current as any, config: toolbarConfig }) - // console.log(tb.getConfig().toolbarKeys) + onEditorChange && onEditorChange(vm) }, onChange(vm) { diff --git a/src/components/RichTextEditor/services.ts b/src/components/RichTextEditor/services.ts new file mode 100644 index 0000000..e03b04d --- /dev/null +++ b/src/components/RichTextEditor/services.ts @@ -0,0 +1,11 @@ + +import { request } from "umi" + +export const uploadFile = async ( formData: any) => { + return request(`/api/images`, { + method : 'post', + data: { + formData + } + }) +} \ No newline at end of file diff --git a/src/global.less b/src/global.less index d7b6301..27883ea 100644 --- a/src/global.less +++ b/src/global.less @@ -64,3 +64,7 @@ body { } } } +.mgContent{ + background-color: #fff; + padding: 0 12px 16px; +} diff --git a/src/pages/Demand/components/AddDemand.tsx b/src/pages/Demand/components/AddDemand.tsx index 9802ba3..f3457a4 100644 --- a/src/pages/Demand/components/AddDemand.tsx +++ b/src/pages/Demand/components/AddDemand.tsx @@ -1,92 +1,236 @@ import React from "react" -import { Modal, Form, Input, Space, Button, Row, Col, Divider, Select } from "antd" +import { Modal, Form, Input, Space, Button, Row, Col, Spin, Select, Typography } from "antd" import RichTextEditor from "@/components/RichTextEditor" - +import { getDemandDetail,getOutlineList,updateDemand, createDemand, queryTableList } from "../services" +import { requestCodeMessage, isQuestRight, requestNotTableFn, setFormFieldsValue } from "@/utils" +import StatusTag from "@/components/Public/StatusTag" +import { createEditor } from '@wangeditor/editor' +import { isArray, get } from "lodash" type IProps = { onOk: () => void; onCancel?: () => void; } - +type optionType = { + value: React.ReactText; + label: React.ReactText; +} +type promiseRequest = (values: any) => Promise; type IRefs = { [k: string]: any } const ReactComponent: React.ForwardRefRenderFunction = (props, ref) => { const { onOk } = props - const [visible, setVisible] = React.useState(false) - const [loading, setLoading] = React.useState(false) - const [source, setSource] = React.useState(undefined) + const [isLoading, setIsLoading] = React.useState(false) + const [isVeiw, setIsVeiw] = React.useState(false) + const [source, setSource] = React.useState(undefined) + const [selectedOutline, setSelectedOutline] = React.useState() + const [outlineOption, setOutlineOption] = React.useState([]) + const nameRef = React.useRef(null) as any + const textEditorRef = React.useRef(null) as any + const assigneeOption = [ + { value: 'owner', label: 'owner' }, + { value: 'tester', label: 'tester' }, + ] + const promiseQuest = async (questFn: promiseRequest, id?: number) => { + const result = await questFn(id) + const code = get(result, 'code') + const msg = get(result, 'msg') + const data = get(result, 'data') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + return data + } React.useImperativeHandle(ref, () => ({ - show(_: any) { - setSource(_) + show(_: any) { setVisible(true) - form.setFieldsValue(_) + setIsLoading(true) + setIsVeiw(Boolean(_?.id || _?.id === 0)) + Promise.all(_?.id || _?.id === 0 ? [promiseQuest(getOutlineList), promiseQuest(getDemandDetail, _?.id)] : [promiseQuest(getOutlineList)]) + .then((result) => { + setIsLoading(false) + const demandDetail = result[1] + const outlineList = result[0] + setSource(demandDetail) + form.setFieldsValue({ + assignee: demandDetail?.assignee, + outline_id: demandDetail?.outline_id, + title: demandDetail?.title, + }) + const textContent = (demandDetail?.content || '').trim() + const isObject = textContent.indexOf('[') === 0 && textContent.lastIndexOf(']') === textContent.length - 1 + console.log(isObject,'isObject') + console.log(demandDetail?.content,'demandDetail?.content') + + textEditorRef.current = isObject ? JSON.parse(demandDetail?.content) : [] + console.log(textEditorRef.current,'textEditorRef.current') + let arr: optionType[] = [] + if (isArray(outlineList)) { + arr = outlineList.map(item => ({ value: get(item, 'id'), label: get(item, 'title') })) + } + setOutlineOption(arr) + }) + .catch((err: any) => { + setIsLoading(false) + console.log(err) + }) } })) - + const [form] = Form.useForm() - + const handleEditorChange = (vm: any) => { + textEditorRef.current = vm + } + const handleOutlineChange = (keys:number) => { + setFormFieldsValue(form, { outline_id: keys }) + setSelectedOutline(keys) + } + const handleAssigneeChange = (keys:number) => { + setFormFieldsValue(form, { assignee: keys }) + setSelectedOutline(keys) + } + const handleEdit = () => setIsVeiw(false) const handleCancel = () => { + textEditorRef.current = null + form.resetFields() setVisible(false) - setLoading(false) + setIsLoading(false) setSource(undefined) + setIsVeiw(false) + setOutlineOption([]) + setSelectedOutline(undefined) } const handleOk = async () => { - if (loading) return - setLoading(true) - onOk() - handleCancel() - } + form.validateFields() // 触发表单验证,返回Promise + .then(async (values: any) => { + if (isLoading) return + setIsLoading(true) + let questType:promiseRequest = createDemand + const parmas = { + ...values, + } + parmas.content = JSON.stringify(textEditorRef.current.children) + if(isVeiw) parmas.title = source?.title + if(source) { + parmas.id = source?.id + questType = updateDemand + } + const result = await questType(parmas) + const code = get(result, 'code') + const msg = get(result, 'msg') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + if (isQuestRight(code)) { + onOk() + handleCancel() + } + }) + .catch(err => console.log(err)) + } + return ( - + } bodyStyle={{ padding: 0 }} onCancel={handleCancel} onOk={handleOk} > - - - - -
- -
-
- - -
- - - - - - -
- -
+ +
+ + + + {isVeiw ?
+ + + {source?.title} + {source?.status && } + + + + {source?.owner && + + {`${source?.owner} 创建于 ${source?.gmt_created || '-'}`} + {`更新于 ${source?.gmt_modified || '-'}`} + + } +
: + + + + } + + {!isVeiw ?
+ +
: +
+ } + + + + + + + + + + + + + + ) } -export default React.forwardRef(ReactComponent) \ No newline at end of file +export default React.forwardRef(ReactComponent) diff --git a/src/pages/Demand/index.tsx b/src/pages/Demand/index.tsx index 5727e27..a81dff2 100644 --- a/src/pages/Demand/index.tsx +++ b/src/pages/Demand/index.tsx @@ -1,20 +1,25 @@ -import React from "react" -import { Table, Space, Typography, Row, Button } from "antd" -import { queryTableList } from "./services" -import { useRequest } from "ahooks" +import { useState, useRef } from "react" +import { Table, Space, Typography, Row, Button, Layout } from "antd" +import { getDemandList, queryTableList, deleteDemand } from "./services" import AddModal from "./components/AddDemand" - -const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 } +import { requestFn, defaultParams } from "@/utils" +import { useUpdateEffect } from 'ahooks' +import CustomPagination from "@/components/CustomPagination" +import StatusTag from "@/components/Public/StatusTag" +import DelModal from "@/components/Public/DelModal" +import gCls from '@/global.less' +import _ from 'lodash' +const DEFAULT_PAGE_QUERY = { page_size: 10, page_num: 1 } const TestDemand: React.FC = () => { - const [pageQuery, setPageQuery] = React.useState(DEFAULT_PAGE_QUERY) - - const { data: dataSource, loading, refresh } = useRequest( - (params = pageQuery) => queryTableList(params), - { refreshDeps: [pageQuery] } - ) + const [questParmas, setQuestParmas] = useState(DEFAULT_PAGE_QUERY) + const { data: tableSource, loading, run, refresh } = requestFn(getDemandList, DEFAULT_PAGE_QUERY) - const addModalRef = React.useRef(null) as any + useUpdateEffect(() => { + run(questParmas) + }, [questParmas]) + const addModalRef = useRef(null) as any + const delModalRef = useRef(null) as any const handleAdd = async () => { addModalRef.current?.show() @@ -24,70 +29,114 @@ const TestDemand: React.FC = () => { addModalRef.current?.show(row) } - const handleDelete = async (row: any) => { + const handleDelete = (id: number) => { + delModalRef.current?.show(id) + } + const delCallback= async () => { + const parmasCopy = _.cloneDeep(questParmas) + const { page_size = 10, page_num = 1 } = parmasCopy + const totalPage: number = Math.ceil((_.get(tableSource, 'total') - 1) / page_size) || 1 + if (totalPage <= page_num) { + parmasCopy.page_num = totalPage + setQuestParmas(parmasCopy) + } else { + await refresh() + } } const columns = [{ title: "标题", - dataIndex: "", - }, { + dataIndex: 'title', + key: 'title', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "阶段", - dataIndex: "", - }, { + dataIndex: 'status', + key: 'status', + ellipsis: true, + render: (row: any) => row ? : '-' + }, + { title: "测试大纲", - dataIndex: "", - }, { + dataIndex: 'outline_title', + key: 'outline_title', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "创建人", - dataIndex: "", - }, { + dataIndex: "owner", + key: 'owner', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "指派人", - dataIndex: "", - }, { + dataIndex: "assignee", + key: 'assignee', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "创建日期", - dataIndex: "", - }, { + dataIndex: "gmt_created", + key: 'gmt_created', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "操作", + width: 100, render(row: any) { return ( handleEdit(row)}> 编辑 - handleDelete(row)}> + handleDelete(row?.id)}> 删除 ) } }] - + return ( - - - 测试需求 + + + {`测试需求 (${tableSource?.total || 0})`} + { + setQuestParmas({ ...questParmas, page_size: size, page_num: page }) } - }} + } /> - + + ) } -export default TestDemand \ No newline at end of file +export default TestDemand diff --git a/src/pages/Demand/services.ts b/src/pages/Demand/services.ts index 22287f7..64a14a5 100644 --- a/src/pages/Demand/services.ts +++ b/src/pages/Demand/services.ts @@ -1,9 +1,57 @@ +import React from "react"; import { request } from "umi" type DemandParams = { - + page_size: number; + page_num: number; } -export const queryTableList = async (data: DemandParams) => { - return request(`/api/xxxxxxx`, { method: "post", data }) -} \ No newline at end of file +export const queryTableList = async (params: DemandParams) => { + return request(`/project/public`, {params }) +} +// 鿴б +export const getDemandList = async ( params : DemandParams ) => { + return request(`/api/requirement`, { params }) +} +// 鿴 +export const getDemandDetail = async ( id : number ) => { + return request(`/api/requirement/${id}`) +} +// ǰ׺ҲԴ +export const getOutlineList = async ( prefix? : string ) => { + return request(`/api/outline/list`, { params: {prefix}}) +} +// ² +export const updateDemand = async ( data:{ + id: number, + title: string, + content ?: string, + assignee: React.ReactText, + outline_id ?: React.ReactText, +} ) => { + return request(`/api/requirement/edit/${data?.id}`, { + method : 'post', + data:{ + ...data, + id: undefined + } + }) +} +// +export const createDemand = async ( data:{ + title: string, + content ?: string, + assignee: React.ReactText, + outline_id ?: React.ReactText, +} ) => { + return request(`/api/requirement/create`, { + method : 'post', + data + }) +} +// ɾ +export const deleteDemand = async( id:number | null) => { + return request(`/api/requirement/${id}` , { + method : 'delete', + }) +} diff --git a/src/pages/Server/components/AddServerModal.tsx b/src/pages/Server/components/AddServerModal.tsx index 146c226..82a2934 100644 --- a/src/pages/Server/components/AddServerModal.tsx +++ b/src/pages/Server/components/AddServerModal.tsx @@ -1,6 +1,10 @@ import React from "react" import { Modal, Form, Input, Space, Button, Radio } from "antd" - +import { getDeviceDetail, getTagList, updateDevice, createDevice, queryTableList } from "../services" +import { requestCodeMessage, isQuestRight, requestNotTableFn, setFormFieldsValue } from "@/utils" +import { isArray, get } from "lodash" +import CheckboxSearchSelect from './CheckboxSearchSelect' +import ColorPicker from '@/components/ColorPicker' type IProps = { onOk: () => void; onCancel?: () => void; @@ -9,19 +13,54 @@ type IProps = { type IRefs = { [k: string]: any } - +type optionType = { + value: React.ReactText; + label: React.ReactText; +} +type promiseRequest = (values: any) => Promise; const ReactComponent: React.ForwardRefRenderFunction = (props, ref) => { const { onOk } = props const [visible, setVisible] = React.useState(false) const [loading, setLoading] = React.useState(false) const [source, setSource] = React.useState(undefined) + const [archValue, setArchValue] = React.useState('x86') + const [typeValue, setTypeValue] = React.useState('x86') + const promiseQuest = async (questFn: promiseRequest, id?: number) => { + const result = await questFn(id) + const code = get(result, 'code') + const msg = get(result, 'msg') + const data = get(result, 'data') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + return data + } React.useImperativeHandle(ref, () => ({ - show(_: any) { - setSource(_) + async show(_: any) { setVisible(true) - form.setFieldsValue(_) + if (_) { + setLoading(true) + const result = await getDeviceDetail(_?.id) + setLoading(false) + const code = get(result, 'code') + const msg = get(result, 'msg') + const deviceDetail = get(result, 'data') + console.log(deviceDetail, 'deviceDetail') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + if (isQuestRight(code)) { + setSource(deviceDetail) + // let tag = deviceDetail?.lable || '' + // tag = tag.split(',').filter((val:string|undefined) => val && val.trim()) + form.setFieldsValue({ + name: deviceDetail?.name, + ip: deviceDetail?.ip, + arch: deviceDetail?.arch, + type: deviceDetail?.type, + sn: deviceDetail?.sn, + lable: deviceDetail?.lable, + }) + } + } } })) @@ -31,15 +70,46 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re setVisible(false) setLoading(false) setSource(undefined) + form.setFieldsValue({ + arch: 'x86', + type: 'unlimit', + }) } const handleOk = async () => { - if (loading) return - setLoading(true) - onOk() - handleCancel() + + form.validateFields() // 触发表单验证,返回Promise + .then(async (values: any) => { + // console.log(values,'values') + if (loading) return + setLoading(true) + let questType: promiseRequest = createDevice + const parmas = { + ...values, + } + if (source) { + parmas.id = source?.id + questType = updateDevice + } + const result = await questType(parmas) + setLoading(false) + const code = get(result, 'code') + const msg = get(result, 'msg') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + if (isQuestRight(code)) { + onOk() + handleCancel() + } + }) + .catch(err => console.log(err)) } + const onArchChange = (e: any) => { + setArchValue(e.target.value); + } + const onTypeChange = (e: any) => { + setTypeValue(e.target.value); + } return ( = (props, re + {/* */} - x86_64 - aarch64 - loogarch64 + x86_64 + aarch64 + loogarch64 - + + {/* */} - vm - docker - 物理机 + 不限制 + VM + docker + 物理机 - - + + diff --git a/src/pages/Server/components/CheckboxSearchSelect.tsx b/src/pages/Server/components/CheckboxSearchSelect.tsx new file mode 100644 index 0000000..31348fc --- /dev/null +++ b/src/pages/Server/components/CheckboxSearchSelect.tsx @@ -0,0 +1,170 @@ + +import { Col, Empty, Divider, message, Checkbox, Select, Input, Space } from 'antd' +import React, { useState, useEffect, useRef } from 'react' +import { requestNotTableFn,requestCodeMessage, promiseRequest, isQuestRight } from "@/utils" +import { useUpdateEffect } from 'ahooks'; +import { addLabel } from "../services" +import styles from '../index.less' +import _ from 'lodash' +import classNames from 'classnames'; +import { PlusOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons' +import ColorPicker from '@/components/ColorPicker' +type MultipleSelectProp = { + dataIndex: string; + formReact: any; + questLink: promiseRequest; + placeholder ?: string; + effect ?: any; +} +type itemType = { + name: string; + color: string; +} +const MultipleSelect: React.FC = (props) => { + const { dataIndex, formReact, questLink, placeholder, effect } = props + const [checkedList, setCheckedList] = useState([]) + const [selectOptions, setSelectOptions] = useState([]) + // const [selectVal, setSelectVal] = useState('') + const [isButton, setIsButton] = useState(true) + const [inputVal,setInputVal] = useState('') + const [selColor,setSelColor] = useState('rgb(255,157,78)') + const { data: resultData} = requestNotTableFn(questLink,'',true) + + const lableSelect: any = useRef(null) + + useUpdateEffect(() =>{ + const {data} = resultData || {} + let arr:itemType[] = [] + if(_.isArray(data)) { + data.forEach((item:any) => { + if(_.get(item,'name').trim()) arr.push({name: _.get(item,'name').trim(), color: _.get(item,'color')}) + }) + } + setSelectOptions(arr) + },[resultData]) + useEffect(() => { + let arr = formReact.getFieldsValue()[dataIndex] || [] + arr = arr.map((val: itemType) => val?.name) + setCheckedList(arr) + }, [effect]) + + // const handleSelectSearch = (val: any) => { + // setSelectVal(val) + // } + const handleSelectChange = (value: any) => { + setCheckedList(value); + } + // const handleSelectBlur = () => { + // const lableNames = formReact.getFieldValue('lable') || [] + // if (selectVal) { + // formReact.setFieldsValue({ lable: lableNames.concat([selectVal]) }) + // setSelectVal('') + // lableSelect.current.blur() + // } + // } + const onChange = (list: any) => { + setCheckedList(list) + const newList = _.reduce(list, function(arr:itemType[],name) { + const target = _.find(selectOptions,{ name: name }) + if(target) arr.push(target) + return arr + }, []) + formReact.setFieldsValue({ lable: newList }) + } + const handleAddTag = () => setIsButton(!isButton) + const handleTagInput = (e:any) => setInputVal(e.target.value) + const handleChangeColor = (color:string) => { + setSelColor(color) + } + const handleAddOK = async () => { + const name = inputVal.trim() + if (!name) return message.warning('标签名称不能为空') + if (!selColor) return message.warning('标签颜色不能为空') + if(_.find(selectOptions,{ name: name })) return message.warning('标签名称不能重复') + const addTag = { name: name, color: selColor } + const { code, msg } = await addLabel({ name: name, color: selColor }) || {} + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') + if (isQuestRight(code)) { + const lableNames = formReact.getFieldValue('lable') || [] + formReact.setFieldsValue({ lable: lableNames.concat([{ name: name, color: selColor }]) }) + setCheckedList(checkedList.concat([name])) + setSelectOptions([addTag,...selectOptions]) + handleAddCancle() + } + } + const handleAddCancle = () => { + setInputVal('') + setSelColor('rgb(255,157,78)') + setIsButton(!isButton) + formReact.resetFields() + } + const optionsList = selectOptions.map(item => _.get(item,'name')) + // console.log(checkedList,'checkedList') + return ( + + + + + } + + )} + + /> + ) +} +export default MultipleSelect diff --git a/src/pages/Server/index.less b/src/pages/Server/index.less new file mode 100644 index 0000000..6f59d6e --- /dev/null +++ b/src/pages/Server/index.less @@ -0,0 +1,51 @@ +.pers_select{ + :global{ + .cb-row{ + margin-bottom: 4px; + } + } +} +.custom_style_select{ + :global{ + .ant-checkbox-wrapper{ + & > span:last-of-type{ + word-break: break-all; + } + } + } +} +.select_baseline { + :global(.ant-select-selection-search) { + // min-width: 5px; + } +} +.join_baseline{ + width: 100%; +} +.join_base_line{ + cursor: pointer; + color: rgba(0,0,0,0.65); + height : 32px ; + line-height : 32px ; +} +.tag_select{ + :global{ + .ant-checkbox{ + display: none; + } + .ant-checkbox-group-item{ + display: block; + width: 100%; + } + .ant-checkbox-wrapper-checked { + color: #1890FF; + background-color: #E6F7FF; + } + .ant-select-item-option-selected { + color : #1890FF; + font-weight: normal; + } + } + +} + diff --git a/src/pages/Server/index.tsx b/src/pages/Server/index.tsx index 1ff5038..c07398b 100644 --- a/src/pages/Server/index.tsx +++ b/src/pages/Server/index.tsx @@ -1,63 +1,133 @@ -import React from "react" -import { Table, Space, Typography, Row, Button } from "antd" -import { queryTableList } from "./services" -import { useRequest } from "ahooks" +import React,{useState} from "react" +import { Table, Space, Typography, Row, Button, Layout, Badge, Tag } from "antd" +import { queryTableList, deleteDevice, getDeviceList, getDevicePing } from "./services" + +import { requestFn, defaultParams,} from "@/utils" +import { useUpdateEffect } from 'ahooks' +import CustomPagination from "@/components/CustomPagination" import AddModal from "./components/AddServerModal" +import DelModal from "@/components/Public/DelModal" +import gCls from '@/global.less' +import { isArray, isString } from "lodash" const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 } const TableList: React.FC = () => { - const [pageQuery, setPageQuery] = React.useState(DEFAULT_PAGE_QUERY) + const [questParmas, setQuestParmas] = useState(DEFAULT_PAGE_QUERY) - const { data: dataSource, loading, refresh } = useRequest( - (params = pageQuery) => queryTableList(params), - { refreshDeps: [pageQuery] } - ) + const { data: tableSource, loading, run, refresh } = requestFn(getDeviceList, DEFAULT_PAGE_QUERY) + + useUpdateEffect(() => { + run(questParmas) + }, [questParmas]) const addModalRef = React.useRef(null) as any + const delModalRef = React.useRef(null) as any const handleAdd = async () => { addModalRef.current?.show() } - + const handlePing = (id: number) => { + + } const handleEdit = async (row: any) => { addModalRef.current?.show(row) } - const handleDelete = async (row: any) => { + const handleDelete = (id: number) => { + delModalRef.current?.show(id) + } + const delCallback= async () => { + const parmasCopy = _.cloneDeep(questParmas) + const { page_size = 10, page_num = 1 } = parmasCopy + const totalPage: number = Math.ceil((_.get(tableSource, 'total') - 1) / page_size) || 1 + if (totalPage <= page_num) { + parmasCopy.page_num = totalPage + setQuestParmas(parmasCopy) + } else { + await refresh() + } + } + const getSatus = (status:boolean) => { + if(status) return {color: 'green',text: '可用'} + return { color: 'red', text: '不可用' } + } + const getType = (type:string) => { + if(type === 'vm') return 'VM' + if(type === 'docker') return 'docker' + if(type === 'physics') return '物理机' + return '-' } const columns = [{ title: "名称", - dataIndex: "", - }, { + dataIndex: 'name', + key: 'name', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "状态", - dataIndex: "", - }, { + dataIndex: 'status', + key: 'status', + ellipsis: true, + render: (row: any) => row ? : '-' + }, + { title: "类型", - dataIndex: "", - }, { + dataIndex: 'type', + key: 'type', + ellipsis: true, + render: (row: any) => getType(row) + }, + { title: "IP", - dataIndex: "", - }, { + dataIndex: "ip", + key: 'ip', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "SN", - dataIndex: "", - }, { + dataIndex: "sn", + key: 'sn', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "标签", - dataIndex: "", - }, { + dataIndex: "label", + key: 'label', + ellipsis: true, + render: (row: any) => { + if(isArray(row)){ + return row.map((item:{name:string,color:string}) =>{item?.name}) + } + if(isString(row) && row) return {row} + return '-' + } + }, + { title: "创建人", - dataIndex: "", - }, { + dataIndex: "owner", + key: 'owner', + ellipsis: true, + render: (row: any) => row || '-' + }, + { title: "操作", + width: 120, render(row: any) { return ( + handlePing(row?.id)}> + 校验 + handleEdit(row)}> 编辑 - handleDelete(row)}> + handleDelete(row?.id)}> 删除 @@ -66,30 +136,37 @@ const TableList: React.FC = () => { }] return ( - - - 测试设备 - + + + {`测试需求 (${tableSource?.total || 0})`} +
+ { + setQuestParmas({ ...questParmas, page_size: size, page_num: page }) } - }} + } /> - + + ) } diff --git a/src/pages/Server/services.ts b/src/pages/Server/services.ts index d4b2252..13b9816 100644 --- a/src/pages/Server/services.ts +++ b/src/pages/Server/services.ts @@ -1,5 +1,76 @@ +import React from "react"; import { request } from "umi" -export const queryTableList = async () => { - return request(`/api/xxx`) +type DemandParams = { + page_size: number; + page_num: number; +} + +export const queryTableList = async (params: DemandParams) => { + return request(`/project/public`, {params }) +} +// 查看设备列表 +export const getDeviceList = async ( params : DemandParams ) => { + return request(`/api/device`, { params }) +} +// 查看单个设备详情 +export const getDeviceDetail = async ( id : number ) => { + return request(`/api/device/${id}`) +} +// 根据前缀查找设备标签 +export const getTagList = async ( prefix? : string ) => { + return request(`/api/device/labels`, { params: {prefix}}) +} +// 更新设备 +export const updateDevice = async ( data:{ + name: string, + ip: string, + arch: string, + type : string, + sn?: string, + lable?: any, + id: number +} ) => { + return request(`/api/device/modify/${data?.id}`, { + method : 'post', + data:{ + ...data, + id: undefined + } + }) +} +// 创建设备 +export const createDevice = async ( data:{ + name: string, + ip: string, + arch: string, + type : string, + sn?: string, + lable?: string, +} ) => { + return request(`/api/device/add`, { + method : 'post', + data + }) +} +// 删除设备 +export const deleteDevice = async( id:number | null) => { + return request(`/api/device/${id}` , { + method : 'delete', + }) +} +// 测试设备连通性(未实现) +export const getDevicePing = async ( id : number ) => { + return request(`/api/device/ping/${id}`) +} + +// 创建设备 +export const addLabel = async ( data:{ + name: string, + color: string, +} ) => { + return request(`/api/device/label/add`, { + method : 'post', + data + }) } \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index e69de29..dcef24a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -0,0 +1,78 @@ +import { useRequest, history } from 'umi' +import { message } from 'antd'; +import _ from 'lodash' + +type defaultParams = { + page_num?: number; + page_size?: number; + [k: string]: any; +} + +type promiseRequest = (values: any) => Promise; + +const requestFn = (querUrl: promiseRequest,questParams?:defaultParams) => { + return useRequest( + (data: defaultParams) => querUrl(data), + { + formatResult: (response:any) => response, + initialData: { data: [],total: 0 }, + defaultParams: questParams ? [questParams] : [{}], + // refreshDeps: [questParams], + onSuccess:(data: any) =>{ + const code = _.get(data,'code') + const msg = _.get(data,'msg') + if(!isQuestRight(code)) requestCodeMessage(code,msg || '쳣') + }, + onError:(error: Error) => { + requestCodeMessage(500,'쳣') + console.error(error) + }, + } + ) +} +const requestNotTableFn = (querUrl: promiseRequest,questParams ?:any, flag:boolean = false) => { + if(!querUrl) return { data: {}, loading: false, run: () => {}, refresh: () => {} } + return useRequest( + (data: any) => querUrl(questParams || data), + { + formatResult: (response:any) => response, + initialData: { data: {} }, + onSuccess:(data: any) =>{ + if(flag) return + const code = _.get(data,'code') + const msg = _.get(data,'msg') + if(!isQuestRight(code)) requestCodeMessage(code,msg || '쳣') + }, + onError:(error: Error) => { + requestCodeMessage(500,'쳣') + console.error(error) + }, + } + ) +} + +const isQuestRight = (code:number) => code >= 200 && code < 300 || code === 304 + +const requestCodeMessage = (code : number, msg : any) => { + // if(code === 404) return history.push('/404') + // if(code === 500) return history.push('/500') + // if(code === 401 || code === 403) return history.push('/403') + let mesg = msg + if(_.isObjectLike(msg)) mesg = JSON.stringify(msg) + if (isQuestRight(Number(code))) return message.success( mesg || 'ɹ' ) + return message.error( mesg || 'ʧ' ) +} +const setFormFieldsValue = (form: any, newObj: object) => { + const valuesClone = _.cloneDeep(form.getFieldsValue()) + form.setFieldsValue({ ...valuesClone, ...newObj }) +} + +export { + requestFn, + requestCodeMessage, + isQuestRight, + defaultParams, + promiseRequest, + requestNotTableFn, + setFormFieldsValue, +} -- Gitee From 12756ac7aeda665b49945271389c35d89d5e993c Mon Sep 17 00:00:00 2001 From: wulibaibao <13366578180@163.com> Date: Thu, 14 Apr 2022 20:31:27 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=B5=8B=E8=AF=95=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=EF=BC=8C=E6=B5=8B=E8=AF=95=E9=9C=80=E6=B1=82=E6=96=B0?= =?UTF-8?q?=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/pages/Demand/components/AddDemand.tsx | 44 +++++++++-------- src/pages/Outline/index.tsx | 6 +-- .../Server/components/AddServerModal.tsx | 8 +++- src/pages/Server/index.tsx | 46 +++++++++--------- src/pages/Task/index.tsx | 2 +- src/utils/index.ts | 48 +++++++++---------- 6 files changed, 80 insertions(+), 74 deletions(-) diff --git a/src/pages/Demand/components/AddDemand.tsx b/src/pages/Demand/components/AddDemand.tsx index f3457a4..981755d 100644 --- a/src/pages/Demand/components/AddDemand.tsx +++ b/src/pages/Demand/components/AddDemand.tsx @@ -1,7 +1,7 @@ import React from "react" import { Modal, Form, Input, Space, Button, Row, Col, Spin, Select, Typography } from "antd" import RichTextEditor from "@/components/RichTextEditor" -import { getDemandDetail,getOutlineList,updateDemand, createDemand, queryTableList } from "../services" +import { getDemandDetail, getOutlineList, updateDemand, createDemand, queryTableList } from "../services" import { requestCodeMessage, isQuestRight, requestNotTableFn, setFormFieldsValue } from "@/utils" import StatusTag from "@/components/Public/StatusTag" import { createEditor } from '@wangeditor/editor' @@ -43,7 +43,7 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re return data } React.useImperativeHandle(ref, () => ({ - show(_: any) { + show(_: any) { setVisible(true) setIsLoading(true) setIsVeiw(Boolean(_?.id || _?.id === 0)) @@ -60,11 +60,11 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re }) const textContent = (demandDetail?.content || '').trim() const isObject = textContent.indexOf('[') === 0 && textContent.lastIndexOf(']') === textContent.length - 1 - console.log(isObject,'isObject') - console.log(demandDetail?.content,'demandDetail?.content') - + console.log(isObject, 'isObject') + console.log(demandDetail?.content, 'demandDetail?.content') + textEditorRef.current = isObject ? JSON.parse(demandDetail?.content) : [] - console.log(textEditorRef.current,'textEditorRef.current') + console.log(textEditorRef.current, 'textEditorRef.current') let arr: optionType[] = [] if (isArray(outlineList)) { arr = outlineList.map(item => ({ value: get(item, 'id'), label: get(item, 'title') })) @@ -77,16 +77,16 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re }) } })) - + const [form] = Form.useForm() const handleEditorChange = (vm: any) => { textEditorRef.current = vm } - const handleOutlineChange = (keys:number) => { + const handleOutlineChange = (keys: number) => { setFormFieldsValue(form, { outline_id: keys }) setSelectedOutline(keys) } - const handleAssigneeChange = (keys:number) => { + const handleAssigneeChange = (keys: number) => { setFormFieldsValue(form, { assignee: keys }) setSelectedOutline(keys) } @@ -107,13 +107,13 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re .then(async (values: any) => { if (isLoading) return setIsLoading(true) - let questType:promiseRequest = createDemand + let questType: promiseRequest = createDemand const parmas = { ...values, } parmas.content = JSON.stringify(textEditorRef.current.children) - if(isVeiw) parmas.title = source?.title - if(source) { + if (isVeiw) parmas.title = source?.title + if (source) { parmas.id = source?.id questType = updateDemand } @@ -129,7 +129,7 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re .catch(err => console.log(err)) } - + return ( = (props, re {isVeiw ?
- - {source?.title} + + + {source?.title} + {source?.status && } - + {source?.owner && @@ -169,7 +171,7 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re } - + {!isVeiw ?
= (props, re onEditorChange={handleEditorChange} />
:
} - +
- - + + diff --git a/src/pages/Outline/index.tsx b/src/pages/Outline/index.tsx index 3c46f72..76f0c14 100644 --- a/src/pages/Outline/index.tsx +++ b/src/pages/Outline/index.tsx @@ -108,10 +108,10 @@ const TestDemand: React.FC = () => { ]; return ( - + - - 测试大纲 + + 测试大纲({dataSource?.total || 0}) - + } onCancel={handleCancel} @@ -127,6 +127,10 @@ const ReactComponent: React.ForwardRefRenderFunction = (props, re
diff --git a/src/pages/Server/index.tsx b/src/pages/Server/index.tsx index c07398b..89d4ef3 100644 --- a/src/pages/Server/index.tsx +++ b/src/pages/Server/index.tsx @@ -1,15 +1,15 @@ -import React,{useState} from "react" +import React, { useState } from "react" import { Table, Space, Typography, Row, Button, Layout, Badge, Tag } from "antd" import { queryTableList, deleteDevice, getDeviceList, getDevicePing } from "./services" -import { requestFn, defaultParams,} from "@/utils" +import { requestFn, defaultParams, } from "@/utils" import { useUpdateEffect } from 'ahooks' import CustomPagination from "@/components/CustomPagination" import AddModal from "./components/AddServerModal" import DelModal from "@/components/Public/DelModal" import gCls from '@/global.less' -import { isArray, isString } from "lodash" +import _, { isArray, isString } from "lodash" const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 } const TableList: React.FC = () => { @@ -28,7 +28,7 @@ const TableList: React.FC = () => { addModalRef.current?.show() } const handlePing = (id: number) => { - + } const handleEdit = async (row: any) => { addModalRef.current?.show(row) @@ -37,26 +37,26 @@ const TableList: React.FC = () => { const handleDelete = (id: number) => { delModalRef.current?.show(id) } - const delCallback= async () => { + const delCallback = async () => { const parmasCopy = _.cloneDeep(questParmas) - const { page_size = 10, page_num = 1 } = parmasCopy - const totalPage: number = Math.ceil((_.get(tableSource, 'total') - 1) / page_size) || 1 + const { page_size = 10, page_num = 1 } = parmasCopy + const totalPage: number = Math.ceil((_.get(tableSource, 'total') - 1) / page_size) || 1 - if (totalPage <= page_num) { - parmasCopy.page_num = totalPage - setQuestParmas(parmasCopy) - } else { - await refresh() - } + if (totalPage <= page_num) { + parmasCopy.page_num = totalPage + setQuestParmas(parmasCopy) + } else { + await refresh() + } } - const getSatus = (status:boolean) => { - if(status) return {color: 'green',text: '可用'} + const getSatus = (status: boolean) => { + if (status) return { color: 'green', text: '可用' } return { color: 'red', text: '不可用' } } - const getType = (type:string) => { - if(type === 'vm') return 'VM' - if(type === 'docker') return 'docker' - if(type === 'physics') return '物理机' + const getType = (type: string) => { + if (type === 'vm') return 'VM' + if (type === 'docker') return 'docker' + if (type === 'physics') return '物理机' return '-' } @@ -101,10 +101,10 @@ const TableList: React.FC = () => { key: 'label', ellipsis: true, render: (row: any) => { - if(isArray(row)){ - return row.map((item:{name:string,color:string}) =>{item?.name}) + if (isArray(row)) { + return row.map((item: { name: string, color: string }) => {item?.name}) } - if(isString(row) && row) return {row} + if (isString(row) && row) return {row} return '-' } }, @@ -138,7 +138,7 @@ const TableList: React.FC = () => { return ( - {`测试需求 (${tableSource?.total || 0})`} + {`测试设备 (${tableSource?.total || 0})`}
{ return ( - 测试任务 + 测试任务({dataSource?.total || 0})
Promise; -const requestFn = (querUrl: promiseRequest,questParams?:defaultParams) => { +const requestFn = (querUrl: promiseRequest, questParams?: defaultParams) => { return useRequest( (data: defaultParams) => querUrl(data), { - formatResult: (response:any) => response, - initialData: { data: [],total: 0 }, + formatResult: (response: any) => response, + initialData: { data: [], total: 0 }, defaultParams: questParams ? [questParams] : [{}], // refreshDeps: [questParams], - onSuccess:(data: any) =>{ - const code = _.get(data,'code') - const msg = _.get(data,'msg') - if(!isQuestRight(code)) requestCodeMessage(code,msg || '쳣') + onSuccess: (data: any) => { + const code = _.get(data, 'code') + const msg = _.get(data, 'msg') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '请求异常') }, - onError:(error: Error) => { - requestCodeMessage(500,'쳣') + onError: (error: Error) => { + requestCodeMessage(500, '网络异常') console.error(error) }, } ) } -const requestNotTableFn = (querUrl: promiseRequest,questParams ?:any, flag:boolean = false) => { - if(!querUrl) return { data: {}, loading: false, run: () => {}, refresh: () => {} } +const requestNotTableFn = (querUrl: promiseRequest, questParams?: any, flag: boolean = false) => { + if (!querUrl) return { data: {}, loading: false, run: () => { }, refresh: () => { } } return useRequest( (data: any) => querUrl(questParams || data), { - formatResult: (response:any) => response, + formatResult: (response: any) => response, initialData: { data: {} }, - onSuccess:(data: any) =>{ - if(flag) return - const code = _.get(data,'code') - const msg = _.get(data,'msg') - if(!isQuestRight(code)) requestCodeMessage(code,msg || '쳣') + onSuccess: (data: any) => { + if (flag) return + const code = _.get(data, 'code') + const msg = _.get(data, 'msg') + if (!isQuestRight(code)) requestCodeMessage(code, msg || '网络异常') }, - onError:(error: Error) => { - requestCodeMessage(500,'쳣') + onError: (error: Error) => { + requestCodeMessage(500, '网络异常') console.error(error) }, } ) } -const isQuestRight = (code:number) => code >= 200 && code < 300 || code === 304 +const isQuestRight = (code: number) => code >= 200 && code < 300 || code === 304 -const requestCodeMessage = (code : number, msg : any) => { +const requestCodeMessage = (code: number, msg: any) => { // if(code === 404) return history.push('/404') // if(code === 500) return history.push('/500') // if(code === 401 || code === 403) return history.push('/403') let mesg = msg - if(_.isObjectLike(msg)) mesg = JSON.stringify(msg) - if (isQuestRight(Number(code))) return message.success( mesg || 'ɹ' ) - return message.error( mesg || 'ʧ' ) + if (_.isObjectLike(msg)) mesg = JSON.stringify(msg) + if (isQuestRight(Number(code))) return message.success(mesg || '操作成功') + return message.error(mesg || '操作失败') } const setFormFieldsValue = (form: any, newObj: object) => { const valuesClone = _.cloneDeep(form.getFieldsValue()) -- Gitee From a1a78a4ea6d781db2e42ae8f1b0b79abfb5cbfaf Mon Sep 17 00:00:00 2001 From: wulibaibao <13366578180@163.com> Date: Thu, 14 Apr 2022 23:21:54 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E7=94=A8=E4=BE=8B=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4,=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E5=AF=BC=E8=88=AA=E4=BF=AE=E6=94=B9=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/routes.ts | 4 + src/app.tsx | 48 ++++++++++ .../assets/svg/case-n.svg | 0 .../assets/svg/demand-n.svg | 0 .../assets/svg/outline-n.svg | 0 .../assets/svg/plan-n.svg | 0 .../assets/svg/server-n.svg | 0 .../assets/svg/task-n.svg | 0 src/components/MenuItemRender/index.tsx | 40 ++++++++ src/components/MenuItemRender/styled.tsx | 28 ++++++ .../RightContent/AvatarDropdown.tsx | 2 + src/components/RightContent/index.tsx | 26 ++---- src/pages/Suite/components/LeftList/index.tsx | 5 +- .../components/RightContent/FilterForm.tsx | 39 ++++++-- .../Suite/components/RightContent/index.tsx | 64 ++++--------- src/pages/Suite/services.ts | 91 ++++++++++--------- 16 files changed, 227 insertions(+), 120 deletions(-) rename "src/assets/case/\347\224\250\344\276\213-n.svg" => src/assets/svg/case-n.svg (100%) rename "src/assets/case/\351\234\200\346\261\202-n.svg" => src/assets/svg/demand-n.svg (100%) rename "src/assets/case/\345\244\247\347\272\262-n.svg" => src/assets/svg/outline-n.svg (100%) rename "src/assets/case/\346\226\271\346\241\210-n.svg" => src/assets/svg/plan-n.svg (100%) rename "src/assets/case/\350\256\276\345\244\207-n.svg" => src/assets/svg/server-n.svg (100%) rename "src/assets/case/\344\273\273\345\212\241-n.svg" => src/assets/svg/task-n.svg (100%) create mode 100644 src/components/MenuItemRender/index.tsx create mode 100644 src/components/MenuItemRender/styled.tsx diff --git a/config/routes.ts b/config/routes.ts index 647b79f..136e589 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -57,6 +57,10 @@ name: "server", component: "./Server", }, + { + path: "*", + redirect: "/outline", + }, { component: "./404", }, diff --git a/src/app.tsx b/src/app.tsx index bbd61b5..f02f89c 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -6,6 +6,51 @@ import RightContent from '@/components/RightContent'; import Footer from '@/components/Footer'; import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; import { BookOutlined, LinkOutlined } from '@ant-design/icons'; +import menuItemRender from "@/components/MenuItemRender" +import styled from "styled-components" + +const HeaderWrapper = styled.div` + #logo a h1 { + color: #fff; + } + + .ant-pro-top-nav-header { + background-color: rgba(5,17,54,1); + } + .ant-pro-top-nav-header-menu .ant-menu.ant-menu-horizontal { + height: 43px; + } + + .ant-menu, + .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected {color: #fff;} + .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected::after { + border-bottom-color: #fff; + } + + .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover { + color: rgb(255 255 255 / 50%); + transition: unset; + border-bottom: 2px solid transparent; + } + + .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover { + &::after { + border-bottom: none; + } + } + + .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-active::after { + border-bottom: none; + } + + .ant-menu-horizontal > .ant-menu-item::after, .ant-menu-horizontal > .ant-menu-submenu::after { + &:hover { + transition: unset; + border-bottom: none; + } + } + +` const isDev = process.env.NODE_ENV === 'development'; const loginPath = '/user/login'; @@ -56,6 +101,9 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => { history.push(loginPath); } }, + headerTheme: "dark", + headerRender: (props, dom) => {dom}, + menuItemRender, contentStyle: { margin: 12, }, diff --git "a/src/assets/case/\347\224\250\344\276\213-n.svg" b/src/assets/svg/case-n.svg similarity index 100% rename from "src/assets/case/\347\224\250\344\276\213-n.svg" rename to src/assets/svg/case-n.svg diff --git "a/src/assets/case/\351\234\200\346\261\202-n.svg" b/src/assets/svg/demand-n.svg similarity index 100% rename from "src/assets/case/\351\234\200\346\261\202-n.svg" rename to src/assets/svg/demand-n.svg diff --git "a/src/assets/case/\345\244\247\347\272\262-n.svg" b/src/assets/svg/outline-n.svg similarity index 100% rename from "src/assets/case/\345\244\247\347\272\262-n.svg" rename to src/assets/svg/outline-n.svg diff --git "a/src/assets/case/\346\226\271\346\241\210-n.svg" b/src/assets/svg/plan-n.svg similarity index 100% rename from "src/assets/case/\346\226\271\346\241\210-n.svg" rename to src/assets/svg/plan-n.svg diff --git "a/src/assets/case/\350\256\276\345\244\207-n.svg" b/src/assets/svg/server-n.svg similarity index 100% rename from "src/assets/case/\350\256\276\345\244\207-n.svg" rename to src/assets/svg/server-n.svg diff --git "a/src/assets/case/\344\273\273\345\212\241-n.svg" b/src/assets/svg/task-n.svg similarity index 100% rename from "src/assets/case/\344\273\273\345\212\241-n.svg" rename to src/assets/svg/task-n.svg diff --git a/src/components/MenuItemRender/index.tsx b/src/components/MenuItemRender/index.tsx new file mode 100644 index 0000000..44cb65d --- /dev/null +++ b/src/components/MenuItemRender/index.tsx @@ -0,0 +1,40 @@ +import { IRoute, history, useLocation } from 'umi'; +import { MenuItemProps } from 'antd' +import type { BaseMenuProps } from '@ant-design/pro-layout/lib/components/SiderMenu/BaseMenu'; +import { Icon, ItemWrapper } from './styled' + +import { ReactComponent as Outline } from '@/assets/svg/outline-n.svg' +import { ReactComponent as Demand } from '@/assets/svg/demand-n.svg' +import { ReactComponent as Plan } from '@/assets/svg/plan-n.svg' +import { ReactComponent as Case } from '@/assets/svg/case-n.svg' +import { ReactComponent as Server } from '@/assets/svg/server-n.svg' +import { ReactComponent as Task } from '@/assets/svg/task-n.svg' + +const switchMenuIcon = ({ locale }: IRoute) => { + return new Map([ + [`menu.outline`, ], + ['menu.demand', ], + ['menu.plan', ], + ['menu.suite', ], + ['menu.server', ], + ['menu.task', ], + ]).get(locale) +} + +const menuItemRender = (props: MenuItemProps & IRoute, defaultDom: React.ReactNode, menuProps: BaseMenuProps) => { + const { pathname } = useLocation() + const { path } = props + + const reg = new RegExp(`^${path as string}`).test(pathname) + + return ( + history.push(path as string)} align="middle" > + + {switchMenuIcon(props)} + + {defaultDom} + + ) +} + +export default menuItemRender \ No newline at end of file diff --git a/src/components/MenuItemRender/styled.tsx b/src/components/MenuItemRender/styled.tsx new file mode 100644 index 0000000..8d6bcd9 --- /dev/null +++ b/src/components/MenuItemRender/styled.tsx @@ -0,0 +1,28 @@ +import styled from 'styled-components'; +import { Row } from 'antd' + +const activeSvgCls = ` + svg { + path { + fill: #fff!important; + fill-opacity : 1; + } + } +` + +export const Icon = styled.span<{ isActive: boolean }>` + margin-right:8px; + display: flex; + ${({ isActive }) => isActive && activeSvgCls} +` + +export const ItemWrapper = styled(Row)` + &:hover { + svg { + path { + fill: rgb(255 255 255 / 50%)!important; + fill-opacity : 1; + } + } + } +` \ No newline at end of file diff --git a/src/components/RightContent/AvatarDropdown.tsx b/src/components/RightContent/AvatarDropdown.tsx index d9e6731..7b7cff7 100644 --- a/src/components/RightContent/AvatarDropdown.tsx +++ b/src/components/RightContent/AvatarDropdown.tsx @@ -31,6 +31,7 @@ const loginOut = async () => { }; const AvatarDropdown: React.FC = ({ menu }) => { + return <> const { initialState, setInitialState } = useModel('@@initialState'); const onMenuClick = useCallback( @@ -58,6 +59,7 @@ const AvatarDropdown: React.FC = ({ menu }) => { ); + if (!initialState) { return loading; } diff --git a/src/components/RightContent/index.tsx b/src/components/RightContent/index.tsx index 6e478ab..8901e5f 100644 --- a/src/components/RightContent/index.tsx +++ b/src/components/RightContent/index.tsx @@ -30,30 +30,16 @@ const GlobalHeaderRight: React.FC = () => { placeholder="站内搜索" defaultValue="umi ui" options={[ - { - label: umi ui, - value: 'umi ui', - }, - { - label: Ant Design, - value: 'Ant Design', - }, - { - label: Pro Table, - value: 'Pro Table', - }, - { - label: Pro Layout, - value: 'Pro Layout', - }, - ]} // onSearch={value => { - // console.log('input', value); - // }} + + ]} + // onSearch={value => { + // console.log('input', value); + // }} /> { - window.open('https://pro.ant.design/docs/getting-started'); + // window.open('https://pro.ant.design/docs/getting-started'); }} > diff --git a/src/pages/Suite/components/LeftList/index.tsx b/src/pages/Suite/components/LeftList/index.tsx index 25f5402..b8b449a 100644 --- a/src/pages/Suite/components/LeftList/index.tsx +++ b/src/pages/Suite/components/LeftList/index.tsx @@ -28,7 +28,6 @@ const LeftList: React.FC = (props) => { React.useEffect(() => { getModules() - // dispath({ type: "update", payload: new Array(6).fill("").map((x, i) => ({ mod_id: i + 1, name: "默认分类" })) }) return () => { handleRestInput() } @@ -79,13 +78,13 @@ const LeftList: React.FC = (props) => { const handleRenameOk = async (id: string, name: string) => { if (!name) return handleRestInput() const { code, msg } = await renameModule(id, { name }) - if (code !== 200) return + if (code !== 200) return message.error(msg) await getModules() handleRestInput() } const handleActiveModule = (id?: null | string | undefined) => { - id ? + typeof id === "number" ? history.replace(`/cases/${id}`) : history.replace(`/cases`) } diff --git a/src/pages/Suite/components/RightContent/FilterForm.tsx b/src/pages/Suite/components/RightContent/FilterForm.tsx index a915f51..f8b806d 100644 --- a/src/pages/Suite/components/RightContent/FilterForm.tsx +++ b/src/pages/Suite/components/RightContent/FilterForm.tsx @@ -1,7 +1,8 @@ import React from "react" -import { Space, Input, Row, Button, Form, Col, Divider, Radio, FormItemProps } from "antd" +import { Space, Input, Row, Button, Form, Col, Divider, Radio, FormItemProps, DatePicker, Select } from "antd" import { runMethodOptions, runModelOptions, isAvailableOptions, serviceTypeOptions } from "@/pages/Suite/utils" import styled from "styled-components" +import moment from "moment" type SelectItem = { label: string; @@ -66,9 +67,17 @@ type IProps = { [k: string]: any } -const FilterForm: React.FC = (props) => { +type IRefs = { + [k: string]: any +} + +const dateFormat = 'YYYY-MM-DD'; + +const FilterForm: React.ForwardRefRenderFunction = (props, ref) => { const { onOk } = props + React.useImperativeHandle(ref, () => ({ reset: form.resetFields })) + const [form] = Form.useForm() const renderJson = [ { @@ -94,20 +103,32 @@ const FilterForm: React.FC = (props) => { { label: "用例名称", name: "name", + children: }, { label: "创建人", name: "creator", + children: - + } )) @@ -144,4 +167,4 @@ const FilterForm: React.FC = (props) => { ) } -export default FilterForm \ No newline at end of file +export default React.forwardRef(FilterForm) \ No newline at end of file diff --git a/src/pages/Suite/components/RightContent/index.tsx b/src/pages/Suite/components/RightContent/index.tsx index 54d34f8..77efdee 100644 --- a/src/pages/Suite/components/RightContent/index.tsx +++ b/src/pages/Suite/components/RightContent/index.tsx @@ -15,6 +15,7 @@ import { useParams } from "umi" import CaseChild from "@/pages/Suite/components/Case" import { useCaseProvider } from "../../provider" +import Loading from "@/components/Loading" type IProps = { [k: string]: any @@ -25,8 +26,7 @@ const RightContent: React.FC = (props) => { const defaultParams = { mod_id } const { refreshModules, state } = useCaseProvider() - - const { modules, caseCount } = state + const { modules } = state const [loading, setLoading] = React.useState(true) const [cases, setCases] = React.useState([]) @@ -43,23 +43,18 @@ const RightContent: React.FC = (props) => { const [pageParams, setPageParams] = React.useState(defaultParams) - const handleExportOk = () => { - refreshModules() - getModalCase(pageParams) - } - - const getModalCase = async (params: any) => { + const getModalCase = async (params: any = pageParams) => { setLoading(true) - const { code, msg, data } = await queryModalCases(params) + const { code, data } = await queryModalCases(params) if (code !== 200) { setLoading(false) return setCases([]) } setCases(data) if (data.length > 0) { - if (activeCase && activeCase.id) { + if (activeCase && typeof activeCase.id === "number") { const idx = data.findIndex((i: any) => i.id === activeCase.id) - if (!idx) + if (~idx) setActiveCase(data[idx]) } else setActiveCase(data[0]) @@ -67,45 +62,27 @@ const RightContent: React.FC = (props) => { setLoading(false) } - console.log(activeCase) - React.useEffect(() => { getModalCase({ mod_id }) - return () => { setCases([]) setLoading(true) + setInp("") setSelectCases([]) - setPageParams(defaultParams) setFilter(false) } }, [mod_id]) - const handleMoveOk = () => { - getModalCase(pageParams) - setSelectCases([]) - } - - const handleDeleteOk = () => { - getModalCase(pageParams) - setSelectCases([]) - } - - const handleCreateOk = () => { - getModalCase(pageParams) + const refreshCases = () => { refreshModules() - } - - const getFilterCase = async (params: any) => { - const { code, msg, data } = await queryCases(params) - if (code !== 200) { - return - } - setCases(data) + setSelectCases([]) + getModalCase({ ...pageParams, mod_id }) } const handleOkFilter = (vals: any) => { - getModalCase({ ...pageParams, ...vals }) + const params = { ...pageParams, mod_id, ...vals } + setPageParams(params) + getModalCase(params) } const exportExcel = async () => { @@ -176,7 +153,8 @@ const RightContent: React.FC = (props) => { value={inp} allowClear onChange={({ target }) => setInp(target.value)} - onSearch={() => handleOkFilter({ prefix: inp })} + onSearch={() => handleOkFilter({ key: inp })} + // onKeyUp={handleKeyup} /> setFilter(!filter)}> @@ -219,16 +197,14 @@ const RightContent: React.FC = (props) => { { loading && -
- -
+ } - - + + - - + + ) } diff --git a/src/pages/Suite/services.ts b/src/pages/Suite/services.ts index 5311ca5..5cfa5d1 100644 --- a/src/pages/Suite/services.ts +++ b/src/pages/Suite/services.ts @@ -1,122 +1,123 @@ -import {request} from "umi" +import { request } from "umi" export type ICase = { - name: string; - run_method: "manual" | "auto"; - run_model: "single" | "cluster"; - is_available: boolean; - base_fields: any; - tone_case?: any; - custom_field?: any; - parent?: any + name: string; + run_method: "manual" | "auto"; + run_model: "single" | "cluster"; + is_available: boolean; + base_fields: any; + tone_case?: any; + custom_field?: any; + parent?: any } //新增测试用例 export const createCases = async (data: ICase) => { - return request(`/api/case/create/`, {method: "post", data}) + return request(`/api/case/create/`, { method: "post", data }) } export type IModal = { - mod_id: any; - page_num?: any; - page_size?: any; + mod_id: any; + page_num?: any; + page_size?: any; } //查个每个模块下的测试用例 export const queryModalCases = async (params: IModal) => { - return request(`/api/case/`, {method: "get", params}) + return request(`/api/case/search/`, { method: "get", params }) } //重命名测试用例 export const renameCase = async (case_id: any, data: { name: string }) => { - return request(`/api/case/rename/${case_id}`, {method: "post", data}) + return request(`/api/case/rename/${case_id}`, { method: "post", data }) } //批量删除测试用例 export const deleteCases = async (params: { id: any }) => { - return request(`/api/case/`, {method: "delete", params}) + return request(`/api/case/`, { method: "delete", params }) } //移动测试用例 export const moveCase = async (data: { parent: any, cases: any[] }) => { - return request(`/api/case/move`, {method: "post", data}) + return request(`/api/case/move`, { method: "post", data }) } //更新测试用例 export const updateCase = async (case_id: string, data: ICase) => { - return request(`/api/case/edit/${case_id}`, {method: "post", data}) + return request(`/api/case/edit/${case_id}`, { method: "post", data }) } export type IModule = { - name: string; - level?: any; - parent?: any; + name: string; + level?: any; + parent?: any; } //创建分类模块 export const createModule = async (data: IModule) => { - return request(`/api/case/module/create`, {method: "post", data}) + return request(`/api/case/module/create`, { method: "post", data }) } //获得某一模块写的所有子模块 export const queryModules = async (node_id: any) => { - return request(`/api/case/module/${node_id}`) + return request(`/api/case/module/${node_id}`) } //根据前缀获得相同前缀的所有模块名称 export const queryModuleName = async (params: { start?: string }) => { - return request(`/api/case/modules`, {params}) + return request(`/api/case/modules`, { params }) } //重命名模块名称 export const renameModule = async (mod_id: any, data: { name: string }) => { - return request(`/api/case/module/rename/${mod_id}`, {method: "post", data}) + return request(`/api/case/module/rename/${mod_id}`, { method: "post", data }) } //移动模块路径 export const moveMudal = async (mod_id: any, data: { level: any, parent: any }) => { - return request(`/api/case/module/move/${mod_id}`, {method: "post", data}) + return request(`/api/case/module/move/${mod_id}`, { method: "post", data }) } //删除模块(只删除模块,模块用例移动到删除模块的父模块下) export const deleteModule = async (mod_id: string) => { - return request(`/api/case/module/${mod_id}`, {method: "delete"}) + return request(`/api/case/module/${mod_id}`, { method: "delete" }) } //从excel导入测试用例(只支持.xls,.xlsx) export const importExcelCase = async (data: { excel: any }) => { - return request(`/api/case/import/`, {method: "post", data}) + return request(`/api/case/import/`, { method: "post", data }) } type CaseSearchQuery = { - key: string; //查找的关键词 - page_size?: number;//页面大小,默认1 - page_num?: number;// 分页页数,默认50 - type?: string;//测试用例类型,功能测试,性能测试,默认全部 - run_method?: string;// 用例执行方式,默认全选 - run_model?: string;// 用例运行模式,默认全选 - is_available?: boolean;// 用例是否可用,默认全选 - device_type?: string;// 用例执行机器类型,默认全选 - creator?: string;// 创建人,默认不区分 - start_time?: string;// 开始时间 - end_time?: string;// 结束时间 + key: string; //查找的关键词 + page_size?: number;//页面大小,默认1 + page_num?: number;// 分页页数,默认50 + type?: string;//测试用例类型,功能测试,性能测试,默认全部 + run_method?: string;// 用例执行方式,默认全选 + run_model?: string;// 用例运行模式,默认全选 + is_available?: boolean;// 用例是否可用,默认全选 + device_type?: string;// 用例执行机器类型,默认全选 + creator?: string;// 创建人,默认不区分 + start_time?: string;// 开始时间 + end_time?: string;// 结束时间 + mod_id?: string; } //查找用例 export const queryCases = async (params: CaseSearchQuery) => { - return request(`/api/case/search/`, {params}) + return request(`/api/case/`, { params }) } export const querySuiteCase = async (params: any) => { - return request(`/tone/api/case/workspace/case/`, {params: {...params, ws_id: "s63u44w7"}}) + return request(`/tone/api/case/workspace/case/`, { params: { ...params, ws_id: "s63u44w7" } }) } -export const queryDomainList = async (params: any) => { - return request(`/tone/api/case/test_domain/?test_type=domainconf`) +export const queryDomainList = async () => { + return request(`/tone/api/case/test_domain/?test_type=domainconf`) } export const downloadCaseTempFile = async () => { - return request(`/api/case/export`, {method: 'get', responseType: 'blob'}) + return request(`/api/case/export`, { method: 'get', responseType: 'blob' }) } export const exportCases = async (cases: any) => { - return request(`/api/case/export`, {method: 'get', params: {case: cases}, responseType: 'blob'}) + return request(`/api/case/export`, { method: 'get', params: { case: cases }, responseType: 'blob' }) } -- Gitee