Commit 1d752bdf authored by wuyongsheng's avatar wuyongsheng

Merge branch 'feat-20220801' into 'master'

Feat 20220801

See merge request !55
parents d3439787 eedd4ab0
This diff is collapsed.
<!--
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-02 11:43:28
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-10 10:40:49
* @FilePath: /bkunyun/public/index.html
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="cn"> <html lang="cn">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/facicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
...@@ -24,7 +32,7 @@ ...@@ -24,7 +32,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>平台</title> <title>北鲲云-工作流平台</title>
<script src="%PUBLIC_URL%/RDKit_minimal.js"></script> <script src="%PUBLIC_URL%/RDKit_minimal.js"></script>
<script> <script>
window window
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-13 09:56:57 * @Date: 2022-06-13 09:56:57
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-10 13:47:53 * @LastEditTime: 2022-08-15 17:10:49
* @FilePath: /bkunyun/src/api/api_manager.ts * @FilePath: /bkunyun/src/api/api_manager.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import { BACKEND_API_URI_PREFIX } from "./api_prefix"; import { BACKEND_API_URI_PREFIX } from "./api_prefix";
import { APIOPTION } from './fileserver/raysyncApi'
const RESTAPI = { const RESTAPI = {
API_USER_FETCH: `${BACKEND_API_URI_PREFIX}/accounts/current`, //获取账户信息 API_USER_FETCH: `${BACKEND_API_URI_PREFIX}/accounts/current`, //获取账户信息
...@@ -31,6 +32,7 @@ const RESTAPI = { ...@@ -31,6 +32,7 @@ const RESTAPI = {
API_WORKBENCH_ADD_TEMPLATE: `${BACKEND_API_URI_PREFIX}/cpp/workbench/project/workflowspec`, //项目管理员-添加工作流模板-提交 API_WORKBENCH_ADD_TEMPLATE: `${BACKEND_API_URI_PREFIX}/cpp/workbench/project/workflowspec`, //项目管理员-添加工作流模板-提交
API_FETCH_TEMPLATE_INFO: `${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowspec`, //点击使用模版查看模版详情 API_FETCH_TEMPLATE_INFO: `${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowspec`, //点击使用模版查看模版详情
API_WORK_FLOW_JOB: `${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowjob`, //点击任务列表查看任务详情 API_WORK_FLOW_JOB: `${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowjob`, //点击任务列表查看任务详情
API_FLOW_OUTPUT_NUMBER: `${ APIOPTION() }/readstreamnum`, //任务列表查看流输出数据集数量
API_WORKBENCH_WORKFLOWJOB_LIST: `${BACKEND_API_URI_PREFIX}/cpp/workbench/project/workflowjob`, //查询工作流任务 API_WORKBENCH_WORKFLOWJOB_LIST: `${BACKEND_API_URI_PREFIX}/cpp/workbench/project/workflowjob`, //查询工作流任务
API_WORKBENCH_DEL_WORKFLOWJOB: `${BACKEND_API_URI_PREFIX}/cpp/workflow/job/`, //删除工作流任务 API_WORKBENCH_DEL_WORKFLOWJOB: `${BACKEND_API_URI_PREFIX}/cpp/workflow/job/`, //删除工作流任务
API_WORKBENCH_CANCEL_WORKFLOWJOB: `${BACKEND_API_URI_PREFIX}/cpp/workflow/cancel`, //取消工作流 API_WORKBENCH_CANCEL_WORKFLOWJOB: `${BACKEND_API_URI_PREFIX}/cpp/workflow/cancel`, //取消工作流
...@@ -41,6 +43,8 @@ const RESTAPI = { ...@@ -41,6 +43,8 @@ const RESTAPI = {
API_SAVE_USERSPEC:`${BACKEND_API_URI_PREFIX}/cpp/workflow/saveuserspec`, // 保存用户自定义工作流模板 API_SAVE_USERSPEC:`${BACKEND_API_URI_PREFIX}/cpp/workflow/saveuserspec`, // 保存用户自定义工作流模板
API_OVERVIEW_GET:`${BACKEND_API_URI_PREFIX}/cpp/basicInformation`, // 获取概览基本信息 API_OVERVIEW_GET:`${BACKEND_API_URI_PREFIX}/cpp/basicInformation`, // 获取概览基本信息
API_TASK_OVERVIEW_LIST:`${BACKEND_API_URI_PREFIX}/cpp/workflowJobInformation`, // 查询任务概览 API_TASK_OVERVIEW_LIST:`${BACKEND_API_URI_PREFIX}/cpp/workflowJobInformation`, // 查询任务概览
API_OPERATOR_LISTSTREAMACTORS:`${BACKEND_API_URI_PREFIX}/cpp/workflow/liststreamactors`, // 获取流算子列表,可用于模糊查询,返回所有版本流算子
API_SAVE_BATCHACTOR:`${BACKEND_API_URI_PREFIX}/cpp/workflow/savebatchactor`, // 保存批算子
}; };
export default RESTAPI; export default RESTAPI;
...@@ -30,6 +30,15 @@ type IGetDatasetItemsListParams = { ...@@ -30,6 +30,15 @@ type IGetDatasetItemsListParams = {
sort?: string; // 排序 sort?: string; // 排序
} }
type IGetDatasetSizeParams = {
type?: string; // 产品id
projectId: string;
token: string;
filetoken: string;
path: string; // 数据集路径
name: string; // 数据集名称
}
type ISaveDatasetParams = { type ISaveDatasetParams = {
type?: string; type?: string;
projectId: string; projectId: string;
...@@ -229,6 +238,34 @@ class CloudEController { ...@@ -229,6 +238,34 @@ class CloudEController {
} }
} }
// 获取文件文本内容(字符串)
static JobFileDownloadText(url: any, filetoken: string, projectId: string) {
if (ApiUtils.getAuthorizationHeaders(headers)) {
url = url + urlToken(filetoken, projectId);
headers["Cache-Control"] = "no-cache";
headers["Content-Type"] = "multipart/form-data";
return axios.get(
APIOPTION() + "/download" + url + "&showhidden=false",
{
headers: headers,
}
);
}
}
// 获取dataset条数
static GetDatasetSize(params: IGetDatasetSizeParams) {
if (ApiUtils.getAuthorizationHeaders(headers)) {
headers["Cache-Control"] = "no-cache";
let url = getUrlThroughParams(params, ['filetoken','path','token'], '?')
return axios.get(
`${APIOPTION()}:5003/size${url}`,
{
headers: headers,
}
);
}
}
// 获取分子列表 // 获取分子列表
static GetDatasetItemsList(params: IGetDatasetItemsListParams) { static GetDatasetItemsList(params: IGetDatasetItemsListParams) {
......
...@@ -221,6 +221,15 @@ const fetchWorkFlowJob = (params: { id: string }) => { ...@@ -221,6 +221,15 @@ const fetchWorkFlowJob = (params: { id: string }) => {
}); });
}; };
// 工作列表详情获取流输出数量
const fetchFlowOutputNumber = (params: any) => {
return request({
url: Api.API_FLOW_OUTPUT_NUMBER,
method: "get",
params
});
};
type submitWorkFlowParams = { type submitWorkFlowParams = {
name: string; name: string;
projectId: string; projectId: string;
...@@ -270,6 +279,44 @@ const getTaskOverview=(params:getTaskOverviewParams)=>{ ...@@ -270,6 +279,44 @@ const getTaskOverview=(params:getTaskOverviewParams)=>{
}) })
} }
// 获取流算子列表,可用于模糊查询,返回所有版本流算子
type getOperatorListParams = {
productId: string;
keyword?: string;
page?: number;
size?: number;
};
const getOperatorList=(params:getOperatorListParams)=>{
return request({
url:Api.API_OPERATOR_LISTSTREAMACTORS,
method:"get",
params,
})
}
type saveBatchQuery = {
productId: string;
batchName: string;
batchVersion: string;
description?: string;
isEdit?: string;
}
type saveBatchBody = Array<any>
type saveBatchParams = {
query: saveBatchQuery;
body: saveBatchBody;
}
// 提交工作流
const saveBatchActor = (params: saveBatchParams) => {
return request({
url: Api.API_SAVE_BATCHACTOR,
method: "post",
params: params.query,
data: params.body,
});
};
export { export {
current, current,
...@@ -292,5 +339,8 @@ export { ...@@ -292,5 +339,8 @@ export {
submitWorkFlow, submitWorkFlow,
getworkFlowTaskInfo, getworkFlowTaskInfo,
getOverviewInfo, getOverviewInfo,
getTaskOverview getTaskOverview,
getOperatorList,
saveBatchActor,
fetchFlowOutputNumber
}; };
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-05 14:00:37 * @Date: 2022-07-05 14:00:37
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-10 13:54:04 * @LastEditTime: 2022-08-08 16:34:52
* @FilePath: /bkunyun/src/api/workbench_api.ts * @FilePath: /bkunyun/src/api/workbench_api.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import request from "@/utils/axios/service"; import request from "@/utils/axios/service";
import Api from "./api_manager"; import Api from "./api_manager";
import { IGetOperatorListParams, IFetchOperatorListParams } from "./workbenchInterface"; import { IGetOperatorListParams } from "./workbenchInterface";
function current() { function current() {
return request({ return request({
...@@ -139,15 +139,6 @@ const fetchOperatorList = (params: IGetOperatorListParams) => { ...@@ -139,15 +139,6 @@ const fetchOperatorList = (params: IGetOperatorListParams) => {
}; };
// 获取指定版本算子
const fetchVersionOperator = (params: IFetchOperatorListParams) => {
return request({
url: Api.API_VERSION_OPERATOR,
method: "get",
params,
});
};
// 保存用户自定义工作流模板 // 保存用户自定义工作流模板
const saveUserSpec = (params: any) => { const saveUserSpec = (params: any) => {
return request({ return request({
...@@ -168,6 +159,5 @@ export { ...@@ -168,6 +159,5 @@ export {
deleteWorkflowJob, deleteWorkflowJob,
cancelWorkflowJob, cancelWorkflowJob,
fetchOperatorList, fetchOperatorList,
fetchVersionOperator,
saveUserSpec saveUserSpec
}; };
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 12</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-12">
<rect id="背景" x="0" y="0" width="16" height="16"></rect>
<g id="项目管理" transform="translate(1.000000, 0.500000)" stroke="#8A9099" stroke-width="1.60714286">
<path d="M9.40680569,0.803571429 L13.1232896,4.61571277 L13.1964286,13.9285714 L1.07142857,14.1964286 L0.803571429,1.07142857 L9.40680569,0.803571429 Z" id="矩形"></path>
<line x1="3.69384615" y1="5.25" x2="8.00153846" y2="5.25" id="路径-2"></line>
<line x1="3.69384615" y1="9.53571429" x2="10.1553846" y2="9.53571429" id="路径-2备份"></line>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 12备份 8</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-12备份-8">
<rect id="背景" x="0" y="0" width="16" height="16"></rect>
<g id="项目管理" transform="translate(1.000000, 0.500000)" stroke="#1370FF" stroke-width="1.60714286">
<path d="M9.40680569,0.803571429 L13.1232896,4.61571277 L13.1964286,13.9285714 L1.07142857,14.1964286 L0.803571429,1.07142857 L9.40680569,0.803571429 Z" id="矩形"></path>
<line x1="3.69384615" y1="5.25" x2="8.00153846" y2="5.25" id="路径-2"></line>
<line x1="3.69384615" y1="9.53571429" x2="10.1553846" y2="9.53571429" id="路径-2备份"></line>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="17px" height="17px" viewBox="0 0 17 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组">
<rect id="矩形备份-4" x="0" y="0.242640687" width="16" height="16"></rect>
<g id="编组-4备份" transform="translate(1.000000, 0.000000)" stroke="#8A9099" stroke-width="1.5">
<rect id="矩形" x="0.75" y="1.99264069" width="4.5" height="4.5" rx="1"></rect>
<rect id="矩形备份-2" x="0.75" y="9.99264069" width="4.5" height="4.5" rx="1"></rect>
<rect id="矩形备份" transform="translate(10.892641, 4.242641) rotate(-45.000000) translate(-10.892641, -4.242641) " x="8.64264069" y="1.99264069" width="4.5" height="4.5" rx="1"></rect>
<rect id="矩形备份-3" x="8.5" y="9.99264069" width="4.5" height="4.5" rx="1"></rect>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="17px" height="17px" viewBox="0 0 17 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组备份 3</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组备份-3">
<rect id="矩形备份-4" x="0" y="0.242640687" width="16" height="16"></rect>
<g id="编组-4备份" transform="translate(1.000000, 0.000000)" stroke="#1370FF" stroke-width="1.5">
<rect id="矩形" x="0.75" y="1.99264069" width="4.5" height="4.5" rx="1"></rect>
<rect id="矩形备份-2" x="0.75" y="9.99264069" width="4.5" height="4.5" rx="1"></rect>
<rect id="矩形备份" transform="translate(10.892641, 4.242641) rotate(-45.000000) translate(-10.892641, -4.242641) " x="8.64264069" y="1.99264069" width="4.5" height="4.5" rx="1"></rect>
<rect id="矩形备份-3" x="8.5" y="9.99264069" width="4.5" height="4.5" rx="1"></rect>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 58</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-56">
<rect id="背景" x="0" y="0" width="16" height="16"></rect>
<g id="项目设置" stroke="#8A9099" stroke-width="1.5">
<path d="M5.58808928,0.759546858 C6.24087167,1.18742985 7.27894484,2.03357391 8.0410091,2.03357391 C8.79391996,2.03357391 9.82900874,1.20312298 10.1257116,0.879260616 L10.1257116,0.879260616 L12.4223498,1.92673302 C12.4838159,2.4170648 12.2821914,2.95405099 12.2821914,3.4239819 C12.2821914,4.19023092 12.5792307,4.88335657 13.0540612,5.3859324 C13.5439168,5.90441126 14.2215221,6.22104975 14.9662484,6.22104975 C15.1008851,6.51473263 15.25,7.34188133 15.25,8.00533082 C15.25,8.66737979 15.1010676,9.49361375 15.0943199,9.53058939 C14.2354924,9.80169918 13.5671599,10.1121377 13.083231,10.6039797 C12.5858984,11.1094446 12.2822113,11.8099862 12.2822113,12.5854135 C12.2822113,13.0633674 12.4859773,13.5981268 12.5031644,13.6416351 L12.5031644,13.6416351 L10.5347562,15.1859987 C9.8023742,14.7872629 8.74070743,13.8890234 7.95899092,13.8890234 C7.23777174,13.8890234 6.20467319,14.6831976 5.82852375,15.113358 L5.82852375,15.113358 L3.57620168,14.0817426 C3.50474061,13.6209845 3.71780868,13.0708966 3.71780868,12.5853924 C3.71780868,11.8193033 3.42075488,11.1262121 2.94586849,10.6236497 C2.45602443,10.1052579 1.77845122,9.7886411 1.03377167,9.7886411 C0.897650311,9.48798028 0.75,8.66482749 0.75,8.0053097 C0.75,7.34441337 0.897848614,6.52032512 0.905715983,6.47853298 C1.76432975,6.20788019 2.43260143,5.89750044 2.91648052,5.40589587 C3.4140514,4.90038092 3.71780868,4.19971123 3.71780868,3.42400299 C3.71780868,2.94206588 3.51060559,2.40259827 3.49603335,2.36594194 L3.49603335,2.36594194 Z" id="路径" fill-rule="nonzero"></path>
<circle id="椭圆形备份" cx="8" cy="8" r="2.45"></circle>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 56备份 2</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-56备份">
<rect id="背景" x="0" y="0" width="16" height="16"></rect>
<g id="项目设置" stroke="#1370FF" stroke-width="1.5">
<path d="M5.58808928,0.759546858 C6.24087167,1.18742985 7.27894484,2.03357391 8.0410091,2.03357391 C8.79391996,2.03357391 9.82900874,1.20312298 10.1257116,0.879260616 L10.1257116,0.879260616 L12.4223498,1.92673302 C12.4838159,2.4170648 12.2821914,2.95405099 12.2821914,3.4239819 C12.2821914,4.19023092 12.5792307,4.88335657 13.0540612,5.3859324 C13.5439168,5.90441126 14.2215221,6.22104975 14.9662484,6.22104975 C15.1008851,6.51473263 15.25,7.34188133 15.25,8.00533082 C15.25,8.66737979 15.1010676,9.49361375 15.0943199,9.53058939 C14.2354924,9.80169918 13.5671599,10.1121377 13.083231,10.6039797 C12.5858984,11.1094446 12.2822113,11.8099862 12.2822113,12.5854135 C12.2822113,13.0633674 12.4859773,13.5981268 12.5031644,13.6416351 L12.5031644,13.6416351 L10.5347562,15.1859987 C9.8023742,14.7872629 8.74070743,13.8890234 7.95899092,13.8890234 C7.23777174,13.8890234 6.20467319,14.6831976 5.82852375,15.113358 L5.82852375,15.113358 L3.57620168,14.0817426 C3.50474061,13.6209845 3.71780868,13.0708966 3.71780868,12.5853924 C3.71780868,11.8193033 3.42075488,11.1262121 2.94586849,10.6236497 C2.45602443,10.1052579 1.77845122,9.7886411 1.03377167,9.7886411 C0.897650311,9.48798028 0.75,8.66482749 0.75,8.0053097 C0.75,7.34441337 0.897848614,6.52032512 0.905715983,6.47853298 C1.76432975,6.20788019 2.43260143,5.89750044 2.91648052,5.40589587 C3.4140514,4.90038092 3.71780868,4.19971123 3.71780868,3.42400299 C3.71780868,2.94206588 3.51060559,2.40259827 3.49603335,2.36594194 L3.49603335,2.36594194 Z" id="路径" fill-rule="nonzero"></path>
<circle id="椭圆形备份" cx="8" cy="8" r="2.45"></circle>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 57</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-57">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<g id="工作台" transform="translate(0.500000, 1.500000)" stroke="#8A9099" stroke-width="1.5">
<rect id="矩形" x="0.75" y="0.75" width="13.5" height="8.5" rx="1"></rect>
<line x1="2.5" y1="12" x2="12.5" y2="12" id="路径-3"></line>
<line x1="11.5" y1="6" x2="6.5" y2="6" id="路径-4备份"></line>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 62</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-62">
<rect id="矩形备份-22" x="0" y="0" width="16" height="16"></rect>
<g id="工作台备份" transform="translate(0.500000, 1.500000)" stroke="#1370FF" stroke-width="1.5">
<rect id="矩形" x="0.75" y="0.75" width="13.5" height="8.5" rx="1"></rect>
<line x1="2.5" y1="12" x2="12.5" y2="12" id="路径-3"></line>
<line x1="11.5" y1="6" x2="6.5" y2="6" id="路径-4备份"></line>
</g>
</g>
</g>
</svg>
\ No newline at end of file
/* 修改滚动条样式 */ /* 修改滚动条样式 */
div::-webkit-scrollbar, div::-webkit-scrollbar,
main::-webkit-scrollbar { main::-webkit-scrollbar {
width: 8px; width: 4px;
height: 5px !important; height: 5px !important;
} }
...@@ -15,11 +15,12 @@ main::-webkit-scrollbar-track { ...@@ -15,11 +15,12 @@ main::-webkit-scrollbar-track {
div::-webkit-scrollbar-thumb, div::-webkit-scrollbar-thumb,
main::-webkit-scrollbar-thumb { main::-webkit-scrollbar-thumb {
background-color: #d8d8d8; background-color: #C2C6CC;
-webkit-border-radius: 4px; -webkit-border-radius: 2px;
-moz-border-radius: 4px; -moz-border-radius: 2px;
border-radius: 4px; border-radius: 2px;
height: 5px !important; height: 5px !important;
width: 4px!important;
} }
body, body,
......
.FSBox { .FSBox {
width: 900px; width: 900px;
height: 650px;
position: relative; position: relative;
} }
......
...@@ -12,16 +12,20 @@ import AddIcon from "@mui/icons-material/Add"; ...@@ -12,16 +12,20 @@ import AddIcon from "@mui/icons-material/Add";
import AddProject from "@/views/Project/components/AddProject"; import AddProject from "@/views/Project/components/AddProject";
import React, { useState } from "react"; import React, { useState } from "react";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import { toJS } from "mobx";
import { observer } from "mobx-react-lite";
import { useStores } from "@/store/index";
const NoProject = () => { const NoProject = observer((props: any) => {
// 新建弹窗显示控制 // 新建弹窗显示控制
const [addOpen, setAddOpen] = useState(false); const [addOpen, setAddOpen] = useState(false);
const { currentProjectStore } = useStores();
let projectList = toJS(currentProjectStore.projectList)
return ( return (
<div className={style.noProject}> <div className={style.noProject}>
<img src={noData} alt="" className={style.noDataImg} /> <img src={noData} alt="" className={style.noDataImg} />
<div className={style.text1}>当前产品暂无项目</div> <div className={style.text1}>{projectList.length > 0 ? "当前未选中任何项目" : "当前产品暂无项目"}</div>
<div className={style.text2}>请先创建项目</div> <div className={style.text2}>{projectList.length > 0 ? "或创建新项目" : "请先创建项目"}</div>
<MyButton <MyButton
text='创建项目' text='创建项目'
variant="contained" variant="contained"
...@@ -30,11 +34,11 @@ const NoProject = () => { ...@@ -30,11 +34,11 @@ const NoProject = () => {
onClick={() => setAddOpen(true)} onClick={() => setAddOpen(true)}
style={{ backgroundColor: "#1370ff", color: "#fff" }} style={{ backgroundColor: "#1370ff", color: "#fff" }}
/> />
<AddProject addOpen={addOpen} setAddOpen={setAddOpen} /> <AddProject addOpen={addOpen} setAddOpen={setAddOpen} />
</div> </div>
); );
}; });
export default NoProject; export default NoProject;
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-02 11:43:28
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-03 14:07:23
* @FilePath: /bkunyun/src/components/BusinessComponents/SearchInput/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import OutlinedInput, { OutlinedInputProps } from "@mui/material/OutlinedInput"; import OutlinedInput, { OutlinedInputProps } from "@mui/material/OutlinedInput";
import { ThemeProvider, createTheme } from "@mui/material/styles"; import { ThemeProvider, createTheme } from "@mui/material/styles";
import SearchIcon from "@mui/icons-material/Search"; import SearchIcon from "@mui/icons-material/Search";
...@@ -30,6 +38,9 @@ const SearchInput = (props: SearchInputProps) => { ...@@ -30,6 +38,9 @@ const SearchInput = (props: SearchInputProps) => {
height: "32px", height: "32px",
fontSize: "14px", fontSize: "14px",
paddingRight: "8px", paddingRight: "8px",
"& .MuiOutlinedInput-notchedOutline": {
borderColor: '#DDE1E6',
},
":hover": { ":hover": {
"& .MuiOutlinedInput-notchedOutline": { "& .MuiOutlinedInput-notchedOutline": {
borderColor: "#1370ff", borderColor: "#1370ff",
......
...@@ -8,13 +8,10 @@ ...@@ -8,13 +8,10 @@
z-index: 100; z-index: 100;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
animation: showBG 0.31s ease; animation: showBG 0.31s ease;
-webkit-animation: showBG 0.31s ease; -webkit-animation: showBG 0.31s ease;
} }
.drawerBoxHidden {
animation: hiddenBG 0.31s ease;
-webkit-animation: hiddenBG 0.31s ease;
}
.closeBox { .closeBox {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
...@@ -23,13 +20,6 @@ ...@@ -23,13 +20,6 @@
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
align-items: center;
animation: showClose 0.31s ease;
-webkit-animation: showClose 0.31s ease;
}
.closeBoxHidden {
animation: hiddenClose 0.31s ease;
-webkit-animation: hiddenClose 0.31s ease;
} }
.closeiIcon { .closeiIcon {
position: absolute; position: absolute;
...@@ -44,17 +34,14 @@ ...@@ -44,17 +34,14 @@
position: fixed; position: fixed;
top: 40px; top: 40px;
left: 0; left: 0;
right: 0;
background-color: #fff; background-color: #fff;
border-radius: 16px 0 0 0; border-radius: 16px 0 0 0;
/* padding: 24px 32px; */
box-sizing: border-box; box-sizing: border-box;
overflow: scroll; overflow: scroll;
animation: showDrawer 0.31s ease; animation: showDrawer 0.31s ease;
-webkit-animation: showDrawer 0.31s ease; -webkit-animation: showDrawer 0.31s ease;
} margin: 0 auto;
.contentBoxHidden {
animation: hiddenDrawer 0.31s ease;
-webkit-animation: hiddenDrawer 0.31s ease;
} }
@keyframes showBG { @keyframes showBG {
...@@ -66,47 +53,15 @@ ...@@ -66,47 +53,15 @@
} }
} }
@keyframes hiddenBG {
from {
background-color: rgba(0, 0, 0, 0.78);
}
to {
background-color: rgba(0, 0, 0, 0);
}
}
@keyframes showClose {
from {
top: calc(100% - 40px);
}
to {
top: 0px;
}
}
@keyframes hiddenClose {
from {
top: 0px;
}
to {
top: calc(100% - 40px);
}
}
@keyframes showDrawer { @keyframes showDrawer {
from { from {
top: 100%; top: 160px;
width: calc(100vw - 28px);
opacity: 0;
} }
to { to {
top: 40px; top: 40px;
} width: 100vw;
} opacity: 1;
@keyframes hiddenDrawer {
from {
top: 40px;
}
to {
top: 100%;
} }
} }
// 从下往上弹的全屏抽屉 // 从下往上弹的全屏抽屉
import { useState } from "react"; // import { useState } from "react";
import style from "./index.module.css"; import style from "./index.module.css";
import classNames from "classnames"; import classNames from "classnames";
import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined"; import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined";
...@@ -7,30 +7,25 @@ import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined"; ...@@ -7,30 +7,25 @@ import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined";
type IFullScreenDrawerProps = { type IFullScreenDrawerProps = {
children: React.ReactNode; children: React.ReactNode;
handleClose: any; handleClose: any;
zIndex?: any;
}; };
const FullScreenDrawer = (props: IFullScreenDrawerProps) => { const FullScreenDrawer = (props: IFullScreenDrawerProps) => {
const { children, handleClose } = props; const { children, handleClose } = props;
const [closeing, setCloseing] = useState(false);
const handleReadyToClose = () => { const handleReadyToClose = () => {
setCloseing(true); handleClose();
setTimeout(() => {
setCloseing(false);
handleClose();
}, 300);
}; };
return ( return (
<div <div
className={classNames({ className={classNames({
[style.drawerBox]: true, [style.drawerBox]: true,
[style.drawerBoxHidden]: closeing,
})} })}
style={{ zIndex: props.zIndex }}
> >
<div <div
className={classNames({ className={classNames({
[style.closeBox]: true, [style.closeBox]: true,
[style.closeBoxHidden]: closeing,
})} })}
> >
<CloseOutlinedIcon <CloseOutlinedIcon
...@@ -41,7 +36,6 @@ const FullScreenDrawer = (props: IFullScreenDrawerProps) => { ...@@ -41,7 +36,6 @@ const FullScreenDrawer = (props: IFullScreenDrawerProps) => {
<div <div
className={classNames({ className={classNames({
[style.contentBox]: true, [style.contentBox]: true,
[style.contentBoxHidden]: closeing,
})} })}
> >
{children} {children}
......
...@@ -4,29 +4,32 @@ ...@@ -4,29 +4,32 @@
align-items: center; align-items: center;
border: 1px solid #e6e8eb; border: 1px solid #e6e8eb;
border-radius: 4px; border-radius: 4px;
background-color: #e6e8eb; background-color: #f0f2f5;
cursor: pointer; cursor: pointer;
height: 32px; height: 32px;
box-sizing: border-box; box-sizing: border-box;
padding: 2px; padding: 2px;
} }
.radio { .radio {
position: relative;
min-width: 63px;
height: 28px; height: 28px;
box-sizing: border-box; box-sizing: border-box;
font-size: 14px; font-size: 14px;
color: #565c66; color: #565c66;
border-radius: 4px; border-radius: 2px;
line-height: 20px; line-height: 20px;
padding: 3px 18px; padding: 6px 16px;
background-color: #e6e8eb; /* background-color: #e6e8eb; */
flex: 1; flex: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
white-space: nowrap; white-space: nowrap;
} }
.radioActive { .radioActive {
color: #1370ff; color: #1370ff;
background-color: #fff; background-color: #fff;
border: 1px solid #e6e8eb; box-shadow: 2px 4px 12px 0px rgba(0, 27, 63, 0.06);
} }
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-07-11 11:56:58
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-02 17:40:14
* @FilePath: /bkunyun/src/components/CommonComponents/RadioGroupOfButtonStyle/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 按钮样式的单选组 // 按钮样式的单选组
import classnames from "classnames"; import classnames from "classnames";
import style from "./index.module.css"; import style from "./index.module.css";
......
import React, { Fragment, ReactNode } from "react"; import React, { Fragment, ReactNode, useMemo } from "react";
import { Snackbar, Alert, Fade, Grow } from "@mui/material"; import { Snackbar, Alert, Fade, Grow } from "@mui/material";
import useMySnackbar from "./useMySnackbar"; import useMySnackbar from "./useMySnackbar";
import snackbarContext from "./snackbarContext"; import snackbarContext from "./snackbarContext";
import { ThemeProvider, createTheme } from "@mui/material/styles";
type MySnackbarProviderProp = { type MySnackbarProviderProp = {
vertical?: "bottom" | "top"; vertical?: "bottom" | "top";
horizontal?: "center" | "left" | "right"; horizontal?: "center" | "left" | "right";
autoHideDuration?: number; autoHideDuration?: number;
snackerClasses?: object; snackerClasses?: object;
ClickAwayListenerProps?: object; ClickAwayListenerProps?: object;
ContentProps?: object; ContentProps?: object;
disableWindowBlurListener?: boolean; disableWindowBlurListener?: boolean;
resumeHideDuration?: number; resumeHideDuration?: number;
snackbarSx?: object; snackbarSx?: object;
transition?: "grow" | "fade"; transition?: "grow" | "fade";
transitionDuration?: number; transitionDuration?: number;
TransitionProps?: object; TransitionProps?: object;
alertClasses?: object; alertClasses?: object;
closeText?: string; closeText?: string;
variant?: "filled" | "outlined" | "standard"; variant?: "filled" | "outlined" | "standard";
errorIcon?: ReactNode; errorIcon?: ReactNode;
infoIcon?: ReactNode; infoIcon?: ReactNode;
successIcon?: ReactNode; successIcon?: ReactNode;
warningIcon?: ReactNode; warningIcon?: ReactNode;
elevation?: number; elevation?: number;
alertSx?: object; alertSx?: object;
children?: ReactNode; children?: ReactNode;
}; };
const getTransitionComponent = (transition: "grow" | "fade") => { const getTransitionComponent = (transition: "grow" | "fade") => {
switch (transition) { switch (transition) {
case "grow": case "grow":
return Grow; return Grow;
case "fade": case "fade":
return Fade; return Fade;
default: default:
return Grow; return Grow;
} }
}; };
const MySnackbarProvider = ({ const MySnackbarProvider = ({
vertical = "top", vertical = "top",
horizontal = "center", horizontal = "center",
autoHideDuration = 5000, autoHideDuration = 3000,
snackerClasses, snackerClasses,
ClickAwayListenerProps, ClickAwayListenerProps,
ContentProps, ContentProps,
disableWindowBlurListener = true, disableWindowBlurListener = true,
resumeHideDuration, resumeHideDuration,
snackbarSx, snackbarSx,
transition = "grow", transition = "grow",
transitionDuration, transitionDuration,
TransitionProps = {}, TransitionProps = {},
alertClasses, alertClasses,
closeText = "Close", closeText = "Close",
variant = "standard", variant = "standard",
errorIcon, errorIcon,
infoIcon, infoIcon,
successIcon, successIcon,
warningIcon, warningIcon,
elevation = 3, elevation = 3,
alertSx, alertSx,
children, children,
}: MySnackbarProviderProp) => { }: MySnackbarProviderProp) => {
const { const {
open, open,
messageInfo, messageInfo,
handleExited, handleExited,
success, success,
error, error,
warning, warning,
info, info,
close, close,
} = useMySnackbar(); } = useMySnackbar();
return ( // .MuiAlert-filledInfo
<Fragment>
<Snackbar const getColorStyle = useMemo(() => {
open={open} if (messageInfo.severity === "success") {
autoHideDuration={autoHideDuration} return "#02AB83";
onClose={close} }
anchorOrigin={{ vertical: vertical, horizontal: horizontal }} if (messageInfo.severity === "info") {
classes={snackerClasses} return "#1370FF";
ClickAwayListenerProps={ClickAwayListenerProps} }
ContentProps={ContentProps} if (messageInfo.severity === "warning") {
disableWindowBlurListener={disableWindowBlurListener} return "#FFB919";
resumeHideDuration={resumeHideDuration} }
sx={snackbarSx} if (messageInfo.severity === "error") {
TransitionComponent={getTransitionComponent(transition)} return "#FF4E4E";
transitionDuration={transitionDuration} }
TransitionProps={{ onExited: handleExited, ...TransitionProps }} }, [messageInfo.severity]);
>
<Alert const theme = createTheme({
id="Alert" components: {
classes={alertClasses} MuiAlert: {
closeText={closeText} styleOverrides: {
iconMapping={{ root: {
error: errorIcon, color: getColorStyle,
info: infoIcon, "& .MuiAlert-icon": {
success: successIcon, color: getColorStyle,
warning: warningIcon, },
}} },
severity={messageInfo?.severity} },
variant={variant} },
elevation={elevation} },
sx={{ boxShadow: "unset", ...alertSx }} });
>
{messageInfo?.content} return (
</Alert> <Fragment>
</Snackbar> <ThemeProvider theme={theme}>
<snackbarContext.Provider value={{ success, error, warning, info }}> <Snackbar
{children} open={open}
</snackbarContext.Provider> autoHideDuration={autoHideDuration}
</Fragment> onClose={close}
); anchorOrigin={{ vertical: vertical, horizontal: horizontal }}
classes={snackerClasses}
ClickAwayListenerProps={ClickAwayListenerProps}
ContentProps={ContentProps}
disableWindowBlurListener={disableWindowBlurListener}
resumeHideDuration={resumeHideDuration}
sx={snackbarSx}
TransitionComponent={getTransitionComponent(transition)}
transitionDuration={transitionDuration}
TransitionProps={{ onExited: handleExited, ...TransitionProps }}
>
<Alert
id="Alert"
classes={alertClasses}
closeText={closeText}
iconMapping={{
error: errorIcon,
info: infoIcon,
success: successIcon,
warning: warningIcon,
}}
severity={messageInfo?.severity}
variant={variant}
elevation={elevation}
sx={{ boxShadow: "unset", ...alertSx }}
>
{messageInfo?.content}
</Alert>
</Snackbar>
</ThemeProvider>
<snackbarContext.Provider value={{ success, error, warning, info }}>
{children}
</snackbarContext.Provider>
</Fragment>
);
}; };
export default MySnackbarProvider; export default MySnackbarProvider;
...@@ -84,13 +84,13 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => { ...@@ -84,13 +84,13 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => {
return footerRender ? ( return footerRender ? (
footerRender() footerRender()
) : ( ) : (
<DialogActions style={{ padding: "0 24px 24px 24px" }}> <DialogActions style={{ padding: "0 24px 16px 24px" }}>
{showCancel ? ( {showCancel ? (
<MyButton <MyButton
text={cancelText || "取消"} text={cancelText || "取消"}
onClick={onClose} onClick={onClose}
variant="outlined" variant="outlined"
size="small" // size="small"
color="secondary" color="secondary"
/> />
) : null} ) : null}
...@@ -99,7 +99,7 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => { ...@@ -99,7 +99,7 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => {
text={okText || "确定"} text={okText || "确定"}
onClick={onConfirm} onClick={onConfirm}
variant="contained" variant="contained"
size="small" // size="small"
disabled={disabledConfirm} disabled={disabledConfirm}
isLoadingButton={true} isLoadingButton={true}
loading={loading} loading={loading}
...@@ -122,12 +122,16 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => { ...@@ -122,12 +122,16 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => {
"& .MuiPaper-root": { "& .MuiPaper-root": {
// 设置最大宽度, 实际宽度让子元素撑大 // 设置最大宽度, 实际宽度让子元素撑大
maxWidth: "1920px", maxWidth: "1920px",
borderRadius: "8px"
}, },
}, },
}} }}
> >
{isHideHeader ? null : ( {isHideHeader ? null : (
<DialogTitle id="alert-dialog-title"> <DialogTitle
id="alert-dialog-title"
sx={{ padding: "20px 24px" }}
>
<div <div
style={{ style={{
display: "flex", display: "flex",
...@@ -136,10 +140,10 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => { ...@@ -136,10 +140,10 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => {
fontWeight: 600, fontWeight: 600,
}} }}
> >
<span>{title}</span> <span style={{ fontSize: 16, lineHeight: '24px', color: '#1E2633' }}>{title}</span>
<CloseIcon <CloseIcon
onClick={onClose} onClick={onClose}
style={{ color: "#C2C6CC", cursor: "pointer" }} sx={{ color: "#C2C6CC", cursor: "pointer", ":hover": { background: "#f0f2f5", borderRadius: '2px' } }}
/> />
</div> </div>
</DialogTitle> </DialogTitle>
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-05 14:00:37 * @Date: 2022-07-05 14:00:37
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-21 17:33:59 * @LastEditTime: 2022-08-03 14:05:58
* @FilePath: /bkunyun/src/components/mui/MyInput.tsx * @FilePath: /bkunyun/src/components/mui/MyInput.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -67,6 +67,9 @@ const MyInput = (props: MyInputProps) => { ...@@ -67,6 +67,9 @@ const MyInput = (props: MyInputProps) => {
"&.Mui-focused .MuiOutlinedInput-notchedOutline": { "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderWidth: "1px", borderWidth: "1px",
}, },
"& .MuiOutlinedInput-notchedOutline": {
borderColor: '#DDE1E6',
},
":hover": { ":hover": {
"& .MuiOutlinedInput-notchedOutline": error "& .MuiOutlinedInput-notchedOutline": error
? {} ? {}
......
...@@ -68,7 +68,8 @@ const MyPopconfirm = (props: IMyPopconfirmProps) => { ...@@ -68,7 +68,8 @@ const MyPopconfirm = (props: IMyPopconfirmProps) => {
zIndex: 2000, zIndex: 2000,
bgcolor: "#fff", bgcolor: "#fff",
minWidth: "200px", minWidth: "200px",
borderRadius: "2px", borderRadius: "4px",
fontSize: "14px",
padding: "20px 16px", padding: "20px 16px",
boxShadow: "0px 3px 10px 0px rgba(0, 24, 57, 0.14)", boxShadow: "0px 3px 10px 0px rgba(0, 24, 57, 0.14)",
}} }}
...@@ -91,13 +92,12 @@ const MyPopconfirm = (props: IMyPopconfirmProps) => { ...@@ -91,13 +92,12 @@ const MyPopconfirm = (props: IMyPopconfirmProps) => {
<MyButton <MyButton
text={cancelText} text={cancelText}
variant="outlined" variant="outlined"
size="small"
color="inherit" color="inherit"
onClick={handleCancel} onClick={handleCancel}
style={{ marginRight: "12px" }} style={{ marginRight: "12px" }}
/> />
)} )}
<MyButton text={okText} size="small" onClick={handleOk} /> <MyButton text={okText} onClick={handleOk} />
</Box> </Box>
</Popper> </Popper>
); );
......
...@@ -11,65 +11,72 @@ import Radio from "@mui/material/Radio"; ...@@ -11,65 +11,72 @@ import Radio from "@mui/material/Radio";
import RadioGroup, { RadioGroupProps } from "@mui/material/RadioGroup"; import RadioGroup, { RadioGroupProps } from "@mui/material/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl"; import FormControl from "@mui/material/FormControl";
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from "@mui/material/FormHelperText";
interface IMyRadioProps extends RadioGroupProps { interface IMyRadioProps extends RadioGroupProps {
value: any; value: any;
options: Array<ICheckBoxOption>; options: Array<ICheckBoxOption>;
onChange: any; onChange: any;
variant?: "standard" | "outlined" | "filled"; variant?: "standard" | "outlined" | "filled";
error?: boolean; error?: boolean;
helperText?: string; helperText?: string;
}; }
type ICheckBoxOption = { type ICheckBoxOption = {
value: any; value: any;
label?: string; label?: string;
disabled?: boolean; disabled?: boolean;
}; };
// 如果后端给的options不是 value-label的字段可以用下面的方法转换 // 如果后端给的options不是 value-label的字段可以用下面的方法转换
export const optionsTransform = ( export const optionsTransform = (
arr: Array<any>, arr: Array<any>,
labelKey: string = "label", labelKey: string = "label",
valueKey: string = "value", valueKey: string = "value",
disabledKey: string = "disabled" disabledKey: string = "disabled"
): Array<ICheckBoxOption> => { ): Array<ICheckBoxOption> => {
return arr.map((item: any) => { return arr.map((item: any) => {
return { return {
value: item[valueKey], value: item[valueKey],
label: item[labelKey], label: item[labelKey],
disabled: item[disabledKey] || false, disabled: item[disabledKey] || false,
}; };
}); });
}; };
export default function MyRadio(props: IMyRadioProps) { export default function MyRadio(props: IMyRadioProps) {
const { value, options, onChange , error = false, helperText, variant} = props; const {
value,
options,
onChange,
error = false,
helperText,
variant,
} = props;
return ( return (
<FormControl fullWidth variant={variant} error={error}> <FormControl fullWidth variant={variant} error={error}>
<RadioGroup <RadioGroup
{...props} {...props}
row row
aria-labelledby="demo-row-radio-buttons-group-label" aria-labelledby="demo-row-radio-buttons-group-label"
name="row-radio-buttons-group" name="row-radio-buttons-group"
value={value} value={value}
onChange={onChange} onChange={onChange}
> >
{options.map((option) => { {options.map((option) => {
return ( return (
<FormControlLabel <FormControlLabel
key={option.value} key={option.value}
value={option.value} value={option.value}
control={<Radio />} control={<Radio sx={{ color: "rgba(209, 214, 222, 1)" }} />}
label={option.label} label={option.label}
disabled={option.disabled} disabled={option.disabled}
/> />
); );
})} })}
</RadioGroup> </RadioGroup>
{helperText && error && <FormHelperText>{helperText}</FormHelperText>} {helperText && error && <FormHelperText>{helperText}</FormHelperText>}
</FormControl> </FormControl>
); );
} }
...@@ -128,7 +128,7 @@ export default function MySelect(props: IProps) { ...@@ -128,7 +128,7 @@ export default function MySelect(props: IProps) {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<FormControl fullWidth={fullWidth} variant={variant}> <FormControl fullWidth={fullWidth} variant={variant} error={error}>
{isTitle ? ( {isTitle ? (
<InputLabel id="demo-simple-select-label"> <InputLabel id="demo-simple-select-label">
{title || "请选择"} {title || "请选择"}
......
...@@ -11,7 +11,8 @@ import Switch, { SwitchProps } from "@mui/material/Switch"; ...@@ -11,7 +11,8 @@ import Switch, { SwitchProps } from "@mui/material/Switch";
import { ThemeProvider, createTheme } from "@mui/material/styles"; import { ThemeProvider, createTheme } from "@mui/material/styles";
interface IMySwitchProps extends SwitchProps { interface IMySwitchProps extends SwitchProps {
value: boolean; value?: boolean;
defaultChecked?: boolean;
onChange?: any; onChange?: any;
disabled?: boolean; disabled?: boolean;
} }
...@@ -52,23 +53,24 @@ const theme = createTheme({ ...@@ -52,23 +53,24 @@ const theme = createTheme({
}); });
const MySwitch = (props: IMySwitchProps) => { const MySwitch = (props: IMySwitchProps) => {
const { value, onChange, disabled = false, size, ...other } = props; const { value, defaultChecked, onChange, disabled = false, size, ...other } = props;
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Switch <Switch
checked={value} checked={value}
disabled={disabled} disabled={disabled}
defaultChecked={defaultChecked}
onChange={onChange} onChange={onChange}
sx={ sx={
size === "medium" size === "medium"
? { ? {
width: 44, width: 44,
height: 24, height: 24,
} }
: { : {
width: 40, width: 40,
height: 22, height: 22,
} }
} }
{...other} {...other}
/> />
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13 * @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-04 20:18:17 * @LastEditTime: 2022-08-10 11:06:12
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -44,6 +44,9 @@ const theme = createTheme({ ...@@ -44,6 +44,9 @@ const theme = createTheme({
minWidth: "20px", minWidth: "20px",
marginRight: "32px", marginRight: "32px",
color: "#8A9099", color: "#8A9099",
":hover": {
color: "#1E2633",
},
selected: { selected: {
color: "#1976d2", color: "#1976d2",
}, },
...@@ -69,7 +72,7 @@ const Tabs = (props: IProps) => { ...@@ -69,7 +72,7 @@ const Tabs = (props: IProps) => {
tabList, tabList,
defaultValue, defaultValue,
allowNullValue = false, allowNullValue = false,
tabPanelSx = { padding: "20px 0 0 0" }, tabPanelSx = { padding: "24px 0 0 0" },
} = props; } = props;
const [value, setValue] = useState( const [value, setValue] = useState(
defaultValue defaultValue
...@@ -88,14 +91,21 @@ const Tabs = (props: IProps) => { ...@@ -88,14 +91,21 @@ const Tabs = (props: IProps) => {
<Box style={{ display: "flex", alignItems: "center" }}> <Box style={{ display: "flex", alignItems: "center" }}>
{item.icon ? ( {item.icon ? (
<img <img
style={{ width: "14px", marginRight: "10px" }} style={{ width: "16px", marginRight: "8px" }}
src={value === item.value ? item.iconed : item.icon} src={value === item.value ? item.iconed : item.icon}
alt="" alt=""
/> />
) : ( ) : (
"" ""
)} )}
<Typography sx={{ fontSize: "14px", fontWeight: "400" }}> <Typography
sx={{
fontSize: "14px",
lineHeight: "22px",
height: "22px",
fontWeight: "400",
}}
>
{item.label} {item.label}
</Typography> </Typography>
</Box> </Box>
...@@ -105,7 +115,7 @@ const Tabs = (props: IProps) => { ...@@ -105,7 +115,7 @@ const Tabs = (props: IProps) => {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<TabContext value={value}> <TabContext value={value}>
<Box sx={{ borderBottom: 1, borderColor: "#F0F2F5" }}> <Box sx={{ borderBottom: 1, borderColor: "#EDEFF2" }}>
<TabList <TabList
onChange={(e: any, val: string) => { onChange={(e: any, val: string) => {
onChange(val); onChange(val);
...@@ -118,7 +128,7 @@ const Tabs = (props: IProps) => { ...@@ -118,7 +128,7 @@ const Tabs = (props: IProps) => {
<Tab <Tab
key={key} key={key}
label={labelRender(item, key)} label={labelRender(item, key)}
value={item.value} value={item.value || ""}
id={item.value} id={item.value}
disabled={item.disabled} disabled={item.disabled}
/> />
...@@ -130,7 +140,11 @@ const Tabs = (props: IProps) => { ...@@ -130,7 +140,11 @@ const Tabs = (props: IProps) => {
?.filter((item) => !item.hide) ?.filter((item) => !item.hide)
.map((item) => { .map((item) => {
return ( return (
<TabPanel sx={tabPanelSx} value={item.value} key={item.value}> <TabPanel
sx={tabPanelSx}
value={item.value || ""}
key={item.value}
>
{item.component} {item.component}
</TabPanel> </TabPanel>
); );
......
...@@ -113,3 +113,16 @@ export const getUrlThroughParams = (params: any, encodeArr: Array<string> = [], ...@@ -113,3 +113,16 @@ export const getUrlThroughParams = (params: any, encodeArr: Array<string> = [],
}) })
return url return url
} }
// 判断上拉滚动是否需要加载 scrollLoaderThreshold 加载阈值 如0.25表示 离底部四分之一高度(包裹盒子的高)触发加载
export const getScrollLoader = (e: any, scrollLoaderThreshold: number = 0.25) => {
const clientHeight = e.target.clientHeight; // 滚动的外层盒子高度
const scrollHeight = e.target.scrollHeight; // 列表总高度
const scrollTop = e.target.scrollTop; // 滚动的高度
if (scrollTop + clientHeight * (1+scrollLoaderThreshold)> scrollHeight) {
return true
} else {
return false
}
}
\ No newline at end of file
.topApp { .topApp {
height: 56px; height: 56px;
background-color: white; background-color: white;
border-bottom: #e6e8eb 1px solid; border-bottom: #dde1e6 1px solid;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.topLeftBox { .topLeftBox {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.logo { .logo {
padding: 0px 25px 0px 20px; padding: 0px 16px 0px 20px;
margin-top: -10px; margin-top: -10px;
}
.uploadIconBox {
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 16px;
}
.uploadIconBox:hover {
background-color: #f0f2f5;
border-radius: 2px;
cursor: pointer;
} }
.topRightBox { .topRightBox {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
} }
.topRightItem { .topRightItem {
margin-right: 20px; margin-right: 20px;
} }
.ArrowDropDownIconRoot { .ArrowDropDownIconRoot {
color: #8a9099; color: #8a9099;
transition: all 0.2s !important; transition: all 0.2s !important;
transform: rotate(0); transform: rotate(0);
} }
.ArrowDropDownIconRootOpen { .ArrowDropDownIconRootOpen {
color: #8a9099; color: #8a9099;
transform: rotate(180deg); transform: rotate(180deg);
} }
.menuPaper { .menuPaper {
border: 1px solid #f0f2f5; border: 1px solid #f0f2f5;
background: #ffffff; background: #ffffff;
border-radius: 4px; border-radius: 4px;
box-shadow: 4px 4px 20px 0px rgba(0, 24, 57, 0.06) !important; box-shadow: 4px 4px 20px 0px rgba(0, 24, 57, 0.06) !important;
} }
.menuItemRoot { .menuItemRoot {
width: 143px; width: 143px;
font-size: 14px !important; font-size: 14px !important;
} }
...@@ -5,6 +5,7 @@ import { observer } from "mobx-react-lite"; ...@@ -5,6 +5,7 @@ import { observer } from "mobx-react-lite";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import Avatar from "@mui/material/Avatar"; import Avatar from "@mui/material/Avatar";
import { Box, Menu, MenuItem } from "@mui/material"; import { Box, Menu, MenuItem } from "@mui/material";
import classNames from "classnames";
import uploadIcon from "@/assets/img/uploadIcon.svg"; import uploadIcon from "@/assets/img/uploadIcon.svg";
import globalText from "@/utils/globalText_CN"; import globalText from "@/utils/globalText_CN";
...@@ -20,6 +21,7 @@ import { ...@@ -20,6 +21,7 @@ import {
setFileServerEndPointLocalStorage, setFileServerEndPointLocalStorage,
getFiletokenAccordingToId, getFiletokenAccordingToId,
} from "@/views/Project/project"; } from "@/views/Project/project";
import style from "./index.module.css"; import style from "./index.module.css";
const ConsoleLayout = observer(() => { const ConsoleLayout = observer(() => {
...@@ -92,7 +94,7 @@ const ConsoleLayout = observer(() => { ...@@ -92,7 +94,7 @@ const ConsoleLayout = observer(() => {
<MyButton <MyButton
text={globalText.product} text={globalText.product}
variant={"text"} variant={"text"}
style={{ color: "#565C66" }} style={{ color: "#565C66", paddingLeft: '8px' }}
onClick={handleProductClick} onClick={handleProductClick}
dropValue={productOpen} dropValue={productOpen}
drop={true} drop={true}
...@@ -143,12 +145,14 @@ const ConsoleLayout = observer(() => { ...@@ -143,12 +145,14 @@ const ConsoleLayout = observer(() => {
horizontal: "right", horizontal: "right",
}} }}
> >
<img <div className={classNames(style.uploadIconBox)}>
className={style.topRightItem} <img
src={uploadIcon} src={uploadIcon}
alt="" alt=""
style={{ verticalAlign: "middle" }} style={{ verticalAlign: "middle" }}
/> />
</div>
</MyPopover> </MyPopover>
<Box className={style.topRightItem}> <Box className={style.topRightItem}>
<Box <Box
...@@ -209,4 +213,4 @@ const ConsoleLayout = observer(() => { ...@@ -209,4 +213,4 @@ const ConsoleLayout = observer(() => {
); );
}); });
export default ConsoleLayout; export default ConsoleLayout;
\ No newline at end of file
.operatorList {
width: 226px;
height: calc(100% - 40px);
position: absolute;
top: 16px;
left: 16px;
background-color: #fff;
z-index: 10;
}
.searchBox {
height: 54px;
border-bottom: 1px solid rgba(240, 242, 245, 1);
box-sizing: border-box;
padding: 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.searchButton {
width: 22px;
height: 22px;
}
.searchIcon {
color: #999999;
}
.searchInput {
margin: 0 6px;
flex: 1;
color: #1e2633;
font-size: 14px;
line-height: 22px;
}
.list {
height: calc(100% - 55px);
overflow-y: auto;
padding: 15px 20px;
box-sizing: border-box;
}
.li {
background-color: RGBA(240, 242, 245, 1);
padding: 7px 9px;
color: rgba(30, 38, 51, 1);
font-size: 14px;
line-height: 22px;
margin-bottom: 12px;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
}
.nameVersion {
flex: 1;
/* text-align: left; */
margin-left: 13px;
word-wrap: break-word;
/* text-overflow: clip; */
max-width: 140px;
}
.name {
margin-right: 8px;
}
.version {
white-space: nowrap;
}
.icon {
width: 6px;
height: 10px;
/* position: absolute;
top: 13px;
left: 9px; */
display: flex;
flex-direction: column;
justify-content: space-between;
}
.iconLi {
display: flex;
justify-content: space-between;
}
.iconItem {
width: 2px;
height: 2px;
background-color: rgba(86, 92, 102, 1);
}
.loading {
display: flex;
justify-content: center;
align-items: center;
line-height: 20px;
font-size: 12px;
color: rgba(86, 92, 102, 1);
padding: 4px;
}
import { InputBase, IconButton } from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import styles from "./index.module.css";
import cloneDeep from "lodash/cloneDeep";
import { getScrollLoader } from "@/utils/util";
import { useState, useCallback, useEffect, useMemo } from "react";
import { getOperatorList } from "@/api/project_api";
import CircularProgress from "@mui/material/CircularProgress";
import useMyRequest from "@/hooks/useMyRequest";
import { useStores } from "@/store";
import { toJS } from "mobx";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
type IProps = {
operatorList: ITask[];
setOperatorList: any;
};
const OperatorList = (props: IProps) => {
const { operatorList, setOperatorList } = props; // 流程图中流算子列表
const { currentProjectStore } = useStores();
const [list, setList] = useState<ITask[]>([]); // 算子列表
const productId = toJS(currentProjectStore.currentProductInfo.id); // 产品ID
const [keyword, setKeyword] = useState(""); // 搜索算子列表时的关键词
const [dragItem, setDragItem] = useState<any>({}); // 拖拽的算子
const [page, setPage] = useState(0); // 算子列表页码
const [isLastPage, setIsLastPage] = useState(false); // 是否是最后一页
// 应该一个批算子里可以有多个流算子 流算子也可以重复 所以需要在流算子后加一个_${count} 加上operatorList可能有缓存数据, 所以不一定是1开头
const count = useMemo(() => {
if (operatorList.length === 0) {
return 1;
} else {
const countArr = operatorList.map((operatorLi) => {
const _index = operatorLi.id.indexOf("_");
if (_index === -1) {
return 1;
} else {
return Number(operatorLi.id.slice(_index + 1));
}
});
const maxCount = Math.max(...countArr);
return maxCount + 1;
}
}, [operatorList]);
// 获取算子列表
const { run: getOperatorListRun, loading } = useMyRequest(getOperatorList, {
onSuccess: (res) => {
let arr = res?.data?.content.map((item: any) => {
return {
...item,
edges: [],
};
});
setIsLastPage(res?.data?.last);
setList([...list, ...arr]);
},
});
// 获取算子列表方法
const getOperatorListFun = useCallback(
(keyword: string = "", page = 0) => {
getOperatorListRun({
productId: productId as string,
keyword,
page,
size: 20,
});
},
[productId, getOperatorListRun]
);
// 打开弹窗时获取算子列表
useEffect(() => {
getOperatorListFun();
}, [getOperatorListFun]);
// 算子列表搜索关键词变化
const keywordChange = (e: any) => {
if (e.target.value.length <= 30) {
setKeyword(e.target.value);
}
};
// 回车搜索
const handleKeywordKeyUp = (e: any) => {
if (e.keyCode === 13) {
setList([]);
setKeyword(e.target.value);
setPage(0);
getOperatorListFun(e.target.value, 0);
}
};
// 滚动触发
const handleScroll = (e: any) => {
// 满足快滚动到底部时开始加载
if (getScrollLoader(e)) {
if (loading || isLastPage) {
return;
} else {
getOperatorListFun(keyword, page + 1);
setPage(page + 1);
}
}
};
// 渲染六个小点样式
const randerIcon = () => {
return (
<div className={styles.icon}>
<div className={styles.iconLi}>
<div className={styles.iconItem}></div>
<div className={styles.iconItem}></div>
</div>
<div className={styles.iconLi}>
<div className={styles.iconItem}></div>
<div className={styles.iconItem}></div>
</div>
<div className={styles.iconLi}>
<div className={styles.iconItem}></div>
<div className={styles.iconItem}></div>
</div>
</div>
);
};
/** 拖拽开始 */
const onDragStart = (item: any) => {
setDragItem(item);
};
/** 生成批流副本 */
const getNewOperatorItem = useCallback(
(task: ITask, x: number, y: number) => {
const newEdges =
task?.edges &&
task?.edges.map((every) => {
return {
...every,
source: `${every.source}_${count}`,
target: `${every.target}_${count}`,
};
});
return {
...task,
id: `${task.id}_${count}`,
position: {
x: x,
y: y,
},
edges: newEdges.length ? newEdges : task?.edges,
};
},
[count]
);
/** 拖拽结束 */
const onDragEnd = useCallback(
(e: React.DragEvent<HTMLDivElement>) => {
const dom = document.getElementById("customOperatorFlow");
const clientX = e.clientX;
const clientY = e.clientY;
const upperLeftPointX = Number(dom?.offsetLeft);
const upperLeftPointY = Number(dom?.offsetTop);
const lowerRightX = Number(upperLeftPointX) + Number(dom?.offsetWidth);
const lowerRightY = Number(upperLeftPointY) + Number(dom?.offsetHeight);
if (
clientX > upperLeftPointX &&
clientY > upperLeftPointY &&
clientX < lowerRightX &&
clientY < lowerRightY
) {
const newDragItem = cloneDeep(dragItem);
const newOperatorItem = getNewOperatorItem(
newDragItem,
clientX - upperLeftPointX,
clientY - upperLeftPointY - 42
);
const newVal = cloneDeep(operatorList);
newVal.push(newOperatorItem);
setOperatorList(newVal);
}
},
[dragItem, operatorList, setOperatorList, getNewOperatorItem]
);
return (
<div className={styles.operatorList}>
<div className={styles.searchBox}>
<IconButton
type="submit"
className={styles.searchButton}
aria-label="search"
>
<SearchIcon
className={styles.searchIcon}
style={{ color: "rgba(153, 153, 153, 1)" }}
/>
</IconButton>
<InputBase
className={styles.searchInput}
placeholder="请输入算子名称"
value={keyword}
onChange={keywordChange}
onKeyUp={handleKeywordKeyUp}
sx={{ fontSize: "14px" }}
/>
</div>
<div className={styles.list} onScroll={(e) => handleScroll(e)}>
{list.map((item, index) => {
return (
<div
key={index}
className={styles.li}
draggable={true}
onDragStart={() => onDragStart(item)}
onDragEnd={onDragEnd}
>
{randerIcon()}
<div className={styles.nameVersion}>
<span className={styles.name}>{item.title}</span>
<span className={styles.version}>v{item.version}</span>
</div>
</div>
);
})}
{loading && (
<div className={styles.loading}>
<CircularProgress
size={12}
sx={{
color: "rgba(19, 112, 255, 1)",
marginRight: "8px",
}}
/>
加载中...
</div>
)}
{isLastPage && <div className={styles.loading}>已经到底了</div>}
</div>
</div>
);
};
export default OperatorList;
import MyDialog from "@/components/mui/MyDialog";
import MyInput from "@/components/mui/MyInput";
import { checkIsNumberLetterChinese } from "@/utils/util";
import _ from "lodash";
import { useState } from "react";
import useMyRequest from "@/hooks/useMyRequest";
import { saveBatchActor } from "@/api/project_api";
import { useStores } from "@/store";
import { useMessage } from "@/components/MySnackbar";
import { toJS } from "mobx";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
interface IProps {
saveFormDialog: boolean;
setSaveFormDialog: (val: boolean) => void;
operatorList: ITask[];
setShowCustomOperator: any;
}
const SaveOperator = (props: IProps) => {
const {
saveFormDialog,
setSaveFormDialog,
operatorList,
setShowCustomOperator,
} = props;
const { currentProjectStore } = useStores();
const Message = useMessage();
const productId = toJS(currentProjectStore.currentProductInfo.id);
const [title, setTitle] = useState("");
const [version, setVersion] = useState("1.0.0");
const [description, setDescription] = useState("");
// 保存批算子
const { run: saveBatchActorRun, loading } = useMyRequest(saveBatchActor, {
onSuccess: (res) => {
Message.success("保存算子成功");
sessionStorage.setItem("operatorList", "[]");
setSaveFormDialog(false);
setShowCustomOperator();
},
});
const [titleHelper, setTitleHelper] = useState({
// 算子名称错误提示
error: false,
helperText: "",
});
const [versionHelper, setVersionHelper] = useState({
// 算子版本错误提示
error: false,
helperText: "",
});
// 关闭表单弹窗
const handleCloseDialog = () => {
setSaveFormDialog(false);
};
// 算子名称
const handleTitleChange = (e: any) => {
const title = e.target.value;
setTitle(title);
checkTitle(title);
};
// 算子版本
const handleVersionChange = (e: any) => {
let version = e.target.value;
setVersion(version);
checkVersion(version);
};
// 算子描述
const handleDescriptionChange = (e: any) => {
let description = e.target.value;
if (description.length < 301) {
setDescription(description);
}
};
// 校验算子名称
const checkTitle = (title: string) => {
if (!title) {
setTitleHelper({
error: true,
helperText: "必须输入算子名称",
});
return false;
} else if (title.length > 15) {
setTitleHelper({
error: true,
helperText: "格式不正确,必须在15字符以内,仅限大小写字母、数字、中文",
});
return false;
} else if (!checkIsNumberLetterChinese(title)) {
setTitleHelper({
error: true,
helperText: "格式不正确,必须在15字符以内,仅限大小写字母、数字、中文",
});
return false;
} else {
setTitleHelper({
error: false,
helperText: "",
});
return true;
}
};
// 校验版本号格式
const checkVersion = (version: string) => {
if (/^[1-9]\d?(\.(0|[1-9]\d?)){2}$/.test(version)) {
setVersionHelper({
error: false,
helperText: "",
});
return true;
} else {
setVersionHelper({
error: true,
helperText: "格式不正确,必须为X.Y.Z格式,且XYZ必须为0~99的正整数",
});
return false;
}
};
// 坐标转换
const positionTransform = () => {
const copyOperatorList: ITask[] = _.cloneDeep(operatorList);
const positionXArr = copyOperatorList.map((operatorLi) => {
return operatorLi.position.x;
});
const positionYArr = copyOperatorList.map((operatorLi) => {
return operatorLi.position.y;
});
let minPositionX = Math.min(...positionXArr);
let minPositionY = Math.min(...positionYArr);
let startingPointX = minPositionX - 40;
let startingPointY = minPositionY - 10;
return copyOperatorList.map((operatorLi) => {
return {
...operatorLi,
position: {
x: operatorLi.position.x - startingPointX,
y: operatorLi.position.y - startingPointY,
},
};
});
};
// 表单弹窗确定,新建算子
const handleOncofirm = () => {
if (checkTitle(title) && checkVersion(version)) {
saveBatchActorRun({
query: {
productId: productId as string,
batchName: title,
batchVersion: version,
description: description,
},
body: positionTransform(),
});
}
};
return (
<MyDialog
open={saveFormDialog}
title="保存算子"
onClose={handleCloseDialog}
onConfirm={handleOncofirm}
loading={loading}
>
<div style={{ width: "388px" }}>
<MyInput
value={title}
label="算子名称"
onChange={handleTitleChange}
required
error={titleHelper.error}
helperText={titleHelper.helperText}
style={{ margin: "20px 0" }}
></MyInput>
<MyInput
value={version}
label="版本号"
onChange={handleVersionChange}
error={versionHelper.error}
helperText={versionHelper.helperText}
style={{ marginBottom: "20px" }}
></MyInput>
<div style={{ position: "relative" }}>
<MyInput
value={description}
id="desc"
label="模板描述"
placeholder="模板描述"
onChange={handleDescriptionChange}
multiline
rows={4}
/>
<span
style={{
position: "absolute",
bottom: "7px",
right: "12px",
color: description.length >= 300 ? "#d32f2f" : "#C2C6CC",
}}
>
{description.length}/300
</span>
</div>
</div>
</MyDialog>
);
};
export default SaveOperator;
.customOperator {
padding: 4px 32px 24px;
background-color: #fff;
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
}
.coTop {
display: flex;
justify-content: space-between;
padding: 17px 0;
}
.coTitle {
color: rgba(30, 38, 51, 1);
font-size: 18px;
line-height: 26px;
font-weight: 600;
}
.coContent {
flex: 1;
background-color: RGBA(247, 248, 250, 1);
position: relative;
}
import React, { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer";
import MyButton from "@/components/mui/MyButton";
import OperatorList from "./components/OperatorList";
// import Flow from "../Project/components/Flow";
import { useMessage } from "@/components/MySnackbar";
import BatchOperatorFlow from "../Project/components/Flow/components/BatchOperatorFlow";
import SaveOperator from "./components/SaveOperator";
import { ITask } from "../Project/ProjectSubmitWork/interface";
import _ from "lodash";
import styles from "./index.module.css";
type IProps = {
setShowCustomOperator: any;
initOperatorList: ITask[];
};
const CustomOperator = observer((props: IProps) => {
const { setShowCustomOperator, initOperatorList } = props;
const Message = useMessage();
const [operatorList, setOperatorList] = useState<ITask[]>(initOperatorList);
const [saveFormDialog, setSaveFormDialog] = useState(false);
// const [showCustomOperator, setShowCustomOperator] = useState(false);
/** 设置选中唯一标识符 */
const handleNodeClick = useCallback((val: string) => {
// setSelectTaskId(val);
// console.log(val);
}, []);
// 判断 每个流算子必须至少有一条连接线。
const checkHasOneLine = (sourceArr: string[], targetArr: string[]) => {
console.log("checkHasOneLine");
const all = _.uniq([...sourceArr, ...targetArr]);
if (all.length === operatorList.length) {
console.log("checkHasOneLine1");
return true;
} else {
console.log("checkHasOneLine2");
return false;
}
// _.uniq([2, 1, 2]);
};
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const checkIn = (targetArr: string[]) => {
console.log("checkIn");
const uniqTargetArr = _.uniq(targetArr);
if (uniqTargetArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqTargetArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const inArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "in"
);
if (inArr.length > 0) {
if (
!inArr.some((inItem) => {
return (
(inItem.domType || "").toLowerCase() === "dataset" ||
(inItem.domType || "").toLowerCase() === "path" ||
(inItem.domType || "").toLowerCase() === "file"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
// 判断 起码有一个结尾算子(可以有多个结尾点)的输出必须为文件保存或数据集保存。
const checkOut = (sourceArr: string[]) => {
console.log("checkOut");
const uniqSourceArr = _.uniq(sourceArr);
if (uniqSourceArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqSourceArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const outArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "out"
);
if (outArr.length > 0) {
if (
!outArr.some((outItem) => {
return (
(outItem.domType || "").toLowerCase() === "dataset" ||
(outItem.domType || "").toLowerCase() === "file" ||
(outItem.domType || "").toLowerCase() === "path"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
useEffect(() => {
sessionStorage.setItem("operatorList", JSON.stringify(operatorList));
}, [operatorList]);
const handleCheck = () => {
if (operatorList.length === 0) {
Message.error("内容不能为空!");
return;
}
let sourceArr: string[] = [];
let targetArr: string[] = [];
operatorList.forEach((flowNode) => {
flowNode.edges.forEach((edge) => {
edge.source && sourceArr.push(edge.source);
edge.target && targetArr.push(edge.target);
});
});
console.log("operatorList", operatorList);
console.log("sourceArr", sourceArr);
console.log("targetArr", targetArr);
if (!checkHasOneLine([...sourceArr], [...targetArr])) {
console.log("checkHasOneLine");
Message.error("内容校验未通过,请检查!");
return;
}
if (!checkIn([...targetArr])) {
console.log("checkIn");
Message.error("内容校验未通过,请检查!");
return;
}
if (!checkOut([...sourceArr])) {
console.log("checkOut");
Message.error("内容校验未通过,请检查!");
return;
}
setSaveFormDialog(true);
};
return (
<FullScreenDrawer handleClose={setShowCustomOperator} zIndex={1100}>
<div className={styles.customOperator}>
<div className={styles.coTop}>
<div className={styles.coTitle}>添加算子</div>
<MyButton
text="添加"
onClick={() => {
handleCheck();
}}
></MyButton>
<SaveOperator
saveFormDialog={saveFormDialog}
setSaveFormDialog={setSaveFormDialog}
operatorList={operatorList}
setShowCustomOperator={setShowCustomOperator}
></SaveOperator>
</div>
<div className={styles.coContent} id="customOperatorFlow">
<OperatorList
operatorList={operatorList}
setOperatorList={setOperatorList}
/>
<BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit"
onFlowNodeClick={handleNodeClick}
flowNodeDraggable={true}
ListenState={!saveFormDialog}
showVersion={true}
showControls={false}
/>
</div>
</div>
</FullScreenDrawer>
);
});
export default CustomOperator;
.container { .container {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
justify-content: center; justify-content: center;
} }
.aside { .aside {
border-right: 1px solid #e6e8eb; border-right: 1px solid #dde1e6;
width: 10%; width: 10%;
min-width: 220px; min-width: 220px;
height: calc(100vh - 57px); height: calc(100vh - 57px);
background-color: #f7f8fa; background-color: #f7f8fa;
} }
.content { .content {
flex: 1; flex: 1;
height: calc(100vh - 57px); height: calc(100vh - 57px);
overflow: scroll; overflow: scroll;
} }
.listItem { .listItem {
padding: 8px 25px; padding: 8px 25px;
height: 38px; height: 38px;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
color: #565c66; color: #565c66;
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
border-left: 3px solid #fff;
} }
.listItem:hover { .listItem:hover {
background-color: #EEF1F5; background-color: #eef1f5;
} }
.routerIcon {
vertical-align: middle;
margin-right: 12px;
line-height: 22px;
}
.active { .active {
border-left: 3px solid #1370ff; border-left: 3px solid #1370ff;
color: #1370ff; color: #1370ff;
background-color: #ebedf0; background-color: #ebedf0;
} }
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-02 11:43:28
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-03 19:05:48
* @FilePath: /bkunyun/src/views/MenuLayout/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { Box, List } from "@mui/material"; import { Box, List } from "@mui/material";
import CurrentProject from "../Project/components/CurrentProject"; import CurrentProject from "../Project/components/CurrentProject";
import React from "react"; import React from "react";
...@@ -21,11 +29,27 @@ const MenuLayout = observer(() => { ...@@ -21,11 +29,27 @@ const MenuLayout = observer(() => {
navigate("/home"); navigate("/home");
} }
const routerIcon = (id: string, isSelect: boolean) => {
try {
const result = require(`../../assets/project/${id}${isSelect ? '_BLUE' : ''}.svg`)
return result || ''
} catch (error) {
console.log(error)
}
}
return ( return (
<Box className={style.container}> <Box className={style.container}>
<Box className={style.aside}> <Box className={style.aside}>
<CurrentProject /> {
<List> pathname.indexOf('userCenter') < 0 && <CurrentProject />
}
<List
sx={{
paddingTop: 0,
paddingBottom: 0,
}}
>
{permissionStore.sidebarRouters.map((item, index) => { {permissionStore.sidebarRouters.map((item, index) => {
if (item.show) { if (item.show) {
return ( return (
...@@ -37,7 +61,8 @@ const MenuLayout = observer(() => { ...@@ -37,7 +61,8 @@ const MenuLayout = observer(() => {
})} })}
onClick={() => item.type === "page" && navigate(item.path)} onClick={() => item.type === "page" && navigate(item.path)}
> >
{item.name} <img className={style.routerIcon} src={routerIcon(item.id || '', `/v3${item.path}` === pathname) || undefined} alt='' />
<span style={{ verticalAlign: 'middle', fontWeight: '500' }}>{item.name}</span>
</li> </li>
); );
} }
......
...@@ -60,7 +60,9 @@ const AddFolder = (props: IAddFolderProps) => { ...@@ -60,7 +60,9 @@ const AddFolder = (props: IAddFolderProps) => {
const handleFileNameChange = (e: any) => { const handleFileNameChange = (e: any) => {
const fileName = e.target.value; const fileName = e.target.value;
setFileName(fileName); if (fileName.length <= 30) {
setFileName(fileName);
}
if (!fileName) { if (!fileName) {
setFileNameCheck({ setFileNameCheck({
error: true, error: true,
...@@ -95,7 +97,7 @@ const AddFolder = (props: IAddFolderProps) => { ...@@ -95,7 +97,7 @@ const AddFolder = (props: IAddFolderProps) => {
<MyButton <MyButton
text="新建文件夹" text="新建文件夹"
variant="outlined" variant="outlined"
size="small" size="medium"
onClick={() => setDeleteDialogOpen(true)} onClick={() => setDeleteDialogOpen(true)}
disabled={ disabled={
selectIds.length !== 0 || !isPass("PROJECT_DATA_ADDDIR", "USER") selectIds.length !== 0 || !isPass("PROJECT_DATA_ADDDIR", "USER")
...@@ -123,7 +125,7 @@ const AddFolder = (props: IAddFolderProps) => { ...@@ -123,7 +125,7 @@ const AddFolder = (props: IAddFolderProps) => {
helperText={fileNameCheck.help} helperText={fileNameCheck.help}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end"> <InputAdornment position="end" style={{ color: fileName.length >= 30 ? "#d32f2f" : "#C2C6CC" }}>
{fileName.length}/30 {fileName.length}/30
</InputAdornment> </InputAdornment>
), ),
......
...@@ -53,7 +53,7 @@ const SeeDataset = observer((props: ISeeDatasetProps) => { ...@@ -53,7 +53,7 @@ const SeeDataset = observer((props: ISeeDatasetProps) => {
useEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
setShowAnimation(false); setShowAnimation(false);
}, 1000); }, 300);
}, []); }, []);
const isCadd = useMemo(() => { const isCadd = useMemo(() => {
...@@ -159,7 +159,7 @@ const SeeDataset = observer((props: ISeeDatasetProps) => { ...@@ -159,7 +159,7 @@ const SeeDataset = observer((props: ISeeDatasetProps) => {
}; };
return ( return (
<FullScreenDrawer handleClose={props.handleClose}> <FullScreenDrawer handleClose={props.handleClose} zIndex={1100}>
<div <div
className={classNames({ className={classNames({
[style.datasetBox]: true, [style.datasetBox]: true,
......
...@@ -164,12 +164,12 @@ const Save = (props: ISaveProps) => { ...@@ -164,12 +164,12 @@ const Save = (props: ISaveProps) => {
value={showDpath} value={showDpath}
required required
label="保存路径" label="保存路径"
onClick={() => {
setFileSelectOpen(true);
}}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<img <img
onClick={() => {
setFileSelectOpen(true);
}}
src={fileSelectIcon} src={fileSelectIcon}
alt="选择输出路径" alt="选择输出路径"
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
......
.uploderBox { .uploderBox {
width: 900px; width: 900px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.uploderBoxLeft { .uploderBoxLeft {
width: 300px; width: 300px;
} }
.dropTitle { .dropTitle {
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
color: #1e2633; color: #1e2633;
margin-bottom: 14px; margin-bottom: 14px;
font-weight: 600; font-weight: 600;
} }
.dropBox { .dropBox {
height: 300px; cursor: pointer;
display: flex; height: 300px;
flex-direction: column; display: flex;
justify-content: center; flex-direction: column;
align-items: center; justify-content: center;
border: 1px dashed #d1d6de; align-items: center;
border-radius: 4px; border: 1px dashed #d1d6de;
border-radius: 4px;
}
.dropBox:hover {
background-color: #f7f8fa;
} }
.dropBoxDragActive { .dropBoxDragActive {
background-color: #f7f8fa; background-color: #f7f8fa;
} }
.uploaderIcon { .uploaderIcon {
width: 56px; width: 56px;
margin-bottom: 24px; margin-bottom: 24px;
} }
.uploderText1 { .uploderText1 {
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
color: #565c66; color: #565c66;
margin-bottom: 2px; margin-bottom: 2px;
} }
.uploderText2 { .uploderText2 {
line-height: 20px; line-height: 20px;
font-size: 12px; font-size: 12px;
color: #8a9099; color: #8a9099;
} }
.fileListBox { .fileListBox {
width: 528px; width: 528px;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
} }
/* .tableBox { /* .tableBox {
height: 300px; height: 300px;
overflow: scroll; overflow: scroll;
} */ } */
.fileIconBox { .fileIconBox {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
} }
.fileIconBoxText{ .fileIconBoxText {
display: block; display: block;
width: 260px; width: 260px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
-o-text-overflow:ellipsis; -o-text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.noFile { .noFile {
position: absolute; position: absolute;
top: 92px; top: 92px;
height: 200px; height: 200px;
width: 100%; width: 100%;
background-color: #fff; background-color: #fff;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.noFileIcon { .noFileIcon {
width: 83px; width: 83px;
height: 70px; height: 70px;
height: auto; height: auto;
margin-bottom: 6px; margin-bottom: 6px;
} }
.noFileText { .noFileText {
line-height: 18px; line-height: 18px;
font-size: 12px; font-size: 12px;
color: rgba(34, 34, 34, 0.45); color: rgba(34, 34, 34, 0.45);
} }
.fileIcon { .fileIcon {
margin-right: 12px; margin-right: 12px;
} }
.red { .red {
color: #ff4e4e; color: #ff4e4e;
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
position: relative; position: relative;
} }
.projectDataStickyTop { .projectDataStickyTop {
padding: 28px 24px; padding: 28px 24px 64px;
position: relative; position: relative;
} }
.projectDataTitle { .projectDataTitle {
...@@ -56,9 +56,10 @@ ...@@ -56,9 +56,10 @@
.projectDataStickyBox { .projectDataStickyBox {
height: 64px; height: 64px;
position: sticky; width: calc(100vw - 220px);
bottom: -1px; position: fixed;
border: 1px solid #ebedf0; bottom: 0px;
border-top: 1px solid #ebedf0;
z-index: 100; z-index: 100;
background-color: #fff; background-color: #fff;
display: flex; display: flex;
......
...@@ -515,7 +515,7 @@ const ProjectData = observer(() => { ...@@ -515,7 +515,7 @@ const ProjectData = observer(() => {
<MyButton <MyButton
text="上传文件" text="上传文件"
variant="contained" variant="contained"
size="small" size="medium"
style={{ marginRight: "12px" }} style={{ marginRight: "12px" }}
onClick={() => setUploaderDialogOpen(true)} onClick={() => setUploaderDialogOpen(true)}
disabled={ disabled={
...@@ -556,7 +556,10 @@ const ProjectData = observer(() => { ...@@ -556,7 +556,10 @@ const ProjectData = observer(() => {
size="small" size="small"
onClick={handleRefresh} onClick={handleRefresh}
disabled={!isPass("PROJECT_DATA_REFRESH", "USER")} disabled={!isPass("PROJECT_DATA_REFRESH", "USER")}
sx={{ marginLeft: "17px" }} sx={{ marginLeft: "17px", width: '32px', height: '32px', ":hover": {
backgroundColor: "#F0F2F5 ",
borderRadius: 2
} }}
> >
<RefreshIcon sx={{ fontSize: "18px" }} /> <RefreshIcon sx={{ fontSize: "18px" }} />
</IconButton> </IconButton>
......
.logView {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
z-index: 1002;
}
.logViewBox {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 900px;
height: 600px;
background: #282C34;
box-shadow: 2px 4px 20px 0px rgba(0, 15, 45, 0.1200);
border-radius: 8px;
}
.close {
position: absolute;
right: 0;
top: -28px;
cursor: pointer;
color: #fff;
}
.logViewTop {
/* height: 30px; */
background-color: #1D2126;
border-radius: 8px 8px 0 0;
display: flex;
font-size: 12px;
color: #8A9099;
overflow: auto;
}
.logViewTop::-webkit-scrollbar-track {
background-color: #282C34;
}
.logTitle {
display: flex;
align-items: center;
height: 30px;
line-height: 20px;
padding: 0 24px;
cursor: pointer;
}
.logTitleSelected {
background: #282C34;
color: #FFFFFF;
}
.fileIcon{
width: 14px!important;
margin-right: 4px;
}
.logViewContent {
position: relative;
box-sizing: border-box;
height: 510px;
padding: 16px 24px 0;
color: #D1D6DE;
white-space:pre-wrap;
word-break: break-word;
overflow: auto;
font-size: 14px;
line-height: 22px;
}
.logViewContentMask{
height: 16px;
width: 852px;
background-color: #282C34;
position: absolute;
top: 30px;
left: 24px;
z-index: 1005;
}
.logViewContent::-webkit-scrollbar-track {
background-color: #282C34;
}
.logViewBottom {
display: flex;
align-items: center;
justify-content: end;
height: 60px;
padding-right: 24px;
}
\ No newline at end of file
import { useState, useCallback, useEffect, useMemo } from "react";
import classnames from "classnames";
import style from "./index.module.css";
import CloseIcon from "@mui/icons-material/Close";
import MyButton from "@/components/mui/MyButton";
import InsertDriveFileOutlinedIcon from "@mui/icons-material/InsertDriveFileOutlined";
import CloudEController from "@/api/fileserver/CloudEController";
import { useStores } from "@/store";
import { toJS } from "mobx";
type LogViewProps = {
isshow: boolean;
handleClose: () => void;
logs: any[];
};
const LogView = (props: LogViewProps) => {
const { isshow, handleClose, logs } = props;
const { currentProjectStore } = useStores();
const fileToken = toJS(currentProjectStore.currentProjectInfo.filetoken);
const projectId = toJS(currentProjectStore.currentProjectInfo.id);
// 当前选择的日志
const [logCurrent, setLogCurrent] = useState<number>(0);
// 当前日志的内容文本
const [logText, setLogText] = useState('')
// 当前日志路径
const [logPath, setLogPath] = useState('')
useEffect(() => {
setLogPath(logs[logCurrent]?.logPath)
}, [logs]);
// 请求日志文本
useEffect(() => {
if (logPath) {
const path = logPath.slice(12)
CloudEController.JobFileDownloadText(
path,
fileToken as string,
projectId as string
)?.then((res) => {
setLogText(res.data)
})
}else{
setLogText("")
}
}, [logPath]);
// 选择日志时改变日志路径
useEffect(() => {
setLogPath(logs[logCurrent]?.logPath)
}, [logCurrent]);
// 下载当前日志
const handleDownLoad=()=>{
const path = logPath.slice(12)
CloudEController.JobFileDownload(
path,
fileToken as string,
projectId as string
);
}
return <div className={style.logView} style={isshow ? {} : { display: "none" }}>
<div className={style.logViewBox}>
<CloseIcon onClick={handleClose} className={style.close} />
<div className={style.logViewContentMask}></div>
<div className={style.logViewTop}>
{logs.map((item: any, index: number) => {
return <div
key={index}
onClick={() => { setLogCurrent(index) }}
className={classnames({
[style.logTitle]: true,
[style.logTitleSelected]: index === logCurrent,
})}>
<InsertDriveFileOutlinedIcon className={style.fileIcon} />
<span>{item.logName}</span>
</div>
})}
</div>
<div className={style.logViewContent}>
{logText}
</div>
<div className={style.logViewBottom}>
<MyButton text='下载当前日志' onClick={handleDownLoad} />
</div>
</div>
</div>
}
export default LogView
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: RGBA(247, 248, 250, 1); background-color: RGBA(247, 248, 250, 1);
overflow-y: scroll; overflow-y: auto;
} }
.swHeader { .swHeader {
z-index: 1001; z-index: 1001;
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
margin: 6px 0; margin: 6px 0;
} }
.outputLiLeft { .outputLiLeft {
cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
color: rgba(19, 112, 255, 1); color: rgba(19, 112, 255, 1);
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-15 14:44:24
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-15 14:46:02
* @FilePath: /bkunyun/src/views/Project/ProjectJobDetail/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export interface IFlowNodeTransmissionNum {
target: string,
value: number,
batchId: string,
source: string
}
\ No newline at end of file
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-15 15:03:10
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-15 15:43:16
* @FilePath: /bkunyun/src/views/Project/ProjectJobDetail/utils.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { IEdge, ITask } from "../ProjectSubmitWork/interface";
import { IFlowNodeTransmissionNum } from "./interface";
// 根据outputs的路径获取数据集的路径
export const getDatasetPath = (path: string) => {
let datasetPath = "";
const noProjectPath = path.slice(12);
const fileIndex = noProjectPath.indexOf("/.dataset");
if (fileIndex !== -1) {
datasetPath = noProjectPath.slice(0, fileIndex);
} else {
datasetPath = noProjectPath;
}
return datasetPath ? datasetPath : "/";
};
// 根据outputs的路径获取数据集的名称
export const getDatasetName = (path: string) => {
let name = "";
let nameIndex = path.indexOf("/.dataset/") + 10;
const lastIndex = path.lastIndexOf("/");
if (nameIndex !== -1 && lastIndex !== -1) {
name = path.slice(nameIndex, lastIndex);
}
return name;
};
/** 根据批节点 判断是否在同一个批 */
export const getSameBatch = (flowNodeInfo: IFlowNodeTransmissionNum[], task: ITask) => {
return flowNodeInfo.some(every=>{
return every.batchId === task.parentNode
})
}
/** 连接的数组 */
export const getConnectionArr = (flowNodeInfo: IFlowNodeTransmissionNum[], edge: IEdge) => {
return flowNodeInfo.filter(every => {
return every.source === edge?.source && every.target === edge?.target
})
}
...@@ -36,11 +36,17 @@ ...@@ -36,11 +36,17 @@
} }
.taskName { .taskName {
min-width: 100px;
font-size: 14px; font-size: 14px;
line-height: 22px;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
} }
.taskName:hover{
color: #1370FF;
}
.taskStatus { .taskStatus {
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -59,12 +65,13 @@ ...@@ -59,12 +65,13 @@
.taskCreator { .taskCreator {
font-size: 12px; font-size: 12px;
line-height: 20px;
color: #1E2633; color: #1E2633;
margin-top: 5px; margin-top: 4px;
} }
.taskProgress { .taskProgress {
height: 40px; height: 32px;
margin: 12px 0; margin: 12px 0;
} }
......
...@@ -50,7 +50,7 @@ const TaskCard = (props: TaskCardProps) => { ...@@ -50,7 +50,7 @@ const TaskCard = (props: TaskCardProps) => {
const goToProjectData = (path: string) => { const goToProjectData = (path: string) => {
const lastIndex = path.lastIndexOf("/"); const lastIndex = path.lastIndexOf("/");
if (lastIndex !== -1) { if (lastIndex !== -1) {
path = path.slice(0, lastIndex + 1); path = path.slice(0, lastIndex);
} }
path = path.slice(12); path = path.slice(12);
if (path) { if (path) {
...@@ -66,7 +66,7 @@ const TaskCard = (props: TaskCardProps) => { ...@@ -66,7 +66,7 @@ const TaskCard = (props: TaskCardProps) => {
// 跳转详情页 // 跳转详情页
const gotoDetail = (id: string) => { const gotoDetail = (id: string) => {
navigate(`/product/cadd/projectJobDetail`, { navigate(`/product/cadd/projectJobDetail`, {
state: { taskId: id }, state: { taskId: id, from: 'projectOverview' },
}); });
} }
// 渲染状态图标 // 渲染状态图标
......
.basicInformation { .basicInformation {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: 28px 24px 0 24px; margin: 24px 24px 0 24px;
padding-bottom: 20px; padding-bottom: 20px;
border-bottom: 1px solid #F0F2F5; border-bottom: 1px solid #F0F2F5;
} }
...@@ -14,12 +14,13 @@ ...@@ -14,12 +14,13 @@
.titleBox { .titleBox {
display: flex; display: flex;
align-items: center; align-items: flex-end;
} }
.projectName { .projectName {
margin-left: 12px; margin-left: 12px;
font-size: 18px; font-size: 18px;
line-height: 26px;
font-weight: 600; font-weight: 600;
} }
...@@ -39,6 +40,8 @@ ...@@ -39,6 +40,8 @@
} }
.otherInformationBoxRight{ .otherInformationBoxRight{
line-height: 22px;
margin-bottom: 4px;
color: #565C66; color: #565C66;
} }
...@@ -51,15 +54,15 @@ ...@@ -51,15 +54,15 @@
.verticalLine { .verticalLine {
height: 32px; height: 32px;
border: 1px solid #EBEDF0; border-right: 1px solid #EBEDF0;
margin-left: 20px; margin-left: 28px;
margin-right: 20px; margin-right: 28px;
} }
.searchBox { .searchBox {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: 28px 24px 0 24px; margin: 24px 24px 0 24px;
} }
.searchLineLeft { .searchLineLeft {
......
...@@ -43,7 +43,10 @@ const ProjectOverview = observer(() => { ...@@ -43,7 +43,10 @@ const ProjectOverview = observer(() => {
setTaskList(result.data.content); setTaskList(result.data.content);
}, },
}); });
useEffect(() => { useEffect(() => {
// 切项目时重置为初始7
setDay("7");
if (currentProjectStore.currentProjectInfo.id) { if (currentProjectStore.currentProjectInfo.id) {
getOverview({ getOverview({
id: currentProjectStore.currentProjectInfo.id as string, id: currentProjectStore.currentProjectInfo.id as string,
...@@ -69,6 +72,10 @@ const ProjectOverview = observer(() => { ...@@ -69,6 +72,10 @@ const ProjectOverview = observer(() => {
} }
} }
const handleKeyWordChangeBlur=(e:any)=>{
setJobName(e.target.value);
}
const storageUnitFromB = (b: number) => { const storageUnitFromB = (b: number) => {
if (b <= 0) { if (b <= 0) {
return { data: '0.00', unit: 'KB' }; return { data: '0.00', unit: 'KB' };
...@@ -142,6 +149,7 @@ const ProjectOverview = observer(() => { ...@@ -142,6 +149,7 @@ const ProjectOverview = observer(() => {
</div> </div>
<SearchInput <SearchInput
onKeyUp={handleKeyWordChangeKeyUp} onKeyUp={handleKeyWordChangeKeyUp}
onBlur={handleKeyWordChangeBlur}
sx={{ width: 340 }} sx={{ width: 340 }}
/> />
</div> </div>
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
font-weight: 550; font-weight: 550;
margin-bottom: 12px; margin-bottom: 8px;
} }
.projectInfoName::after { .projectInfoName::after {
content: "*"; content: "*";
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
color: #ff4e4e; color: #ff4e4e;
margin-bottom: 8px; margin-bottom: 20px;
} }
.deleteText2 { .deleteText2 {
color: #1e2633; color: #1e2633;
......
...@@ -21,7 +21,6 @@ import { toJS } from "mobx"; ...@@ -21,7 +21,6 @@ import { toJS } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import InformationDisplay from "@/components/CommonComponents/InformationDisplay"; import InformationDisplay from "@/components/CommonComponents/InformationDisplay";
import classnames from "classnames"; import classnames from "classnames";
import { TextField } from "@mui/material";
import LoadingButton from "@mui/lab/LoadingButton"; import LoadingButton from "@mui/lab/LoadingButton";
import InputAdornment from "@mui/material/InputAdornment"; import InputAdornment from "@mui/material/InputAdornment";
import { useMessage } from "@/components/MySnackbar"; import { useMessage } from "@/components/MySnackbar";
...@@ -29,11 +28,8 @@ import Loading from "@/views/Loading"; ...@@ -29,11 +28,8 @@ import Loading from "@/views/Loading";
import MyDialog from "@/components/mui/MyDialog"; import MyDialog from "@/components/mui/MyDialog";
import { getProjectList } from "../../project"; import { getProjectList } from "../../project";
import { checkIsNumberLetterChinese } from "@/utils/util"; import { checkIsNumberLetterChinese } from "@/utils/util";
import {
setFileServerEndPointLocalStorage,
getFiletokenAccordingToId,
} from "@/views/Project/project";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import MySelect, { optionsTransform } from "@/components/mui/MySelect";
import MyInput from "@/components/mui/MyInput"; import MyInput from "@/components/mui/MyInput";
type zoneIdOption = { type zoneIdOption = {
...@@ -205,7 +201,11 @@ const BaseInfo = observer(() => { ...@@ -205,7 +201,11 @@ const BaseInfo = observer(() => {
showMessage && message.error(help); showMessage && message.error(help);
return false; return false;
} else if (budget) { } else if (budget) {
if (isNaN(Number(budget)) || Number(budget) > 10000000 || Number(budget) < 0) { if (
isNaN(Number(budget)) ||
Number(budget) > 10000000 ||
Number(budget) < 0
) {
help = "格式错误,请输入0~10000000之间的数值,结果最高保留两位小数。"; help = "格式错误,请输入0~10000000之间的数值,结果最高保留两位小数。";
setBudgetCheck({ setBudgetCheck({
error: true, error: true,
...@@ -229,7 +229,7 @@ const BaseInfo = observer(() => { ...@@ -229,7 +229,7 @@ const BaseInfo = observer(() => {
showMessage && message.error(help); showMessage && message.error(help);
return false; return false;
} }
} };
const budgetChange = (e: any) => { const budgetChange = (e: any) => {
setProjectInfo({ setProjectInfo({
...@@ -237,13 +237,16 @@ const BaseInfo = observer(() => { ...@@ -237,13 +237,16 @@ const BaseInfo = observer(() => {
projectBudget: e.target.value, projectBudget: e.target.value,
}); });
checkBudget(e.target.value); checkBudget(e.target.value);
} };
const budgetBlur = (e: any) => { const budgetBlur = (e: any) => {
if (e.target.value.indexOf(" ") == -1 && e.target.value) { if (e.target.value.indexOf(" ") === -1 && e.target.value) {
setProjectInfo({ setProjectInfo({
...projectInfo, ...projectInfo,
projectBudget: Number(e.target.value) || Number(e.target.value) == 0 ? Number(e.target.value).toFixed(2) : e.target.value, projectBudget:
Number(e.target.value) || Number(e.target.value) === 0
? Number(e.target.value).toFixed(2)
: e.target.value,
}); });
} }
}; };
...@@ -262,7 +265,10 @@ const BaseInfo = observer(() => { ...@@ -262,7 +265,10 @@ const BaseInfo = observer(() => {
// 修改项目 // 修改项目
const handleClickUpdate = () => { const handleClickUpdate = () => {
if (checkName(projectInfo.name, true) && checkBudget(projectInfo.projectBudget, true)) { if (
checkName(projectInfo.name, true) &&
checkBudget(projectInfo.projectBudget, true)
) {
updateProjectRun({ ...projectInfo, product: "cadd" }); updateProjectRun({ ...projectInfo, product: "cadd" });
} else { } else {
return; return;
...@@ -276,20 +282,9 @@ const BaseInfo = observer(() => { ...@@ -276,20 +282,9 @@ const BaseInfo = observer(() => {
const projectList = await getProjectList(); const projectList = await getProjectList();
currentProjectStore.setProjectList(projectList); currentProjectStore.setProjectList(projectList);
// 项目删完了 // 项目删完了
if (projectList.length === 0) { currentProjectStore.changeProject({});
currentProjectStore.changeProject({}); localStorage.setItem("fileServerEndPoint", "");
localStorage.setItem("fileServerEndPoint", ""); setProjectInfo({});
setProjectInfo({});
} else {
projectList[0].filetoken = getFiletokenAccordingToId(projectList[0].id);
currentProjectStore.changeProject(projectList[0]);
setFileServerEndPointLocalStorage(projectList[0].zoneId);
getFiletokenAccordingToId(projectList[0].id).then((res) => {
projectList[0].filetoken = res;
currentProjectStore.changeProject(projectList[0]);
});
setProjectInfo(projectList[0]);
}
}, },
}); });
...@@ -349,34 +344,18 @@ const BaseInfo = observer(() => { ...@@ -349,34 +344,18 @@ const BaseInfo = observer(() => {
[style.projectInfoTextarea]: true, [style.projectInfoTextarea]: true,
})} })}
onChange={descChange} onChange={descChange}
placeholder="项目描述限制100字以内" placeholder="项目描述限制300字以内"
maxLength={100} maxLength={300}
></textarea> ></textarea>
{/* <MyInput
value={projectInfo.desc}
multiline
rows={4}
placeholder="项目描述限制100字以内"
onChange={descChange}
/> */}
</div> </div>
<div className={style.projectInfoListLi}> <div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>计算区</div> <div className={style.projectInfoListLiLabel}>计算区</div>
<select <MySelect
value={projectInfo.zoneId} value={projectInfo.zoneId}
disabled disabled
className={classnames({ options={optionsTransform(zoneIdOptions, "name", "id")}
[style.projectInfoListLiValue]: true, sx={{ width: "560px" }}
[style.projectInfoSelect]: true, ></MySelect>
[style.disable]: true,
})}
>
{zoneIdOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.name}
</option>
))}
</select>
</div> </div>
<div className={style.projectInfoListLi}> <div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>创建人</div> <div className={style.projectInfoListLiLabel}>创建人</div>
...@@ -410,8 +389,8 @@ const BaseInfo = observer(() => { ...@@ -410,8 +389,8 @@ const BaseInfo = observer(() => {
padding: "6.5px 2px", padding: "6.5px 2px",
}, },
"& .MuiTypography-root": { "& .MuiTypography-root": {
fontSize: '14px' fontSize: "14px",
} },
}} }}
/> />
</div> </div>
...@@ -425,29 +404,32 @@ const BaseInfo = observer(() => { ...@@ -425,29 +404,32 @@ const BaseInfo = observer(() => {
</div> </div>
<div className={style.projectInfoListLi}> <div className={style.projectInfoListLi}>
<LoadingButton <LoadingButton
variant="outlined" variant="contained"
className={style.updateButton} className={style.updateButton}
onClick={handleClickUpdate} onClick={handleClickUpdate}
loading={updateLoading} loading={updateLoading}
sx={{
height: "32px",
}}
> >
保存修改 保存修改
</LoadingButton> </LoadingButton>
</div> </div>
<div className={style.projectInfoListLi}> <div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>删除项目</div> <div
className={style.projectInfoListLiLabel}
style={{ paddingTop: "12px" }}
>
删除项目
</div>
<div className={style.projectInfoListLiText}> <div className={style.projectInfoListLiText}>
删除项目将删除其存储的数据和所有相关资源,并且已删除的项目无法恢复!请谨慎操作! 删除项目将删除其存储的数据和所有相关资源,并且已删除的项目无法恢复!请谨慎操作!
</div> </div>
<MyButton <MyButton
text="删除项目" text="删除项目"
variant="contained" variant="outlined"
onClick={() => setDialogOpen(true)} onClick={() => setDialogOpen(true)}
color="error" color="error"
// style={{
// backgroundColor: "#fff",
// color: "#FF4E4E",
// border: "1px solid #FF4E4E",
// }}
/> />
</div> </div>
<MyDialog <MyDialog
...@@ -459,10 +441,7 @@ const BaseInfo = observer(() => { ...@@ -459,10 +441,7 @@ const BaseInfo = observer(() => {
> >
<div className={style.deleteBox}> <div className={style.deleteBox}>
<div className={style.deleteText1}> <div className={style.deleteText1}>
您要删除本项目。已删除的项目将无法恢复!您真的确定吗? 删除项目会导致数据丢失,已删除的项目将无法恢复。
</div>
<div className={style.deleteText2}>
此操作可能会导致数据丢失。为防止意外操作,我们要求您确认您的操作。
</div> </div>
<div className={style.deleteText3}> <div className={style.deleteText3}>
请输入“ 请输入“
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13 * @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-28 18:10:42 * @LastEditTime: 2022-08-03 14:29:19
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -146,7 +146,7 @@ const AddMember = observer((props: IProps) => { ...@@ -146,7 +146,7 @@ const AddMember = observer((props: IProps) => {
} }
}); });
} else { } else {
Message.warning("请先选择项目成员!"); Message.info("请先选择项目成员!");
} }
}; };
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13 * @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-29 11:28:09 * @LastEditTime: 2022-08-11 18:05:06
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -37,15 +37,7 @@ const ChangePermission = observer((props: IProps) => { ...@@ -37,15 +37,7 @@ const ChangePermission = observer((props: IProps) => {
useEffect(() => { useEffect(() => {
if (permissionDialog?.isShow) { if (permissionDialog?.isShow) {
http.get<IResponse<any>>("/cpp/project/listroles").then((res) => { http.get<IResponse<any>>("/cpp/project/listroles").then((res) => {
const arr = []; setSelectOptions(res.data);
const { data } = res;
for (const key in data) {
arr.push({
label: String(data[key]),
value: key,
});
}
setSelectOptions(arr);
}); });
} }
}, [http, permissionDialog]); }, [http, permissionDialog]);
......
...@@ -164,7 +164,7 @@ const ProjectMembers = observer(() => { ...@@ -164,7 +164,7 @@ const ProjectMembers = observer(() => {
variant="contained" variant="contained"
onClick={onAddMember} onClick={onAddMember}
startIcon={<Add />} startIcon={<Add />}
size="small" size="medium"
/> />
) : null} ) : null}
</Box> </Box>
......
...@@ -12,7 +12,6 @@ import { Box } from "@mui/system"; ...@@ -12,7 +12,6 @@ import { Box } from "@mui/system";
import { useStores } from "@/store/index"; import { useStores } from "@/store/index";
import NoProject from "@/components/BusinessComponents/NoProject"; import NoProject from "@/components/BusinessComponents/NoProject";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import projectImg from "@/assets/project/projectIconSmall.svg";
import ProjectMembers from "./ProjectMembers"; import ProjectMembers from "./ProjectMembers";
import BaseInfo from "./BaseInfo"; import BaseInfo from "./BaseInfo";
import Tabs from "@/components/mui/Tabs"; import Tabs from "@/components/mui/Tabs";
...@@ -35,15 +34,21 @@ const ProjectSetting = observer(() => { ...@@ -35,15 +34,21 @@ const ProjectSetting = observer(() => {
component: <BaseInfo />, component: <BaseInfo />,
}, },
]; ];
}, []); }, [isPass]);
if (currentProjectStore.currentProjectInfo.name) { if (currentProjectStore.currentProjectInfo.name) {
return ( return (
<div style={{ padding: 24 }}> <div style={{ padding: "28px 24px 24px" }}>
<div style={{ display: "flex", alignItems: "center" }}> <div style={{ display: "flex", alignItems: "center" }}>
<img src={projectImg} alt="项目logo" /> <span
<span style={{ marginLeft: 12 }}> style={{
{currentProjectStore.currentProjectInfo.name} fontSize: "18px",
lineHeight: "26px",
fontWeight: "600",
color: "#1E2633",
}}
>
项目设置
</span> </span>
</div> </div>
<Box sx={{ width: "100%", typography: "body1" }}> <Box sx={{ width: "100%", typography: "body1" }}>
......
...@@ -20,14 +20,16 @@ export const getCheckResult = ( ...@@ -20,14 +20,16 @@ export const getCheckResult = (
helperText = "该选项是必填项"; helperText = "该选项是必填项";
} }
} }
if (parameter.validators.length > 0) { if (parameter?.validators) {
parameter.validators.forEach((validator) => { if (parameter.validators.length > 0) {
const reg = new RegExp(validator.regex); parameter.validators.forEach((validator) => {
if (!reg.test(value)) { const reg = new RegExp(validator.regex);
error = true; if (!reg.test(value)) {
helperText = validator.message; error = true;
} helperText = validator.message;
}); }
});
}
} }
return { return {
error, error,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment