@@ -118,7 +118,7 @@ const AvatarDropdown: React.FC
= ({ menu }) => {
access.isMember() &&
<>
{menu && }
-
+
申请测试管理员
diff --git a/src/components/RightContent/Token.tsx b/src/components/RightContent/Token.tsx
index 3ceb728da1b46894f0b16b5c68ef88b0d19c1109..31909751e14a17d2cf559f1ed86836a869b82d96 100644
--- a/src/components/RightContent/Token.tsx
+++ b/src/components/RightContent/Token.tsx
@@ -8,6 +8,7 @@ import { ReactComponent as View } from "@/assets/rightContent/view.svg"
import { ReactComponent as Refresh } from "@/assets/rightContent/refresh.svg"
import { ReactComponent as Off } from "@/assets/rightContent/view-off.svg"
import { ReactComponent as Copy } from "@/assets/rightContent/copy-fill-line.svg"
+import { copyText } from "@/utils"
const PointerSpan = styled.span`
@@ -39,7 +40,7 @@ const Token: React.FC = () => {
}
const handleCopy = () => {
-
+ copyText(initialState?.currentUser?.token)
}
return (
diff --git a/src/pages/Auth/Login/index.tsx b/src/pages/Auth/Login/index.tsx
index e4dd0fac6e728bdd1c2da3ccc022338f1e1db5e5..c52d3fbe48153529f02511ca267dd95502495d87 100644
--- a/src/pages/Auth/Login/index.tsx
+++ b/src/pages/Auth/Login/index.tsx
@@ -7,10 +7,9 @@ import { login } from "@/pages/Auth/services"
import { BasicForm } from "@/pages/Auth/styled"
const LoginPage: React.FC = () => {
- const [form] = Form.useForm()
-
const { setInitialState, initialState } = useModel("@@initialState")
const [loading, setLoading] = React.useState(false)
+ const [form] = Form.useForm()
const handleLogin = () => {
if (loading) return
@@ -67,7 +66,7 @@ const LoginPage: React.FC = () => {
rules={[
{
required: true,
- message: "请输入密码"
+ message: "请输入密码",
}
]}
>
@@ -98,7 +97,7 @@ const LoginPage: React.FC = () => {
还没有账号,去
- 注册
+ 注册
diff --git a/src/pages/Auth/Regist/index.tsx b/src/pages/Auth/Regist/index.tsx
index 497d0c5314794848749d12c07e230170d680652f..d511046e45aa32ed5b40e3b376f6773557584bb0 100644
--- a/src/pages/Auth/Regist/index.tsx
+++ b/src/pages/Auth/Regist/index.tsx
@@ -150,7 +150,7 @@ const RigistPage: React.FC = () => {
已有账号,去
- 登录
+ 登录
diff --git a/src/pages/Auth/components/Layout.tsx b/src/pages/Auth/components/Layout.tsx
index c762cc5d67ec11cff5fd3daf2b392c32be2ad63f..d544c7df6fa23f1b61ed109e77dcf0991139ab7e 100644
--- a/src/pages/Auth/components/Layout.tsx
+++ b/src/pages/Auth/components/Layout.tsx
@@ -8,7 +8,7 @@ import bg from "@/assets/auth/bg0.png"
const Container = styled.div`
width: 100%;
height: 100%;
- padding: 3%;
+ padding: 6%;
position: relative;
background: url(${bg}) no-repeat left center / 100% 100%;
`
diff --git a/src/pages/Suite/components/LeftList/index.tsx b/src/pages/Suite/components/LeftList/index.tsx
index 09f8cb18ae6c3edacfdf0f7fa3c0faf459e9550a..b1f77020766df3d919df310726b42af5edb7e0b8 100644
--- a/src/pages/Suite/components/LeftList/index.tsx
+++ b/src/pages/Suite/components/LeftList/index.tsx
@@ -1,55 +1,44 @@
import React from "react"
-import { Space, message, Row, Typography, Dropdown, Menu, Tooltip } from "antd"
+import { message, Row, Typography, Dropdown, Menu, Tree } from "antd"
import { PlusOutlined, MoreOutlined } from "@ant-design/icons"
import { createModule, renameModule, deleteModule } from "@/pages/Suite/services"
import { useCaseProvider } from "../../provider"
import { history, useParams } from "umi"
-import { ModuleItem, ModulesContainer, FuncModuleIcon } from "./styled"
+import { ModuleItem } from "./styled"
import ConfirmInput from "@/components/Public/ConfirmInput"
-import { useSize } from "ahooks"
import DeleteModal from "@/pages/Outline/components/DeleteModal"
+import { queryModules } from "@/pages/Suite/services"
+import styled from "styled-components"
-const ResizeRow: React.FC = (props) => {
- const { style, name, count } = props
- const countRef = React.useRef(null) as any
- const row = React.useRef(null) as any
- const countSize = useSize(countRef)
- const rowSize = useSize(row)
+const SuiteTree = styled(Tree)`
- const width = React.useMemo(() => {
- if (rowSize && countSize) return rowSize.width - countSize.width - 1
- return 0
- }, [rowSize, countSize])
+`
- return (
-
-
-
-
- {name}
-
-
-
-
-
- {`(${count || 0})`}
-
-
-
- )
-}
+const OptBlock = styled.div`
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ visibility: hidden;
+ cursor: pointer;
+`
+
+const BlockRow = styled(Row)`
+ &:hover {
+ ${OptBlock} {
+ visibility: visible;
+ }
+ }
+`
type IProps = {
[k: string]: any
}
const LeftList: React.FC = (props) => {
- const { state, refreshModules } = useCaseProvider()
+ const { state, refreshModules, dispath } = useCaseProvider()
const { mod_id } = useParams() as any
- const { modules, caseCount } = state
+ const { modules } = state
const [edit, setEdit] = React.useState(false)
const [rename, setRename] = React.useState(null)
@@ -72,21 +61,47 @@ const LeftList: React.FC = (props) => {
setEdit(true)
}
- const handleCreateModuleOk = async (name: string) => {
- if (!name) return
- const { code, msg, data } = await createModule({ name, parent: 0, level: 0 })
+ const handleCreateModuleOk = async (params: { name: string, parent: number, level: number }) => {
+ const { code, msg, data } = await createModule(params)
if (code !== 200) {
message.error(msg)
return
}
- refreshModules()
- setEdit(false)
+ console.log(data)
+ return data
+ }
+
+ const setModuleItem = (list: any, id: number, level: number, data: any) => {
+ return list.reduce((p: any, c: any) => {
+ if (level === c.level && id === c.id)
+ return p.concat({ ...c, children: data })
+ if (c.children)
+ return p.concat({ ...c, children: setModuleItem(c.children, id, level, data) })
+ return p.concat(c)
+ }, [])
}
- const handleMenuClick = (e: any, id: string) => {
+ const createChildModule = async (row: any) => {
+ const data: any = handleCreateModuleOk({ name: "新分类", parent: row.id, level: row.level })
+ if (data) {
+ const parentData = await getModuleData(row.id)
+ const newData = setModuleItem(modules, row.id, row.level, parentData)
+ console.log(newData)
+ data.id && setRename(`${data.id}-${data.level}`)
+ dispath({
+ type: "update",
+ payload: {
+ modules: newData
+ }
+ })
+ }
+ }
+
+ const handleMenuClick = (e: any, row: any) => {
switch (e.key) {
- case "rename": return setRename(id);
- case "delete": return deleteModalRef.current.show(id);
+ case "rename": return setRename(`${row.id}-${row.level}`);
+ case "delete": return deleteModalRef.current.show(row.id);
+ case "create": return createChildModule(row)
}
}
@@ -114,6 +129,100 @@ const LeftList: React.FC = (props) => {
history.replace(`/cases`)
}
+ const [treeExpanedKeys, setTreeExpandedKeys] = React.useState([])
+
+ const onExpand = (expandedKeys: any) => {
+ setTreeExpandedKeys(expandedKeys)
+ };
+
+ const transChildren = (list: any) => {
+ return list.reduce((p: any, c: any) => {
+ const $key = `${c.id}-${c.level}`
+ const base = {
+ ...c,
+ title: (
+
+ {
+ $key !== rename ?
+ <>
+ {c.name}
+ handleMenuClick(event, c)}
+ >
+ 重命名
+ 新建子模块
+ 删除
+
+ }
+ >
+
+
+
+
+ > :
+ handleRenameOk(c.id, val)}
+ onCancel={() => setRename(undefined)}
+ defaultValue={c.name}
+ />
+ }
+
+
+ ),
+ key: $key,
+ isLeaf: !c.children_nums
+ }
+ if (c.children)
+ return p.concat({
+ ...base,
+ children: transChildren(c.children)
+ })
+ return p.concat(base)
+ }, [])
+ }
+
+ const treeData = React.useMemo(() => {
+ return transChildren(modules)
+ }, [modules, rename])
+
+ const getModuleData = async (id: number) => {
+ const { data, code, msg } = await queryModules(id)
+ if (code !== 200) {
+ message.error(msg)
+ return false
+ }
+ return data
+ }
+
+ const transModuleChildren = (list: any, key: string, data: any) => {
+ return list.reduce((p: any, c: any) => {
+ const $key = `${c.id}-${c.level}`
+ if ($key === key)
+ return p.concat({ ...c, children: data })
+ if (c.children)
+ return p.concat({ ...c, children: transModuleChildren(c.children, key, data) })
+ return p.concat(c)
+ }, [])
+ }
+
+ const loadData = async (note: any) => {
+ const { id, level, children } = note
+ if (children) return Promise.resolve()
+ const data = await getModuleData(id)
+ if (data && !!data.length) {
+ const newData = transModuleChildren(modules, `${id}-${level}`, data)
+ dispath({
+ type: "update",
+ payload: {
+ modules: newData
+ }
+ })
+ }
+ return Promise.resolve()
+ }
+
return (
@@ -130,60 +239,18 @@ const LeftList: React.FC = (props) => {
is_active={mod_id === undefined}
onClick={() => handleActiveModule(undefined)}
>
-
-
- 所有用例
- {`(${caseCount || 0})`}
-
+ 所有用例
-
- {
- modules.map((mod: any) => (
- handleActiveModule(mod.id)}
- is_child
- >
-
- {
- rename === mod.id ?
- handleRenameOk(mod.id, val)}
- onCancel={() => setRename(null)}
- /> :
- <>
-
- handleMenuClick(e, mod.id)}>
-
- 重命名
-
-
- 删除
-
-
- }
- >
-
-
-
-
- >
- }
-
- ))
- }
-
+
{
edit &&
diff --git a/src/pages/Suite/index.tsx b/src/pages/Suite/index.tsx
index 1d699c56113660d7fc01579a91d9094c4ca0f449..5857d8656994dc5b9e76a28823c295f60e460376 100644
--- a/src/pages/Suite/index.tsx
+++ b/src/pages/Suite/index.tsx
@@ -5,13 +5,14 @@ import RightContent from "./components/RightContent"
import { Provider as CaseProvider } from "./provider"
import { queryModules } from "@/pages/Suite/services"
+import { message } from "antd"
type IProps = {
[k: string]: any;
}
const TableList: React.FC = (props) => {
- const initialState = { modules: [], caseCount: 0 }
+ const initialState = { modules: [] }/* , caseCount: 0 */
const [state, dispath] = React.useReducer((state: any, action: any) => {
switch (action.type) {
@@ -21,9 +22,12 @@ const TableList: React.FC = (props) => {
}, initialState)
const refreshModules = async () => {
- const { data, code, count } = await queryModules(0)
- if (code !== 200) return dispath({ type: "update", payload: { modules: [], caseCount: 0 } })
- dispath({ type: "update", payload: { modules: data, caseCount: count } })
+ const { data, code, msg } = await queryModules(0)
+ if (code !== 200) {
+ return message.error(msg)
+ /* return dispath({ type: "update", payload: { modules: [], caseCount: 0 } }) */
+ }
+ dispath({ type: "update", payload: { modules: data } })
}
return (
diff --git a/src/pages/Sys/ApprovalAndRecord/Approval/index.tsx b/src/pages/Sys/ApprovalAndRecord/Approval/index.tsx
index 381ff2feb7df3d8fd4bffa60f13ded01fffef701..473b48c0463e2542179b3a1f24ffb258c5998118 100644
--- a/src/pages/Sys/ApprovalAndRecord/Approval/index.tsx
+++ b/src/pages/Sys/ApprovalAndRecord/Approval/index.tsx
@@ -1,10 +1,25 @@
import React from "react"
-import { Table, Space, Typography, TableColumnProps, message } from "antd"
+import { Table, Space, Typography, TableColumnProps, message, Row, Button } from "antd"
import { queryList, reviewRoleApply } from "./services"
import { useRequest } from "ahooks"
+import styled from "styled-components"
+
+const BatchOptionRow = styled(Row)`
+ height: 48px;
+ background: #fff;
+ position: absolute;
+ width: 100%;
+ left: 0;
+ bottom: 0;
+ box-shadow: 0 -9px 28px 0 rgba(0,0,0,0.05);
+ padding: 0 20px;
+`
const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 }
+type ReviewItem = { review_id: number, user_id: number }
+type ReviewResult = "pass" | "fail"
+
const TableList: React.FC = () => {
const [pageQuery, setPageQuery] = React.useState(DEFAULT_PAGE_QUERY)
const [pendding, setPendding] = React.useState(false)
@@ -15,17 +30,30 @@ const TableList: React.FC = () => {
{ refreshDeps: [pageQuery] }
)
- const handleReview = async (row: any, review_result: "pass" | "fail") => {
+ const handleReview = async (row: any, review_result: ReviewResult) => {
+ reviewApply([{ review_id: row.id, user_id: row.applicant_id }], review_result)
+ }
+
+ const reviewApply = async (review_list: ReviewItem[], review_result: ReviewResult) => {
if (pendding) return
setPendding(true)
const { code, msg } = await reviewRoleApply({
- review_list: [{ review_id: row.id, user_id: row.user_id }],
+ review_list,
review_result
})
setPendding(false)
if (code !== 200) return message.error(msg)
message.success("操作成功!")
refresh()
+ setSelectedRowKeys([])
+ }
+
+ const batchReview = async (review_result: ReviewResult) => {
+ const review_list = selectedRowKeys.reduce((pre: any[], cur: number) => {
+ const t = dataSource.data.filter((i: any) => i.id === cur)
+ return pre.concat(t)
+ }, []).map(({ id, applicant_id }: any) => ({ review_id: id, user_id: applicant_id }))
+ reviewApply(review_list, review_result)
}
const columns: TableColumnProps[] = [{
@@ -51,7 +79,7 @@ const TableList: React.FC = () => {
handleReview(row, "pass")}>
通过
- handleReview(row, "fail")}>
+ handleReview(row, "fail")} style={{ cursor: "pointer" }}>
拒绝
@@ -80,6 +108,22 @@ const TableList: React.FC = () => {
}
}}
/>
+ {
+ !!selectedRowKeys.length &&
+
+
+ 已选{selectedRowKeys.length}项
+ setSelectedRowKeys([])}>取消
+
+
+
+
+
+
+ }
>
)
}
diff --git a/src/pages/Sys/ApprovalAndRecord/Record/index.tsx b/src/pages/Sys/ApprovalAndRecord/Record/index.tsx
index 98a98723c3f4616e39b6d056c35f695fd0683331..319d829e70a33dddbe8a91056bc6f40d911be9b4 100644
--- a/src/pages/Sys/ApprovalAndRecord/Record/index.tsx
+++ b/src/pages/Sys/ApprovalAndRecord/Record/index.tsx
@@ -2,6 +2,7 @@ import React from "react"
import { Table, TableColumnProps, Badge } from "antd"
import { queryList } from "./services"
import { useRequest } from "ahooks"
+import UserAvatarColumn from "@/components/Public/UserAvatarColumn"
const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 }
@@ -16,20 +17,20 @@ const TableList: React.FC = () => {
const columns: TableColumnProps[] = [{
title: "申请角色",
render() {
- return "系统管理员"
+ return "测试人员"
}
}, {
title: "申请人",
- render() {
- return "系统管理员"
+ render(row) {
+ return
}
}, {
title: "申请时间",
dataIndex: "gmt_created"
}, {
title: "审批人",
- render() {
- return "系统管理员"
+ render(row) {
+ return
}
}, {
title: "审批时间",
@@ -38,8 +39,8 @@ const TableList: React.FC = () => {
title: "审批结果",
render(row: any) {
if (row.review_result === "pass")
- return
- return
+ return
+ return
}
}]
diff --git a/src/pages/Sys/Users/index.tsx b/src/pages/Sys/Users/index.tsx
index c3aebd9808fee03707f41b8d6fe966fa16caf68b..3e34bda16c60ab2a92e7378c7a5ccd5963853634 100644
--- a/src/pages/Sys/Users/index.tsx
+++ b/src/pages/Sys/Users/index.tsx
@@ -1,10 +1,11 @@
import React from "react"
-import { Table, Space, Typography, Row, Button, Select, message, TableColumnProps } from "antd"
+import { Table, Space, Typography, Row, Menu, Select, message, TableColumnProps, Dropdown } from "antd"
import { deleteUser, queryList, updateUserRole } from "./services"
import { useRequest } from "ahooks"
import { useIntl } from "umi"
import UserAvatarColumn from "@/components/Public/UserAvatarColumn"
import { userRoleMap } from "@/utils"
+import { DownOutlined } from "@ant-design/icons"
const DEFAULT_PAGE_QUERY = { page_size: 20, page_num: 1 }
@@ -42,7 +43,7 @@ const TableList: React.FC = (props) => {
const handleUserRoleChange = async (role: string, row: any) => {
if (pendding) return
setPendding(true)
- const { code, msg } = await updateUserRole({ user_id: row.id, role: localRoleToKey.get(role) || "", method: "" })
+ const { code, msg } = await updateUserRole({ user_id: row.id, role, method: role === "senior" ? "upgrade" : "downgrade" })
setPendding(false)
if (code !== 200) return message.error(msg)
message.success("操作成功!")
@@ -62,26 +63,38 @@ const TableList: React.FC = (props) => {
dataIndex: "role",
render(_: string, row: any) {
return (
-
+
+ {userRoleMap.get(row.role)}
+
+
+
)
}
}, {
diff --git a/src/pages/Sys/index.tsx b/src/pages/Sys/index.tsx
index 4b1652c51c68b25048f50acb5caac7b304aa3b31..c3e1c20cb92fb2aea6372e50e370a19dc833026e 100644
--- a/src/pages/Sys/index.tsx
+++ b/src/pages/Sys/index.tsx
@@ -19,7 +19,7 @@ const SysContainer: React.FC = (props) => {
}, [pathname, route])
return (
-
+
-
+
{
...options,
headers: {
...options.headers,
- "X-Sanic-Token": localStorage.getItem("auth_token"),
- // "testLib": localStorage.getItem("auth_token"),
+ // "X-Sanic-Token": localStorage.getItem("auth_token"),
+ "testLib": localStorage.getItem("auth_token"),
},
},
};
diff --git a/src/utils/index.ts b/src/utils/index.ts
index ca3a26fff0054fff38eda3f2f58cad74e05f2787..7ab21646229ae33e4cf7cf7f754a450d7448ee5f 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,6 +1,26 @@
import { useRequest, history } from 'umi'
import { message } from 'antd';
import _ from 'lodash'
+import Clipboard from "clipboard"
+
+export const copyText = (text?: string) => {
+ if (!text) return
+ const dom = document.createElement("a")
+ dom.style.width = "0px";
+ dom.style.height = "0px"
+ document.body.appendChild(dom)
+ const cp = new Clipboard(dom, {
+ text: () => text
+ })
+
+ cp.on("success", () => {
+ message.success("已复制到剪切板!")
+ })
+
+ dom.click()
+ cp.destroy()
+ document.body.removeChild(dom)
+}
type defaultParams = {
page_num?: number;