From 7ef9387c8b7f1add2876457690a2720f18065e01 Mon Sep 17 00:00:00 2001 From: zhu-mingye <934230207@qq.com> Date: Wed, 1 Nov 2023 15:13:32 +0800 Subject: [PATCH] refactor dva --- .../dinky/controller/DataBaseController.java | 22 +- .../dinky/controller/FragmentController.java | 14 +- .../controller/SuggestionController.java | 65 ++++ .../org/dinky/data/dto/SuggestionDTO.java | 34 ++ .../data/model/suggestion/Suggestion.java | 76 ++++ .../model/suggestion/SuggestionLabel.java | 43 +++ .../org/dinky/service/SuggestionService.java | 50 +++ .../service/impl/SuggestionServiceImpl.java | 160 ++++++++ .../CustomEditor/CodeEdit/function.tsx | 116 ++++-- .../CustomEditor/CodeEdit/index.tsx | 68 +++- .../CustomEditor/CodeEdit/keyword.json | 1 - .../CustomEditor/CodeShow/index.tsx | 17 +- .../Sidebar/MovableSidebar/index.tsx | 7 +- .../DataStudio/BottomContainer/data.d.ts | 4 + .../DataStudio/BottomContainer/model.tsx | 25 ++ .../DataStudio/FooterContainer/data.d.ts | 39 ++ .../DataStudio/FooterContainer/model.tsx | 160 ++++++++ .../DataStudio/HeaderContainer/data.d.ts | 4 + .../DataStudio/HeaderContainer/model.tsx | 27 ++ .../model/DataStudio/LayoutState/data.d.ts | 61 +++ .../model/DataStudio/LayoutState/model.tsx | 360 ++++++++++++++++++ .../model/DataStudio/LeftContainer/data.d.ts | 27 ++ .../model/DataStudio/LeftContainer/model.tsx | 244 ++++++++++++ .../model/DataStudio/LeftContainer/service.ts | 0 .../DataStudio/MiddleContainer/data.d.ts | 46 +++ .../DataStudio/MiddleContainer/model.tsx | 186 +++++++++ .../model/DataStudio/RightContainer/data.d.ts | 58 +++ .../model/DataStudio/RightContainer/model.tsx | 151 ++++++++ dinky-web/src/model/DataStudio/service.ts | 0 dinky-web/src/{models => model/SSE}/Sse.tsx | 0 .../BottomContainer/TableData/index.tsx | 2 +- .../DataStudio/FooterContainer/index.tsx | 20 +- .../HeaderContainer/Explain/index.tsx | 11 +- .../HeaderContainer/FlinkGraph/index.tsx | 4 +- .../DataStudio/HeaderContainer/index.tsx | 13 +- .../LeftContainer/Catalog/index.tsx | 8 +- .../LeftContainer/Project/JobModal/index.tsx | 10 +- .../LeftContainer/Project/JobTree/index.tsx | 41 +- .../Project/ProjectTitle/index.tsx | 12 +- .../LeftContainer/Project/function.tsx | 24 +- .../LeftContainer/Project/index.tsx | 46 ++- .../pages/DataStudio/LeftContainer/index.tsx | 96 +++-- .../MiddleContainer/Editor/index.tsx | 102 ++--- .../DataStudio/MiddleContainer/index.tsx | 112 +++--- .../RightContainer/HistoryVersion/index.tsx | 13 +- .../RightContainer/JobConfig/index.tsx | 104 ++--- .../RightContainer/JobConfig/service.tsx | 5 + .../RightContainer/JobInfo/index.tsx | 17 +- .../RightContainer/SavePoints/index.tsx | 39 +- .../pages/DataStudio/RightContainer/index.tsx | 76 ++-- dinky-web/src/pages/DataStudio/function.ts | 53 +-- dinky-web/src/pages/DataStudio/index.tsx | 98 ++--- .../JobMetrics/JobChart/JobChart.tsx | 2 +- dinky-web/src/pages/Metrics/index.tsx | 2 +- dinky-web/src/pages/Other/Login/index.tsx | 4 +- .../components/DataSourceList/index.tsx | 23 +- .../pages/RegCenter/DataSource/service.tsx | 2 +- .../components/DocumentProTable/index.tsx | 2 +- dinky-web/src/services/endpoints.tsx | 5 +- dinky-web/src/types/Public/data.ts | 10 + 60 files changed, 2560 insertions(+), 461 deletions(-) create mode 100644 dinky-admin/src/main/java/org/dinky/controller/SuggestionController.java create mode 100644 dinky-admin/src/main/java/org/dinky/data/dto/SuggestionDTO.java create mode 100644 dinky-admin/src/main/java/org/dinky/data/model/suggestion/Suggestion.java create mode 100644 dinky-admin/src/main/java/org/dinky/data/model/suggestion/SuggestionLabel.java create mode 100644 dinky-admin/src/main/java/org/dinky/service/SuggestionService.java create mode 100644 dinky-admin/src/main/java/org/dinky/service/impl/SuggestionServiceImpl.java create mode 100644 dinky-web/src/model/DataStudio/BottomContainer/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/BottomContainer/model.tsx create mode 100644 dinky-web/src/model/DataStudio/FooterContainer/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/FooterContainer/model.tsx create mode 100644 dinky-web/src/model/DataStudio/HeaderContainer/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/HeaderContainer/model.tsx create mode 100644 dinky-web/src/model/DataStudio/LayoutState/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/LayoutState/model.tsx create mode 100644 dinky-web/src/model/DataStudio/LeftContainer/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/LeftContainer/model.tsx create mode 100644 dinky-web/src/model/DataStudio/LeftContainer/service.ts create mode 100644 dinky-web/src/model/DataStudio/MiddleContainer/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/MiddleContainer/model.tsx create mode 100644 dinky-web/src/model/DataStudio/RightContainer/data.d.ts create mode 100644 dinky-web/src/model/DataStudio/RightContainer/model.tsx create mode 100644 dinky-web/src/model/DataStudio/service.ts rename dinky-web/src/{models => model/SSE}/Sse.tsx (100%) diff --git a/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java b/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java index 2914165c916..3f8c4e8fd27 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/DataBaseController.java @@ -54,6 +54,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; +import cn.hutool.core.collection.CollUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -81,7 +82,7 @@ public class DataBaseController { * @param dataBaseDTO {@link DataBaseDTO} * @return {@link Result}< {@link Void}> */ - @PutMapping + @PutMapping("/saveOrUpdateDataBase") @Log(title = "Insert Or Update DataBase", businessType = BusinessType.INSERT_OR_UPDATE) @ApiOperation("Insert Or Update DataBase") @ApiImplicitParam( @@ -110,24 +111,17 @@ public Result saveOrUpdateDataBase(@RequestBody DataBaseDTO dataBaseDTO) { * @param para {@link JsonNode} * @return {@link ProTableResult}< {@link DataBase}> */ - @PostMapping + @GetMapping("/listAll") @ApiOperation("DataBase Get All") - @ApiImplicitParam( - name = "para", - value = "JsonNode", - required = true, - dataType = "JsonNode", - paramType = "body", - dataTypeClass = JsonNode.class) - public ProTableResult listDataBases(@RequestBody JsonNode para) { - final ProTableResult result = databaseService.selectForProTable(para); + public Result> listDataBases() { + final List dataBaseList = databaseService.list(); // 密码不返回 - if (result != null && result.getData() != null) { - for (DataBase data : result.getData()) { + if (CollUtil.isNotEmpty(dataBaseList)) { + for (DataBase data : dataBaseList) { data.setPassword(null); } } - return result; + return Result.succeed(dataBaseList); } /** diff --git a/dinky-admin/src/main/java/org/dinky/controller/FragmentController.java b/dinky-admin/src/main/java/org/dinky/controller/FragmentController.java index 055da434772..ec2479de34f 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/FragmentController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/FragmentController.java @@ -29,7 +29,10 @@ import org.dinky.service.FragmentVariableService; import org.dinky.utils.FragmentVariableUtils; +import java.util.List; + import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -91,7 +94,6 @@ public Result saveOrUpdateFragment(@RequestBody FragmentVariable fragmentV * @return {@link ProTableResult} of {@link FragmentVariable} */ @PostMapping - @Log(title = "FragmentVariable List", businessType = BusinessType.QUERY) @ApiOperation("FragmentVariable List") @ApiImplicitParam( name = "para", @@ -112,6 +114,16 @@ public ProTableResult listFragmentVariable(@RequestBody JsonNo } return result; } + /** + * query list enable + * @return {@link Result} of {@link List} + */ + @GetMapping("/listEnable") + @Log(title = "FragmentVariable List Enable All", businessType = BusinessType.QUERY) + @ApiOperation("FragmentVariable List Enable All") + public Result> listFragmentVariableEnable() { + return Result.succeed(fragmentVariableService.listEnabledAll()); + } /** * delete by id diff --git a/dinky-admin/src/main/java/org/dinky/controller/SuggestionController.java b/dinky-admin/src/main/java/org/dinky/controller/SuggestionController.java new file mode 100644 index 00000000000..58417e03bd6 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/controller/SuggestionController.java @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.controller; + +import org.dinky.data.dto.SuggestionDTO; +import org.dinky.data.model.suggestion.Suggestion; +import org.dinky.data.result.Result; +import org.dinky.service.SuggestionService; + +import java.util.List; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.annotations.Api; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestController +@Api(tags = "Suggestion Controller") +@RequestMapping("/api/suggestion") +@RequiredArgsConstructor +public class SuggestionController { + + private final SuggestionService suggestionService; + + @PostMapping("/queryAllSuggestions") + public Result> queryAllSuggestions(@RequestBody SuggestionDTO suggestionDTO) { + return Result.succeed(suggestionService.getSuggestions(suggestionDTO.isEnableSchemaSuggestion())); + } + + @PostMapping("/queryAllSuggestionsByKeyWord") + public Result> queryAllSuggestionsByKeyWord(@RequestBody SuggestionDTO suggestionDTO) { + return Result.succeed(suggestionService.getSuggestionsByKeyWord( + suggestionDTO.isEnableSchemaSuggestion(), suggestionDTO.getKeyWord())); + } + + @PostMapping("/queryAllSuggestionsBySqlStatement") + public Result> queryAllSuggestionsBySqlStatement(@RequestBody SuggestionDTO suggestionDTO) { + return Result.succeed(suggestionService.getSuggestionsBySqlStatement( + suggestionDTO.isEnableSchemaSuggestion(), + suggestionDTO.getSqlStatement(), + suggestionDTO.getPosition())); + } +} diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/SuggestionDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/SuggestionDTO.java new file mode 100644 index 00000000000..c093f358635 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/data/dto/SuggestionDTO.java @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.data.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SuggestionDTO { + private boolean enableSchemaSuggestion; + private String keyWord; + private String sqlStatement; + private int position; +} diff --git a/dinky-admin/src/main/java/org/dinky/data/model/suggestion/Suggestion.java b/dinky-admin/src/main/java/org/dinky/data/model/suggestion/Suggestion.java new file mode 100644 index 00000000000..bf4764aa81e --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/data/model/suggestion/Suggestion.java @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.data.model.suggestion; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Suggestion implements Serializable { + + private static final long serialVersionUID = 4631906403475634212L; + // label + private SuggestionLabel label; + // kind + /** + * 此字段值可选为: + *

+ * Method = 0, // 方法 + * Function = 1, // 函数 + * Constructor = 2, // 构造函数 + * Field = 3, // 字段 + * Variable = 4, // 变量 + * Class = 5, // 类 + * Struct = 6, // 结构体 + * Interface = 7, // 接口 + * Module = 8, // 模块 + * Property = 9, // 属性 + * Event = 10, // 事件 + * Operator = 11, // 操作符 + * Unit = 12, // 单元 + * Value = 13, // 值 + * Constant = 14, // 常量 + * Enum = 15, // 枚举 + * EnumMember = 16, // 枚举成员 + * Keyword = 17, // 关键字 + * Text = 18, // 文本 + * Color = 19, // 颜色 + * File = 20, // 文件 + * Reference = 21, // 引用 + * Customcolor = 22, // 自定义颜色 + * Folder = 23, // 文件夹 + * TypeParameter = 24, // 类型参数 + * User = 25, // 用户 + * Issue = 26, // 问题 + * Snippet = 27 // 代码片段 + */ + private int kind; + // insertText + private String insertText; + // detail + private String detail; +} diff --git a/dinky-admin/src/main/java/org/dinky/data/model/suggestion/SuggestionLabel.java b/dinky-admin/src/main/java/org/dinky/data/model/suggestion/SuggestionLabel.java new file mode 100644 index 00000000000..166543c1c2a --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/data/model/suggestion/SuggestionLabel.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.data.model.suggestion; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SuggestionLabel implements Serializable { + + private static final long serialVersionUID = 4631906403475634212L; + + // label + private String label; + // value + private String detail; + // description + private String description; +} diff --git a/dinky-admin/src/main/java/org/dinky/service/SuggestionService.java b/dinky-admin/src/main/java/org/dinky/service/SuggestionService.java new file mode 100644 index 00000000000..db1940c75cc --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/service/SuggestionService.java @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.service; + +import org.dinky.data.model.suggestion.Suggestion; + +import java.util.List; + +public interface SuggestionService { + + /** + * 获取编辑器的建议列表 + * @param enableSchemaSuggestion 是否启用schema建议 + * @return 建议列表 + */ + List getSuggestions(boolean enableSchemaSuggestion); + + /** + * 根据关键词获取建议列表 + * @param enableSchemaSuggestion 是否启用schema建议 + * @param keyWord 关键词 + * @return 建议列表 + */ + List getSuggestionsByKeyWord(boolean enableSchemaSuggestion, String keyWord); + + /** + * 根据sql获取建议列表 + * @param enableSchemaSuggestion 是否启用schema建议 + * @param sqlStatement sql语句 + * @return 建议列表 + */ + List getSuggestionsBySqlStatement(boolean enableSchemaSuggestion, String sqlStatement, int position); +} diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/SuggestionServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/SuggestionServiceImpl.java new file mode 100644 index 00000000000..2618937f579 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/service/impl/SuggestionServiceImpl.java @@ -0,0 +1,160 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.service.impl; + +import org.dinky.data.model.Document; +import org.dinky.data.model.FragmentVariable; +import org.dinky.data.model.suggestion.Suggestion; +import org.dinky.data.model.suggestion.SuggestionLabel; +import org.dinky.service.DocumentService; +import org.dinky.service.FragmentVariableService; +import org.dinky.service.SuggestionService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Service +@RequiredArgsConstructor +@Slf4j +public class SuggestionServiceImpl implements SuggestionService { + + private final DocumentService documentService; + private final FragmentVariableService fragmentVariableService; + + /** + * 获取编辑器的建议列表 + * @param enableSchemaSuggestion 是否启用schema建议 + * @return 建议列表 + */ + @Override + public List getSuggestions(boolean enableSchemaSuggestion) { + List suggestions = new ArrayList<>(); + // 1. 构建文档的建议列表 + buildDocumentSuggestions(documentService.list(), suggestions); + // 2. 全局变量的建议列表 + buildGlobalVariableSuggestions(fragmentVariableService.listEnabledAll(), suggestions); + // todo: 如果启用了schema,需要构建schema的建议列表 + // 3. schema的建议列表 + if (enableSchemaSuggestion) { + // buildSchemaSuggestions(schemaService.list(), documentSuggestions); + } + // 4. 自定义关键词提示 + buildCustomSuggestions(new ArrayList<>(), suggestions); + + return suggestions; + } + + /** + * 构建全局变量的建议列表 + * @param fragmentVariableList + * @param suggestions + */ + private static void buildGlobalVariableSuggestions( + List fragmentVariableList, List suggestions) { + fragmentVariableList.stream() + .map(fragmentVariable -> { + SuggestionLabel suggestionLabel = SuggestionLabel.builder() + .label(fragmentVariable.getName()) + .detail(fragmentVariable.getNote()) + .description(fragmentVariable.getNote()) + .build(); + return Suggestion.builder() + .label(suggestionLabel) + .insertText(fragmentVariable.getFragmentValue()) + .detail(fragmentVariable.getNote()) + .build(); + }) + .forEach(suggestions::add); + } + + /** + * 构建schema的建议列表 + * @param buildingSchemaList + * @param suggestions + */ + private static void buildSchemaSuggestions(List buildingSchemaList, List suggestions) { + // todo: 构建schema的建议列表 , 包含 库名 、表名、字段名、.... , 能做到根据库名点出表名,根据表名点出字段名 + } + + /** + * 构建自定义关键词提示 + * @param customKeyWordList + * @param suggestions + */ + private static void buildCustomSuggestions(List customKeyWordList, List suggestions) { + // todo: 自定义关键词提示, + // 1. 此处自定义是属于 dinky 内部自定义语法关键词提示, 如果有片段, 将片段的建议列表加入到文档中进行提示 + // 2. 可以加入 yml 语法的关键词提示 , 因为在集群配置中会有 yml 的配置文件写法 , 获取方式待定 + } + + private static void buildDocumentSuggestions(List documentList, List suggestions) { + documentList.stream() + .map(document -> { + String detail = + document.getCategory() + " ->" + document.getType() + " -> " + document.getSubtype(); + SuggestionLabel suggestionLabel = SuggestionLabel.builder() + .label(document.getName()) + .detail(detail) + .description(document.getDescription()) + .build(); + return Suggestion.builder() + .label(suggestionLabel) + .insertText(document.getFillValue()) + .kind(27) + .detail(detail) + .build(); + }) + .forEach(suggestions::add); + } + + /** + * 根据关键词获取建议列表 + * @param enableSchemaSuggestion 是否启用schema建议 + * @param keyWord 关键词 + * @return + */ + @Override + public List getSuggestionsByKeyWord(boolean enableSchemaSuggestion, String keyWord) { + return getSuggestions(enableSchemaSuggestion).stream() + .filter(suggestion -> suggestion.getLabel().getLabel().contains(keyWord)) + .collect(Collectors.toList()); + } + /** + * 根据sql获取建议列表 + * todo: 根据传入的sql,获取建议列表 + * + * @param enableSchemaSuggestion 是否启用schema建议 + * @param sqlStatement sql语句 + * @return 建议列表 + */ + @Override + public List getSuggestionsBySqlStatement( + boolean enableSchemaSuggestion, String sqlStatement, int position) { + // todo: 根据传入的sql,获取建议列表, 需要和flink的sql解析器结合起来 + return Arrays.asList(); + } +} diff --git a/dinky-web/src/components/CustomEditor/CodeEdit/function.tsx b/dinky-web/src/components/CustomEditor/CodeEdit/function.tsx index 867a41726f8..7e07e93351a 100644 --- a/dinky-web/src/components/CustomEditor/CodeEdit/function.tsx +++ b/dinky-web/src/components/CustomEditor/CodeEdit/function.tsx @@ -15,30 +15,47 @@ * limitations under the License. */ -import { Column, ISuggestions, MetaData } from '@/components/CustomEditor/CodeEdit/data'; +import { Column, MetaData } from '@/components/CustomEditor/CodeEdit/data'; import { Document, GlobalVar } from '@/types/RegCenter/data.d'; import * as monaco from 'monaco-editor'; import keyWordJsonData from './keyword.json'; +import {CancellationToken, editor, languages, Position} from "monaco-editor"; +import ProviderResult = languages.ProviderResult; +import CompletionList = languages.CompletionList; +import CompletionItem = languages.CompletionItem; +import CompletionContext = languages.CompletionContext; +import {queryDataByParams} from "@/services/BusinessCrud"; +import {API_CONSTANTS} from "@/services/endpoints"; // todo: get sqlMetaData from interface -const buildMetaDataSuggestions = () => { - let metaDataSuggestions: ISuggestions[] = []; +const buildMetaDataSuggestions = (range: monaco.Range) => { + let metaDataSuggestions: CompletionItem[] = []; let metaData: [] = []; // temp metaData.forEach((item: MetaData) => { metaDataSuggestions.push({ - label: item.table, + label: { + label: item.table, + detail: item.connector, + description: item.table, + }, kind: monaco.languages.CompletionItemKind.Constant, insertText: item.table, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - detail: 'FlinkSQL Connector => ' + item.connector + detail: 'MetaData Of Table => ' + item.connector, + range: range }); item.columns.forEach((column: Column) => { metaDataSuggestions.push({ - label: column.name, + label: { + label: column.name, + detail: column.name + ' from ' + item.table, + description: column.name, + }, kind: monaco.languages.CompletionItemKind.Field, insertText: column.name, + range: range, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - detail: 'Column => ' + column.type + ' from ' + item.table + detail: 'MetaData Of Column => ' + column.type + ' from ' + item.table }); }); }); @@ -46,14 +63,19 @@ const buildMetaDataSuggestions = () => { }; // todo: get document from interface -const buildFullDocumentSuggestions = () => { - let fullDocumentSuggestions: ISuggestions[] = []; +const buildFullDocumentSuggestions = (range:monaco.Range) => { + let fullDocumentSuggestions: CompletionItem[] = []; let fillDocuments: [] = []; // temp fillDocuments.forEach((item: Document) => { fullDocumentSuggestions.push({ - label: item.name, + label: { + label: item.name, + detail: item.name, + description: item.name, + }, kind: monaco.languages.CompletionItemKind.Snippet, insertText: item.fillValue, + range: range, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, detail: item.description }); @@ -72,18 +94,23 @@ const parseKeyWordJson = (type: string) => { /** * build keyWordJsonSuggestions from {@link ./keyword.json} */ -const buildKeyWordJsonSuggestions = () => { - let keyWordJsonSuggestions: ISuggestions[] = []; +const buildKeyWordJsonSuggestions = (range:monaco.Range) => { + let keyWordJsonSuggestions: CompletionItem[] = []; // get all keys from keyWordJsonData Object.keys(keyWordJsonData).forEach((key: string) => { // parse data of key - const array = parseKeyWordJson(key); + const array = parseKeyWordJson(key) ?? []; // build suggestions array.forEach((item: string) => { keyWordJsonSuggestions.push({ - label: item, + label: { + label: item, + // detail: key + ' => ' + item, + description: key + ' => ' + item, + }, kind: monaco.languages.CompletionItemKind.Keyword, insertText: item, + range: range, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, detail: key }); @@ -93,30 +120,53 @@ const buildKeyWordJsonSuggestions = () => { }; // todo: get globalVariable from interface -const buildGlobalVariableSuggestions = () => { - let globalVariableSuggestions: ISuggestions[] = []; - let globalVariables: [] = []; // temp - globalVariables.forEach((item: GlobalVar) => { - globalVariableSuggestions.push({ - label: item.name, - kind: monaco.languages.CompletionItemKind.Variable, - insertText: item.fragmentValue, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - detail: item.note - }); - }); +const buildGlobalVariableSuggestions = (range:monaco.Range) => { + let globalVariableSuggestions: CompletionItem[] = []; + queryDataByParams(API_CONSTANTS.GLOBAL_VARIABLE_ENABLE_LIST).then(res =>{ + if(res){ + let globalVariables: GlobalVar[] = res as GlobalVar[]; + globalVariables?.forEach((item: GlobalVar) => { + globalVariableSuggestions.push({ + label: { + label: item.name, + detail: item.note, + description: item.note, + }, + kind: monaco.languages.CompletionItemKind.Variable, + insertText: item.fragmentValue, + range: range, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + detail: item.note + }); + }); + } + }) return globalVariableSuggestions; }; /** * build all suggestions */ -export const buildAllSuggestions = () => { - let suggestions: ISuggestions[] = []; +export function buildAllSuggestions(model: editor.ITextModel, position: Position, context: CompletionContext, token: CancellationToken) : ProviderResult { + + const languageId = model.getLanguageId(); + const word = model.getWordUntilPosition(position); + const wordRange = new monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); + // get value in range + const valueInRange = model.getValueInRange(wordRange); + // 根据不同的语言,返回不同的建议 + + let suggestions: CompletionItem[] = []; suggestions = suggestions - .concat(buildMetaDataSuggestions()) // concat metaDataSuggestions - .concat(buildFullDocumentSuggestions()) // concat fullDocumentSuggestions - .concat(buildGlobalVariableSuggestions()) // concat globalVariableSuggestions - .concat(buildKeyWordJsonSuggestions()); // concat keyWordJsonSuggestions - return suggestions; + .concat(buildMetaDataSuggestions(wordRange)) // concat metaDataSuggestions + .concat(buildFullDocumentSuggestions(wordRange)) // concat fullDocumentSuggestions + .concat(buildGlobalVariableSuggestions(wordRange)) // concat globalVariableSuggestions + .concat(buildKeyWordJsonSuggestions(wordRange)); // concat keyWordJsonSuggestions + + console.log(suggestions) + + return { + suggestions: suggestions, + incomplete: false + }; }; diff --git a/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx b/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx index f181efac460..8b544e07c7b 100644 --- a/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx +++ b/dinky-web/src/components/CustomEditor/CodeEdit/index.tsx @@ -17,14 +17,14 @@ * */ -import * as monaco from 'monaco-editor'; - import EditorFloatBtn from '@/components/CustomEditor/EditorFloatBtn'; import { MonacoEditorOptions } from '@/types/Public/data'; import { convertCodeEditTheme } from '@/utils/function'; -import { Editor, OnChange } from '@monaco-editor/react'; -import { editor } from 'monaco-editor'; +import {Editor, OnChange, useMonaco} from '@monaco-editor/react'; +import {editor} from 'monaco-editor'; import { useState } from 'react'; +import {buildAllSuggestions} from "@/components/CustomEditor/CodeEdit/function"; +import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'; export type CodeEditFormProps = { height?: string; @@ -35,14 +35,17 @@ export type CodeEditFormProps = { code: string; readOnly?: boolean; lineNumbers?: string; - theme?: string; autoWrap?: string; showFloatButton?: boolean; + enableSuggestions?: boolean; }; const CodeEdit = (props: CodeEditFormProps) => { const [editorRef, setEditorRef] = useState(); + // 使用编辑器钩子, 拿到编辑器实例 + const monaco = useMonaco(); + /** * 1. height: edit height * 2. width: edit width @@ -67,13 +70,24 @@ const CodeEdit = (props: CodeEditFormProps) => { code, // content readOnly = false, // is readOnly lineNumbers, // show lineNumbers - theme, // edit theme autoWrap = 'on', // auto wrap - showFloatButton = false + showFloatButton = false, + enableSuggestions = false } = props; const { ScrollType } = editor; + const languageId = monaco?.languages.getEncodedLanguageId(language || 'sql'); + monaco?.languages.registerCompletionItemProvider(language || 'sql', { + provideCompletionItems: buildAllSuggestions, + }) + // monaco?.editor.getEditors().forEach((editor) => { + // if (editor.getModel()?.getLanguageId() === languageId) { + // editor.trigger('anyString', 'editor.action.triggerSuggest', {}); + // } + // }); + + /** * editorDidMount * @param {editor.IStandaloneCodeEditor} editor @@ -111,10 +125,6 @@ const CodeEdit = (props: CodeEditFormProps) => { editorRef?.setScrollPosition({ scrollTop: editorRef.getScrollTop() - 500 }, ScrollType.Smooth); }; - // register TypeScript language service - monaco.languages.register({ - id: language || 'typescript' - }); const restEditBtnProps = { handleBackTop, @@ -136,7 +146,41 @@ const CodeEdit = (props: CodeEditFormProps) => { readOnly, wordWrap: autoWrap, autoDetectHighContrast: true, - lineNumbers + lineNumbers, + // suggest: { + // screenReaderAnnounceInlineSuggestion: true, // 是否在内联建议中读出屏幕阅读器的提示 + // quickSuggestions: true, // 是否开启快速建议 + // showWords: enableSuggestions, // 是否显示单词建议 + // insertMode: 'insert', // 插入模式 insert | replace + // maxSuggestions: 20, // 最大建议数量 + // localityBonus: true, // 是否根据单词位置计算建议 + // shareSuggestSelections: true, // 是否共享建议选择 + // fixedOverflowWidgets: true, // 是否固定溢出的建议 + // selectionMode: 'whenTriggerCharacter', // 选择模式, 这里使用触发字符模式 + // preview: true, // 是否预览 + // previewMode: 'prefix', // 预览模式 + // showIcons: true, // 是否显示图标 + // showInlineDetails: true, // 是否显示内联详情 + // showMethods: true, // 是否显示方法 + // showFunctions: true, // 是否显示函数 + // showKeywords: true, // 是否显示关键字 + // showClasses: true, // 是否显示类 + // showStructs: true, // 是否显示结构 + // showValues: true, // 是否显示值 + // showUnits: true, // 是否显示单位 + // showEnumMembers: true, // 是否显示枚举成员 + // showColors: true, // 是否显示颜色 + // showFiles: true, // 是否显示文件 + // showReferences: true, // 是否显示引用 + // showFolders: true, // 是否显示文件夹 + // showTypeParameters: true, // 是否显示类型参数 + // showIssues: true, // 是否显示问题 + // showConstants: true, // 是否显示常量 + // showStructMembers: true, // 是否显示结构成员 + // showEvents: true, // 是否显示事件 + // showVariables: true, // 是否显示变量 + // + // }, }} className={'editor-develop'} onMount={editorDidMount} diff --git a/dinky-web/src/components/CustomEditor/CodeEdit/keyword.json b/dinky-web/src/components/CustomEditor/CodeEdit/keyword.json index 3a345eb5462..93a103bdc73 100644 --- a/dinky-web/src/components/CustomEditor/CodeEdit/keyword.json +++ b/dinky-web/src/components/CustomEditor/CodeEdit/keyword.json @@ -1,5 +1,4 @@ { - "_comment": "this is comments,按照数据库类型输入对应得关键字数组,如果想要函数提示也可,函数内得参数定位需要手动编写:eg:SUM(${1:}) 在输入后会将光标定位在括号内,多个参数列表 则数字依次向下+1, presto & phoenix & clickhouse 暂未整理", "fragments": [ "CASE WHEN ${1:} THEN ${2:} ELSE ${3:} END AS ${4:}", "SELECT ${1:} FROM ${2:}", diff --git a/dinky-web/src/components/CustomEditor/CodeShow/index.tsx b/dinky-web/src/components/CustomEditor/CodeShow/index.tsx index 3c2c459ed28..1cef54cf1c4 100644 --- a/dinky-web/src/components/CustomEditor/CodeShow/index.tsx +++ b/dinky-web/src/components/CustomEditor/CodeShow/index.tsx @@ -25,8 +25,10 @@ import { convertCodeEditTheme } from '@/utils/function'; import { Editor, useMonaco } from '@monaco-editor/react'; import { editor } from 'monaco-editor'; import { EditorLanguage } from 'monaco-editor/esm/metadata'; -import { CSSProperties, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import FullscreenBtn from '../FullscreenBtn'; +import {Loading} from "@/pages/Other/Loading"; +import {l} from "@/utils/intl"; // loader.config({monaco}); /** @@ -47,7 +49,7 @@ export type CodeShowFormProps = { showFloatButton?: boolean; refreshLogCallback?: () => void; fullScreenBtn?: boolean; - style?: CSSProperties; + style?: React.CSSProperties; }; const CodeShow = (props: CodeShowFormProps) => { @@ -116,7 +118,7 @@ const CodeShow = (props: CodeShowFormProps) => { const handleStopAutoRefresh = () => { setStopping(true); setInterval(() => { - clearInterval(timer); + clearInterval(timer as any); setStopping(false); setAutoRefresh(false); }, 1000); @@ -212,9 +214,13 @@ const CodeShow = (props: CodeShowFormProps) => { } + value={code ?? ''} language={language} + keepCurrentModel={false} options={{ + ...options, + readOnlyMessage: { value: l('pages.datastudio.editor.onlyread') }, scrollBeyondLastLine: false, readOnly: true, wordWrap: autoWrap, @@ -244,8 +250,7 @@ const CodeShow = (props: CodeShowFormProps) => { verticalScrollbarSize: 8, horizontalScrollbarSize: 8, arrowSize: 30 - }, - ...options + } }} onMount={editorDidMount} theme={convertCodeEditTheme()} diff --git a/dinky-web/src/components/Sidebar/MovableSidebar/index.tsx b/dinky-web/src/components/Sidebar/MovableSidebar/index.tsx index 954b4eebdd6..a9c20d39335 100644 --- a/dinky-web/src/components/Sidebar/MovableSidebar/index.tsx +++ b/dinky-web/src/components/Sidebar/MovableSidebar/index.tsx @@ -66,7 +66,8 @@ const MovableSidebar: React.FC = (props) => { contentHeight, handlerMinimize, handlerMaxsize, - tagList + tagList, + btnGroup, } = props; const [showBtn, setShowBtn] = useState(false); @@ -100,8 +101,8 @@ const MovableSidebar: React.FC = (props) => {
{title}
- {props.btnGroup} - } /> + {btnGroup} + } />
diff --git a/dinky-web/src/model/DataStudio/BottomContainer/data.d.ts b/dinky-web/src/model/DataStudio/BottomContainer/data.d.ts new file mode 100644 index 00000000000..093d5bd9829 --- /dev/null +++ b/dinky-web/src/model/DataStudio/BottomContainer/data.d.ts @@ -0,0 +1,4 @@ +export const DataStudioBottomState: string = 'DataStudioBottomState'; + + +export type DataStudioBottomStateType = {} diff --git a/dinky-web/src/model/DataStudio/BottomContainer/model.tsx b/dinky-web/src/model/DataStudio/BottomContainer/model.tsx new file mode 100644 index 00000000000..ef050017482 --- /dev/null +++ b/dinky-web/src/model/DataStudio/BottomContainer/model.tsx @@ -0,0 +1,25 @@ +import {createModelTypes} from "@/utils/modelUtils"; +import {DataStudioBottomState, DataStudioBottomStateType} from "@/model/DataStudio/BottomContainer/data.d"; +import { Effect, Reducer } from "@umijs/max"; + + +export type DataStudioBottomModelType = { + namespace: string; // 命名空间 + state: DataStudioBottomStateType; // state 数据| 数据类型 + effects: {}; + reducers: {}; +} + + +const DataStudioBottomModel: DataStudioBottomModelType = { + namespace: DataStudioBottomState, + state: { + }, + effects: {}, + reducers: {} +} + + +export const [DataStudioBottomStateModel,DataStudioBottomStateModelAsync] = createModelTypes(DataStudioBottomModel); + +export default DataStudioBottomModel; diff --git a/dinky-web/src/model/DataStudio/FooterContainer/data.d.ts b/dinky-web/src/model/DataStudio/FooterContainer/data.d.ts new file mode 100644 index 00000000000..c181c9f10be --- /dev/null +++ b/dinky-web/src/model/DataStudio/FooterContainer/data.d.ts @@ -0,0 +1,39 @@ +export const DataStudioFooterState: string = 'DataStudioFooterState'; + + +export type JobRunningInfo = { + taskId: string | number | null; + jobName: string; + jobStatus: string; + runningLog: string; +} + +export type FooterConfigInfo = { + codePosition: [number, number], // 当前光标所在的行列 + space: number, // 当前缩进的空格数 + codeEncoding: string, // 编码 + lineSeparator: string, // 换行符 + codeType: string, // 代码类型 + memoryInfo: string, // 内存信息 +} + +export type DataStudioFooterStateType = { + footerConfigInfo: FooterConfigInfo + jobRunningInfo: JobRunningInfo | JobRunningInfo[] +} + + +export const InitFooterConfigInfo: FooterConfigInfo = { + codePosition: [1, 1], + codeType: '', + memoryInfo: '', + lineSeparator: '', + codeEncoding: '', + space: 0, + jobRunningMsg: { + taskId: null, + jobName: '', + jobStatus: '', + runningLog: '', + } || [], +} diff --git a/dinky-web/src/model/DataStudio/FooterContainer/model.tsx b/dinky-web/src/model/DataStudio/FooterContainer/model.tsx new file mode 100644 index 00000000000..6efa6ed6980 --- /dev/null +++ b/dinky-web/src/model/DataStudio/FooterContainer/model.tsx @@ -0,0 +1,160 @@ +import {createModelTypes} from "@/utils/modelUtils"; + +import { + DataStudioFooterState, + DataStudioFooterStateType, +} from "@/model/DataStudio/FooterContainer/data.d"; +import { Reducer } from "@umijs/max"; +import {getFooterValue} from "@/pages/DataStudio/function"; + + +export type DataStudioFooterModelType = { + namespace: string; // 命名空间 + state: DataStudioFooterStateType; // state 数据| 数据类型 + effects: {}; + reducers: { + /** + * footer 基本信息 + */ + updateFooterConfigInfo: Reducer; // 更新 footerConfigInfo 总的 数据 + updateFooterConfigInfoCodePosition: Reducer; // 更新 footerConfigInfo codePosition + updateFooterConfigInfoSpace: Reducer; // 更新 footerConfigInfo space + updateFooterConfigInfoCodeEncoding: Reducer; // 更新 footerConfigInfo codeEncoding + updateFooterConfigInfoLineSeparator: Reducer; // 更新 footerConfigInfo lineSeparator + updateFooterConfigInfoCodeType: Reducer; // 更新 footerConfigInfo codeType + updateFooterConfigInfoMemoryInfo: Reducer; // 更新 footerConfigInfo memoryInfo + + /** + * 任务运行记录信息 后续可能废弃 + */ + updateJobRunningInfo: Reducer; // 更新 jobRunningInfo 总的 数据 + }; +} + + +const DataStudioFooterModel: DataStudioFooterModelType = { + namespace: DataStudioFooterState, + state: { + footerConfigInfo: { + codePosition: [1, 1], // 代码光标位置 + space: 2, // 缩进空格数 + codeEncoding: 'UTF-8', // 编码 + lineSeparator: 'LF', // 行分隔符 + codeType: '', // 代码类型 + memoryInfo: '100/500M', // 内存信息 + }, + // 任务运行信息 + jobRunningInfo: [] || { + taskId: null, // 任务id + jobName: '', // 任务名称 + jobStatus: '', // 任务状态 + runningLog: '' // 运行日志 + } + }, + effects: {}, + reducers: { + /** + * footer 基本信息 + */ + // 更新 footerConfigInfo 总的 数据 + updateFooterConfigInfo(state, {payload}) { + + const {activeKey ,tabList} = payload; + const itemTypes = tabList.filter((x: { key: any; }) => x.key === payload); + if (itemTypes.length === 1) { + let footerValue: any = getFooterValue(tabList, activeKey); + return { + ...state, + footerConfigInfo: { + ...footerValue + } + } + } + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + } + } + + }, + // 更新 footerConfigInfo codePosition + updateFooterConfigInfoCodePosition(state, {payload}) { + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + codePosition: payload + } + } + }, + // 更新 footerConfigInfo space + updateFooterConfigInfoSpace(state, {payload}) { + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + space: payload + } + } + }, + // 更新 footerConfigInfo codeEncoding + updateFooterConfigInfoCodeEncoding(state, {payload}) { + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + codeEncoding: payload + } + } + }, + // 更新 footerConfigInfo lineSeparator + updateFooterConfigInfoLineSeparator(state, {payload}) { + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + lineSeparator: payload + } + } + }, + // 更新 footerConfigInfo codeType + updateFooterConfigInfoCodeType(state, {payload}) { + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + codeType: payload + } + } + }, + // 更新 footerConfigInfo memoryInfo + updateFooterConfigInfoMemoryInfo(state, {payload}) { + return { + ...state, + footerConfigInfo: { + ...state.footerConfigInfo, + memoryInfo: payload + } + } + }, + + /** + * 任务运行记录信息 后续可能废弃 + */ + // 更新 jobRunningInfo 总的 数据 + updateJobRunningInfo(state, {payload}) { + return { + ...state, + jobRunningInfo: { + ...payload + } + } + }, + } +} + + +export const [DataStudioFooterStateModel, DataStudioFooterStateModelAsync] = createModelTypes(DataStudioFooterModel); + +export default DataStudioFooterModel; diff --git a/dinky-web/src/model/DataStudio/HeaderContainer/data.d.ts b/dinky-web/src/model/DataStudio/HeaderContainer/data.d.ts new file mode 100644 index 00000000000..f472c812144 --- /dev/null +++ b/dinky-web/src/model/DataStudio/HeaderContainer/data.d.ts @@ -0,0 +1,4 @@ +export const DataStudioHeaderState: string = 'DataStudioHeaderState'; + + +export type DataStudioHeaderStateType = {} diff --git a/dinky-web/src/model/DataStudio/HeaderContainer/model.tsx b/dinky-web/src/model/DataStudio/HeaderContainer/model.tsx new file mode 100644 index 00000000000..9c5df2b6f51 --- /dev/null +++ b/dinky-web/src/model/DataStudio/HeaderContainer/model.tsx @@ -0,0 +1,27 @@ +import {createModelTypes} from "@/utils/modelUtils"; + + +import {DataStudioHeaderState, DataStudioHeaderStateType} from "@/model/DataStudio/HeaderContainer/data.d"; +import { Effect, Reducer } from "@umijs/max"; + + +export type DataStudioHeaderModelType = { + namespace: string; // 命名空间 + state: DataStudioHeaderStateType; // state 数据| 数据类型 + effects: {}; + reducers: {}; +} + + +const DataStudioHeaderModel: DataStudioHeaderModelType = { + namespace: DataStudioHeaderState, + state: { + }, + effects: {}, + reducers: {} +} + + +export const [DataStudioHeaderStateModel, DataStudioHeaderStateModelAsync] = createModelTypes(DataStudioHeaderModel); + +export default DataStudioHeaderModel; diff --git a/dinky-web/src/model/DataStudio/LayoutState/data.d.ts b/dinky-web/src/model/DataStudio/LayoutState/data.d.ts new file mode 100644 index 00000000000..f84351e1702 --- /dev/null +++ b/dinky-web/src/model/DataStudio/LayoutState/data.d.ts @@ -0,0 +1,61 @@ +export const DataStudioLayoutState: string = 'DataStudioLayoutState'; + + + +/** + * 初始化布局宽高度 + */ +export const LAYOUT_VIEW = { + headerHeight: 32, + headerNavHeight: 56, + footerHeight: 25, + sideWidth: 40, + leftToolWidth: 180, + marginTop: 84, + topHeight: 35.6, + bottomHeight: 100, + rightMargin: 32, + leftMargin: 36, + midMargin: 44, + otherHeight: 0, + paddingInline: 50 +}; + + + + +export type JobRunningMessage = { + taskId: string | number | null; + jobName: string; + jobStatus: string; + runningLog: string; +} + + +export type ContainerLayoutState = { + selectKey: string; // 左侧侧边栏默认选中的 路由key + selectSubKey: { [c: string]: string }; + height: number | string; // 左侧侧边栏高度 + width: number | string; // 左侧侧边栏宽度 + maxWidth?: number | string; // 左侧侧边栏最大宽度 +}; + + +export type DataStudioLayoutStateType = { + leftTopSliderContainer: ContainerLayoutState, + middleContainer: { + height: number, // 中间内容区域(编辑器区域)高度 + }, + middleAreaHeight: string | number, // 中间区域 从左到右的全部区域的高度 + rightTopSliderContainer: ContainerLayoutState, + leftBottomSliderContainer: ContainerLayoutState, + footerContainer: { + codePosition: [number, number], // 当前光标所在的行列 + space: number, // 当前缩进的空格数 + codeEncoding: string, // 编码 + lineSeparator: string, // 换行符 + codeType: string, // 代码类型 + memoryInfo: string, // 内存信息 + jobRunningMsg: JobRunningMessage, // 任务运行信息 + } +} diff --git a/dinky-web/src/model/DataStudio/LayoutState/model.tsx b/dinky-web/src/model/DataStudio/LayoutState/model.tsx new file mode 100644 index 00000000000..9a95cfc29fa --- /dev/null +++ b/dinky-web/src/model/DataStudio/LayoutState/model.tsx @@ -0,0 +1,360 @@ +import {createModelTypes} from "@/utils/modelUtils"; + + +import {DataStudioLayoutState, DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data.d"; +import { Effect, Reducer } from "@umijs/max"; + + +export type DataStudioLayoutStateModelType = { + namespace: string; // 命名空间 + state: DataStudioLayoutStateType; // state 数据| 数据类型 + effects: NonNullable; // effects 数据| 数据类型 , 如果没有 effects, 则为 undefined , 避免类型检查错误 + reducers: { + /** + * 更新左上侧边栏相关信息 + */ + updateLeftTopSliderContainerSelectKey: Reducer; // 保存左上侧边栏的选中项 + updateLeftTopSliderContainerSelectSubKey: Reducer; // 保存左上侧边栏的选中项 + updateLeftTopSliderContainerHeight: Reducer; // 保存左上侧边栏的高度 + updateLeftTopSliderContainerWidth: Reducer; // 保存左上侧边栏的宽度 + + /** + * 更新中间内容区域(编辑器区域)相关信息 + */ + updateMiddleContainerHeight: Reducer; // 保存中间内容区域(编辑器区域)的高度 + + /** + * 更新 中间整体区域的高度 + */ + updateMiddleAreaHeight: Reducer; // 保存中间区域 从左到右的整体区域的高度 + + /** + * 更新右上侧边栏相关信息 + */ + updateRightTopSliderContainerSelectKey: Reducer; // 保存右上侧边栏的选中项 + updateRightTopSliderContainerSelectSubKey: Reducer; // 保存右上侧边栏的选中项 + updateRightTopSliderContainerHeight: Reducer; // 保存右上侧边栏的高度 + updateRightTopSliderContainerWidth: Reducer; // 保存右上侧边栏的宽度 + + /** + * 更新左下侧边栏相关信息 + */ + updateLeftBottomSliderContainerSelectKey: Reducer; // 保存左下侧边栏的选中项 + updateLeftBottomSliderContainerSelectSubKey: Reducer; // 保存左下侧边栏的选中项 + updateLeftBottomSliderContainerHeight: Reducer; // 保存左下侧边栏的高度 + updateLeftBottomSliderContainerWidth: Reducer; // 保存左下侧边栏的宽度 + + /** + * 更新底部区域相关信息 + */ + updateFooterContainerCodePosition: Reducer; // 保存底部区域的代码位置 + updateFooterContainerSpace: Reducer; // 保存底部区域的空格 + updateFooterContainerCodeEncoding: Reducer; // 保存底部区域的编码 + updateFooterContainerLineSeparator: Reducer; // 保存底部区域的换行符 + updateFooterContainerCodeType: Reducer; // 保存底部区域的代码类型 + updateFooterContainerMemoryInfo: Reducer; // 保存底部区域的内存信息 + updateFooterContainerJobRunningMsg: Reducer; // 保存底部区域的任务运行信息 + updateFooterContainerInfo: Reducer; // 保存底部区域的所有信息 + + }; +} + + +const DataStudioLayoutModel: DataStudioLayoutStateModelType = { + namespace: DataStudioLayoutState, + state: { + leftTopSliderContainer: { + selectKey: 'menu.datastudio.project', // 左上侧边栏默认选中的路由 key + selectSubKey: {}, + height: '100%', // 左上侧边栏高度 + width: 260 // 左上侧边栏宽度 + }, + middleContainer: { + height: 0, // 中间内容区域(编辑器区域)高度 + }, + middleAreaHeight: 0, + rightTopSliderContainer: { + selectKey: '', // 右上侧边栏默认选中的路由 key + selectSubKey: {}, + height: '100%', // 右上侧边栏高度 + width: 260, // 右上侧边栏宽度 + }, + leftBottomSliderContainer: { + selectKey: 'menu.datastudio.console', // 左下侧边栏默认选中的路由 key + selectSubKey: {}, + height: 180, // 左下侧边栏高度 + width: '100%' // 左下侧边栏宽度 + }, + footerContainer: { + codePosition: [1, 1], + space: 2, + codeEncoding: 'UTF-8', + lineSeparator: 'LF', + codeType: '', + memoryInfo: '100/500M', + // 任务运行信息 todo: 任务运行信息待定 + jobRunningMsg: { + taskId: null, + jobName: '', + jobStatus: '', + runningLog: '' + } + } + }, + effects: {}, + reducers: { + /** + * 更新左上侧边栏相关信息 + */ + // 保存左上侧边栏的选中项 + updateLeftTopSliderContainerSelectKey(state, {payload}) { + return { + ...state, + leftTopSliderContainer: { + ...state.leftTopSliderContainer, + selectKey: payload + } + } + }, + // 保存左上侧边栏的选中项 + updateLeftTopSliderContainerSelectSubKey(state, {payload}) { + return { + ...state, + leftTopSliderContainer: { + ...state.leftTopSliderContainer, + selectSubKey: payload + } + } + }, + // 保存左上侧边栏的高度 + updateLeftTopSliderContainerHeight(state, {payload}) { + return { + ...state, + leftTopSliderContainer: { + ...state.leftTopSliderContainer, + height: payload + } + } + }, + // 保存左上侧边栏的宽度 + updateLeftTopSliderContainerWidth(state, {payload}) { + return { + ...state, + leftTopSliderContainer: { + ...state.leftTopSliderContainer, + width: payload + } + } + }, + + /** + * 更新中间内容区域(编辑器区域)相关信息 + */ + // 保存中间内容区域(编辑器区域)的高度 + updateMiddleContainerHeight(state, {payload}) { + return { + ...state, + middleContainer: { + ...state.middleContainer, + height: payload + } + } + }, + /** + * 更新 middleAreaHeight + */ + updateMiddleAreaHeight(state, {payload}) { + return { + ...state, + middleAreaHeight: payload + } + }, + + /** + * + * 更新右上侧边栏相关信息 + */ + // 保存右上侧边栏的选中项 + updateRightTopSliderContainerSelectKey(state, {payload}) { + return { + ...state, + rightTopSliderContainer: { + ...state.rightTopSliderContainer, + selectKey: payload + } + } + }, + // 保存右上侧边栏的选中项 + updateRightTopSliderContainerSelectSubKey(state, {payload}) { + return { + ...state, + rightTopSliderContainer: { + ...state.rightTopSliderContainer, + selectSubKey: payload + } + } + }, + // 保存右上侧边栏的高度 + updateRightTopSliderContainerHeight(state, {payload}) { + return { + ...state, + rightTopSliderContainer: { + ...state.rightTopSliderContainer, + height: payload + } + } + }, + // 保存右上侧边栏的宽度 + updateRightTopSliderContainerWidth(state, {payload}) { + return { + ...state, + rightTopSliderContainer: { + ...state.rightTopSliderContainer, + width: payload + } + } + }, + + /** + * 更新左下侧边栏相关信息 + */ + // 保存左下侧边栏的选中项 + updateLeftBottomSliderContainerSelectKey(state, {payload}) { + return { + ...state, + leftBottomSliderContainer: { + ...state.leftBottomSliderContainer, + selectKey: payload + } + } + }, + // 保存左下侧边栏的选中项 + updateLeftBottomSliderContainerSelectSubKey(state, {payload}) { + return { + ...state, + leftBottomSliderContainer: { + ...state.leftBottomSliderContainer, + selectSubKey: payload + } + } + }, + // 保存左下侧边栏的高度 + updateLeftBottomSliderContainerHeight(state, {payload}) { + return { + ...state, + leftBottomSliderContainer: { + ...state.leftBottomSliderContainer, + height: payload + } + } + }, + // 保存左下侧边栏的宽度 + updateLeftBottomSliderContainerWidth(state, {payload}) { + return { + ...state, + leftBottomSliderContainer: { + ...state.leftBottomSliderContainer, + width: payload + } + } + }, + + /** + * 更新底部区域相关信息 + */ + // 保存底部区域的代码位置 + updateFooterContainerCodePosition(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + codePosition: payload + } + } + }, + + /** + * 更新底部区域相关信息 + * @param state + * @param payload + */ + updateFooterContainerInfo(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + ...payload + } + } + }, + + + // 保存底部区域的代码编码 + updateFooterContainerCodeEncoding(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + codeEncoding: payload + } + } + }, + // 保存底部区域的代码空格 + updateFooterContainerSpace(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + space: payload + } + } + }, + // 保存底部区域的代码换行符 + updateFooterContainerLineSeparator(state, {payload}) { + + return { + ...state, + footerContainer: { + ...state.footerContainer, + lineSeparator: payload + } + } + }, + // 保存底部区域的代码类型 + updateFooterContainerCodeType(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + codeType: payload + } + } + }, + // 保存底部区域的内存信息 + updateFooterContainerMemoryInfo(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + memoryInfo: payload + } + } + }, + // 保存底部区域的任务运行信息 + updateFooterContainerJobRunningMsg(state, {payload}) { + return { + ...state, + footerContainer: { + ...state.footerContainer, + jobRunningMsg: payload + } + } + }, + + } +} + + +export const [DataStudioLayoutStateModel, DataStudioLayoutStateModelASsync] = createModelTypes(DataStudioLayoutModel); + +export default DataStudioLayoutModel; diff --git a/dinky-web/src/model/DataStudio/LeftContainer/data.d.ts b/dinky-web/src/model/DataStudio/LeftContainer/data.d.ts new file mode 100644 index 00000000000..fd867b09188 --- /dev/null +++ b/dinky-web/src/model/DataStudio/LeftContainer/data.d.ts @@ -0,0 +1,27 @@ +import {Key} from "react"; +import {DataSources} from "@/types/RegCenter/data"; + + +export const DataStudioLeftTopSliderState: string = 'DataStudioLeftTopSliderState'; + + +export type DataStudioLeftTopSliderStateType = { + project: { + projectData: [], + expandKeys: Key[], + selectKey: Key[] + }, + catalog: { + catalogData: [], + expandKeys: []; + selectKey: []; + selectCatalogDBId: number | null; + selectCatalogDBOfEnvId: number | null; + }, + datasource: { + dataSourceData: DataSources.DataSource[]; + selectDatabaseId: number | null; + expandKeys: []; + selectKeys: []; + }, +} diff --git a/dinky-web/src/model/DataStudio/LeftContainer/model.tsx b/dinky-web/src/model/DataStudio/LeftContainer/model.tsx new file mode 100644 index 00000000000..bad2355c3b4 --- /dev/null +++ b/dinky-web/src/model/DataStudio/LeftContainer/model.tsx @@ -0,0 +1,244 @@ +import {createModelTypes} from "@/utils/modelUtils"; + +import { Effect, Reducer } from "@umijs/max"; +import { + DataStudioLeftTopSliderState, + DataStudioLeftTopSliderStateType +} from "@/model/DataStudio/LeftContainer/data.d"; +import {getDataBase} from "@/pages/DataStudio/LeftContainer/MetaData/service"; +import {getTaskData} from "@/pages/DataStudio/LeftContainer/Project/service"; + + +export type DataStudioLeftTopSliderModelType = { + namespace: string; // 命名空间 + state: DataStudioLeftTopSliderStateType; // state 数据| 数据类型 + effects: { + queryProjectData: Effect; // 查询项目列表 + queryCatalogData: Effect; // 查询元数据列表 + queryDataSourceData: Effect; // 查询数据源列表 + }; + reducers: { + /** + * 项目相关 + */ + updateProjectTreeData: Reducer; //更新项目数据 + updateProjectTreeExpandKeys: Reducer; //更新项目展开的keys + updateProjectTreeSelectKey: Reducer; //更新项目选中的keys + + /** + * 元数据相关 + */ + updateCatalogTreeData: Reducer; //更新元数据数据 + updateCatalogTreeExpandKeys: Reducer; //更新元数据展开的keys + updateCatalogTreeSelectKey: Reducer; //更新元数据选中的keys + updateCatalogTreeSelectCatalogDBOfEnvId: Reducer; //更新 envId, 用于查询元数据 + updateCatalogTreeSelectCatalogDBId: Reducer; //更新元数据 下拉框中得数据库 id + + + /** + * 数据源相关 + */ + updateDataSourceTreeData: Reducer; //更新数据源数据 + updateDataSourceTreeSelectDatabaseId: Reducer; //更新数据源选中的keys + updateDataSourceTreeExpandKeys: Reducer; //更新数据源展开的keys + updateDataSourceTreeSelectKey: Reducer; //更新数据源选中的keys + + }; +} + + +const DataStudioLeftTopSliderModel: DataStudioLeftTopSliderModelType = { + namespace: DataStudioLeftTopSliderState, + state: { + project: { + projectData: [], // 项目数据初始化 + expandKeys: [], // 项目展开的keys + selectKey: [], // 项目选中的keys + }, + catalog: { + catalogData: [], // 元数据数据初始化 + expandKeys: [], // 元数据展开的keys + selectKey: [], // 元数据选中的keys + selectCatalogDBId: null, // 元数据选中的数据库id + selectCatalogDBOfEnvId: null, // 元数据选中的环境id + }, + datasource: { + dataSourceData: [], // 数据源数据初始化 + selectDatabaseId: null, // 数据源选中的数据库id + expandKeys: [], // 数据源展开的keys + selectKeys: [], // 数据源选中的keys + }, + }, + effects: { + * queryProjectData({payload}, {call, put}) { + + const response: [] = yield call(getTaskData, payload); + yield put({ + type: 'updateProjectTreeData', + payload: response + }) + }, + * queryCatalogData({payload}, {call, put}) { + // todo 请求接口 + // const response: [] = yield call(getTaskData, payload); + yield put({ + type: 'updateCatalogTreeData', + payload: payload + }) + }, + * queryDataSourceData({payload}, {call, put}) { + // todo 请求接口 + const result: [] = yield call(getDataBase); + yield put({ + type: 'updateDataSourceTreeData', + payload: payload + }) + } + }, + reducers: { + /** + * 项目相关 + * @param state + * @param payload + */ + // 更新项目列表数据 + updateProjectTreeData(state, {payload}) { + return { + ...state, + project: { + ...state.project, + projectData: payload + } + } + }, + // 更新项目列表展开的key + updateProjectTreeExpandKeys(state, {payload}) { + return { + ...state, + project: { + ...state.project, + expandKeys: payload + } + } + }, + // 更新项目列表选中的key + updateProjectTreeSelectKey(state, {payload}) { + return { + ...state, + project: { + ...state.project, + selectKeys: payload + } + } + }, + + /** + * 元数据相关 + * @param state + * @param payload + */ + // 更新元数据列表数据 + updateCatalogTreeData(state, {payload}) { + return { + ...state, + catalog: { + ...state.catalog, + catalogData: payload + } + } + }, + // 更新元数据列表展开的key + updateCatalogTreeExpandKeys(state, {payload}) { + return { + ...state, + catalog: { + ...state.catalog, + expandKeys: payload + } + } + }, + + // 更新元数据列表选中的key + updateCatalogTreeSelectKey(state, {payload}) { + return { + ...state, + catalog: { + ...state.catalog, + selectKeys: payload + } + } + }, + // 更新 envid + updateCatalogTreeSelectCatalogDBOfEnvId(state, {payload}) { + return { + ...state, + catalog: { + ...state.catalog, + selectCatalogDBOfEnvId: payload + } + } + }, + // 更新 catalogDBId + updateCatalogTreeSelectCatalogDBId(state, {payload}) { + return { + ...state, + catalog: { + ...state.catalog, + selectCatalogDBId: payload + } + } + }, + + + /** + * 数据源相关 + * @param state + * @param payload + */ + // 更新数据源列表数据 + updateDataSourceTreeData(state, {payload}) { + return { + ...state, + datasource: { + ...state.datasource, + dataSourceData: payload + } + } + }, + // 更新数据源选中得数据源 id + updateDataSourceTreeSelectDatabaseId(state, {payload}) { + return { + ...state, + datasource: { + ...state.datasource, + selectDatabaseId: payload + } + } + }, + // 更新数据源列表展开的key + updateDataSourceTreeExpandKeys(state, {payload}) { + return { + ...state, + datasource: { + ...state.datasource, + expandKeys: payload + } + } + }, + // 更新数据源列表选中的key + updateDataSourceTreeSelectKey(state, {payload}) { + return { + ...state, + datasource: { + ...state.datasource, + selectKeys: payload + } + } + }, + } +} + + +export const [DataStudioLeftTopSliderStateModel, DataStudioLeftTopSliderStateModelAsync] = createModelTypes(DataStudioLeftTopSliderModel); + +export default DataStudioLeftTopSliderModel; diff --git a/dinky-web/src/model/DataStudio/LeftContainer/service.ts b/dinky-web/src/model/DataStudio/LeftContainer/service.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dinky-web/src/model/DataStudio/MiddleContainer/data.d.ts b/dinky-web/src/model/DataStudio/MiddleContainer/data.d.ts new file mode 100644 index 00000000000..f47055def31 --- /dev/null +++ b/dinky-web/src/model/DataStudio/MiddleContainer/data.d.ts @@ -0,0 +1,46 @@ +export const DataStudioMiddleState: string = 'DataStudioMiddleState'; + + +/** + * @description: 任务类型 + */ +export enum TabsItemSubType { + FLINK_SQL = 'FlinkSql' +} + +export type ConsoleState = { + result: {}; +}; + +export enum TabItemType { + NONE = '', + METADATA = 'metadata', + PROJECT = 'project' +} + +export interface TabsItem { + id: string; // tab id 可将主键 id/ 索引下表 作为 tab id + label: string; // tab 标签 + breadcrumbLabel: string; // 面包屑标签 + type: TabItemType; // tab 类型 + subType: TabsItemSubType | null; // tab 子类型 + key: string; // tab key + treeKey: string; // 树节点 key + value: string; // tab value + icon: any; // tab icon + closable: boolean; // 是否可关闭 + path: string[]; // tab 路径 + console: ConsoleState; // 控制台 仅 bottom 下可用 + isModified: boolean; // 是否修改 用于判断是否需要保存 以及渲染tab标签带*号 +} + +export type TabStateType = { + activeKey: string; + activeBreadcrumbTitle: string; + tabList: TabsItem[]; +}; + + +export type DataStudioMiddleStateType = { + tabInfo: TabStateType; +} diff --git a/dinky-web/src/model/DataStudio/MiddleContainer/model.tsx b/dinky-web/src/model/DataStudio/MiddleContainer/model.tsx new file mode 100644 index 00000000000..a63ac8bdc8f --- /dev/null +++ b/dinky-web/src/model/DataStudio/MiddleContainer/model.tsx @@ -0,0 +1,186 @@ +import {createModelTypes} from "@/utils/modelUtils"; + +import { + DataStudioMiddleState, + DataStudioMiddleStateType +} from "@/model/DataStudio/MiddleContainer/data.d"; +import {Reducer} from "@umijs/max"; +import {getFooterValue} from "@/pages/DataStudio/function"; + + +export type DataStudioMiddleModelType = { + namespace: string; // 命名空间 + state: DataStudioMiddleStateType; // state 数据| 数据类型 + effects: {}; + reducers: { + /** + * 基本操作 + */ + updateTabInfo: Reducer; // 更新 tabs 总的信息 + updateTabsActiveKey: Reducer; // 更新 tabs activeKey + updateTabsActiveBreadcrumbTitle: Reducer; // 更新 tabs activeBreadcrumbTitle + updateTabsTabList: Reducer; // 更新 tabs tabList + /** + * 扩展操作 + */ + addTab: Reducer; // 添加 tabs + removeTab: Reducer; // 删除 tabs + removeOtherTabs: Reducer; // 删除其他 tabs + removeAllTabs: Reducer; // 删除所有 tabs + renameTab: Reducer; // 重命名 tabs + + + }; +} + + +const DataStudioMiddleModel: DataStudioMiddleModelType = { + namespace: DataStudioMiddleState, + state: { + tabInfo: { + activeKey: '', // 当前激活的 tab key + activeBreadcrumbTitle: '', // 当前激活的 tab breadcrumb title 传入时是已经拼接好了的 + tabList: [], // 当前所有打开的 tab 列表 + }, + }, + effects: {}, + reducers: { + /** + * 基本操作 + */ + // 更新 tabs 总的信息 + updateTabInfo(state, {payload}) { + return { + ...state, + tabInfo: { + ...state.tabInfo, + ...payload, + }, + }; + }, + // 更新 tabs activeKey + updateTabsActiveKey(state, {payload}) { + const {tabList} = state.tabInfo; + + const itemTypes = tabList.filter((x) => x.key === payload); + if (itemTypes.length === 1) { + const itemType = itemTypes[0]; + return { + ...state, + tabInfo: { + ...state.tabInfo, + activeKey: payload, + activeBreadcrumbTitle: [itemType.type, itemType.breadcrumbLabel, itemType.label].join( + '/' + ) + }, + }; + } + return { + ...state, + tabInfo: { + ...state.tabInfo, + activeKey: payload + }, + }; + }, + // 更新 tabs activeBreadcrumbTitle + updateTabsActiveBreadcrumbTitle(state, {payload}) { + return { + ...state, + tabInfo: { + ...state.tabInfo, + activeBreadcrumbTitle: payload, + }, + }; + }, + // 更新 tabs tabList + updateTabsTabList(state, {payload}) { + return { + ...state, + tabInfo: { + ...state.tabInfo, + tabList: payload, + }, + }; + }, + /** + * 扩展操作 + */ + // 添加 tabs + addTab(state, {payload}) { + return { + ...state, + tabInfo: { + ...state.tabInfo, + tabList: [...state.tabInfo.tabList, payload], + }, + }; + }, + // 删除 tabs + // @ts-ignore + removeTab(state, {payload}) { + const {activeKey,tabList} = state.tabInfo + // 获取要删除的 tab 的 index + const newTabList = tabList.findIndex(item => item.key === payload) + // 删除 + let spliceTabList = tabList.splice(newTabList, 1) ?? []; + return { + ...state, + tabInfo: { + ...state.tabInfo, + tabList: newTabList, + activeBreadcrumbTitle: spliceTabList[spliceTabList.length - 1].label ?? '', // 如果删除的是最后一个 tab 则 activeBreadcrumbTitle 为最后一个 tab 的 label + activeKey: activeKey === payload ? spliceTabList[spliceTabList.length - 1].key ?? '' : '' + }, + }; + }, + // 删除其他 tabs + removeOtherTabs(state, {payload}) { + return { + ...state, + tabInfo: { + ...state.tabInfo, + tabList: state.tabInfo.tabList.filter(item => item.key === payload), + }, + }; + }, + // 删除所有 tabs + removeAllTabs(state) { + return { + ...state, + tabInfo: { + ...state.tabInfo, + tabList: [], + }, + }; + }, + // 重命名 tabs + renameTab(state, {payload}) { + const {key, title} = payload; + const renamedTabList = state.tabInfo.tabList.map(item => { + if (item.key === key) { + return { + ...item, + label: title, + }; + } + return item; + }); + return { + ...state, + tabInfo: { + ...state.tabInfo, + tabList: renamedTabList, + activeBreadcrumbTitle: title, // todo: 这里需要注意,如果是当前激活的 tab,需要更新 activeBreadcrumbTitle 为新的 title, 此处临时处理, 后续需要优化 + activeKey: key, + }, + }; + } + } +} + + +export const [DataStudioMiddleStateModel, DataStudioMiddleStateModelAsync] = createModelTypes(DataStudioMiddleModel); + +export default DataStudioMiddleModel; diff --git a/dinky-web/src/model/DataStudio/RightContainer/data.d.ts b/dinky-web/src/model/DataStudio/RightContainer/data.d.ts new file mode 100644 index 00000000000..d3d0a1cef1f --- /dev/null +++ b/dinky-web/src/model/DataStudio/RightContainer/data.d.ts @@ -0,0 +1,58 @@ +import {Alert, Cluster} from "@/types/RegCenter/data"; +import {DefaultOptionType} from "antd/es/select"; + +export const DataStudioRightState: string = 'DataStudioRightState'; + +export type EnvOptionsType = { + id: number; + name: string; + fragment?: boolean; +}; + + + +export type DataStudioRightStateType = { + sessionClusterData: Cluster.Instance[]; // yarn-session || standalone || k8s session + clusterConfigurationData: Cluster.Config[]; // yarn-per-job || flink-native-k8s || yarn-application || flink-native-k8s-application + flinkConfigOptions: DefaultOptionType[]; // flink配置项 应用于其他配置内的筛选 + envData: EnvOptionsType[]; // env环境信息 + alertGroupData: Alert.AlertGroup[]; // 告警组信息 + taskInfo: TaskInfoState; // 任务信息 +} + + + +export type TaskInfoState = { + id: number | null; // 任务id + catalogueId: number; // 目录id + name: string; // 任务名称 + dialect: string | null; // 语言 + type: string | ''; // 运行模式 local | yarn-session | yarn-per-job | standalone | k8s | flink-native-k8s.... + // checkPoint?: number; + savePointStrategy: number; // 保存点策略 + savePointPath: string | null; // 保存点路径 当保存点策略为自定义时,需要写入保存点路径 + parallelism: number; // 并行度 + fragment: boolean; // 开启全局变量 + statementSet: boolean; // 开启insert语句集 + batchModel: boolean; // 是否是批处理模式 + config: Map[]; // 其他配置 + clusterInstanceId: number; // 集群实例id + clusterInstanceName: string; // 集群实例名称 + clusterConfigurationId: number; // 集群配置id + clusterConfigurationName: string; // 集群配置名称 + databaseId: number | null; // 数据库id | 普通 sql 下使用 + databaseName: string; // 数据库名称 | 普通 sql 下使用 + jarId: number | null; // jar包id | flink jar 下使用 可忽略 暂时弃用 + envId: number; // 环境id + jobInstanceId: number | null; // 任务实例id + note?: string ; // 备注 + enabled?: boolean; // 任务哪有启用/禁用????????? + createTime: Date | null; // 创建时间 + updateTime: Date | null; // 更新时间 + statement: string | null; // 任务语句 + jobName: string; // 任务名称 和 name 一样 是否删除待定 + useResult: boolean; // 是否预览结果 + maxRowNum: number; // 最大行数 任务执行查询时使用 + useChangeLog: boolean; // 是否使用changelog模式 + useAutoCancel: boolean; // 是否自动停止 +}; diff --git a/dinky-web/src/model/DataStudio/RightContainer/model.tsx b/dinky-web/src/model/DataStudio/RightContainer/model.tsx new file mode 100644 index 00000000000..c5493b6e152 --- /dev/null +++ b/dinky-web/src/model/DataStudio/RightContainer/model.tsx @@ -0,0 +1,151 @@ +import {createModelTypes} from "@/utils/modelUtils"; + +import {DataStudioRightState, DataStudioRightStateType} from "@/model/DataStudio/RightContainer/data.d"; +import { Effect, Reducer } from "@umijs/max"; +import {getTaskData} from "@/pages/DataStudio/LeftContainer/Project/service"; +import { + getAlertGroupData, + getClusterConfigurationData, + getEnvData, + getFlinkConfigs, + getSessionData +} from "@/pages/DataStudio/RightContainer/JobConfig/service"; + + +export type DataStudioRightModelType = { + namespace: string; // 命名空间 + state: DataStudioRightStateType; // state 数据| 数据类型 + effects: { + querySessionClusterData: Effect; + queryClusterConfigurationData: Effect; + queryEnvData: Effect; + queryFlinkConfigOptions: Effect; + queryAlertGroupData: Effect; + }; + reducers: { + updateSessionClusterData: Reducer; + updateClusterConfigurationData: Reducer; + updateEnvData: Reducer; + updateFlinkConfigOptions: Reducer; + updateAlertGroupData: Reducer; + updateTaskInfo: Reducer; + }; +} + + +const DataStudioRightModel: DataStudioRightModelType = { + namespace: DataStudioRightState, + state: { + sessionClusterData: [], // session集群数据 + clusterConfigurationData: [], // 集群配置数据 + envData: [], // 环境数据 + flinkConfigOptions: [], // flink配置数据 + alertGroupData: [], // 告警组数据 + taskInfo: { + id: null, // 任务id + catalogueId: -1, // 元数据id + name: '', // 任务名称 + dialect: '', // 方言 + statement: '', // 任务语句 + type: 'standalone', // 任务执行模式 + // checkPoint?: number; + savePointStrategy: -1, // 保存点策略 -1 代表禁用 + savePointPath: '',// 保存点路径 当保存点策略为自定义时,需要写入保存点路径 + parallelism: 1, // 并行度 + fragment: false, // 开启全局变量 + statementSet: false,// 开启insert语句集 + batchModel: false , // 是否是批处理模式 + config: [], // 其他配置 + clusterInstanceId: 0, // 集群实例id + clusterInstanceName: '', // 集群实例名称 + clusterConfigurationId: 0, // 集群配置id + clusterConfigurationName: '',// 集群配置名称 + note: '', // 备注 + // enabled?: boolean; // 任务哪有启用/禁用????????? + createTime: new Date(), // 创建时间 + updateTime: new Date(), // 更新时间 + databaseId: null, // 数据库id | 普通 sql 下使用 + databaseName: '', // 数据库名称 | 普通 sql 下使用 + jarId: null, // jar包id | flink jar 下使用 可忽略 暂时弃用 + envId: -1, // 环境id | -1 代表未选择环境/禁用 + jobInstanceId: null, // 任务实例id + jobName: '', // 任务名称 和 name 一样 是否删除待定 + useResult: true, // 是否预览结果 + maxRowNum: 100, // 最大行数 任务执行查询时使用 + useChangeLog: false, // 是否使用changelog模式 + useAutoCancel: false, // 是否自动停止 + }, + }, + effects: { + //查询session集群数据 + * querySessionClusterData({payload}, {call, put}) { + const response: [] = yield call(getSessionData, payload); + yield put({type: 'updateSessionClusterData', response}); + }, + //查询集群配置数据 + * queryClusterConfigurationData({payload}, {call, put}) { + const response: [] = yield call(getClusterConfigurationData, payload); + yield put({type: 'updateClusterConfigurationData', response}); + }, + //查询环境数据 + * queryEnvData({payload}, {call, put}) { + const response: [] = yield call(getEnvData, payload); + yield put({type: 'updateEnvData', response}); + }, + //查询flink配置数据 + * queryFlinkConfigOptions({payload}, {call, put}) { + const response: [] = yield call(getFlinkConfigs, payload); + yield put({type: 'updateFlinkConfigOptions', response}); + }, + //查询告警组数据 + * queryAlertGroupData({payload}, {call, put}) { + const response: [] = yield call(getAlertGroupData, payload); + yield put({type: 'updateAlertGroupData', response}); + }, + }, + reducers: { + //更新session集群数据 + updateSessionClusterData(state, {payload}) { + return { + ...state,sessionClusterData: payload + }; + }, + //更新集群配置数据 + updateClusterConfigurationData(state, {payload}) { + return { + ...state, + clusterConfigurationData: payload, + }; + }, + //更新环境数据 + updateEnvData(state, {payload}) { + return { + ...state, envData: payload + }; + }, + //更新flink配置数据 + updateFlinkConfigOptions(state, {payload}) { + return { + ...state,flinkConfigOptions: payload + }; + }, + //更新告警组数据 + updateAlertGroupData(state, {payload}) { + return { + ...state,alertGroupData: payload + }; + }, + + //更新任务信息 + updateTaskInfo(state, {payload}) { + return { + ...state,taskInfo: payload + }; + } + } +} + + +export const [DataStudioRightStateModel, DataStudioRightStateModelAsync] = createModelTypes(DataStudioRightModel); + +export default DataStudioRightModel; diff --git a/dinky-web/src/model/DataStudio/service.ts b/dinky-web/src/model/DataStudio/service.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dinky-web/src/models/Sse.tsx b/dinky-web/src/model/SSE/Sse.tsx similarity index 100% rename from dinky-web/src/models/Sse.tsx rename to dinky-web/src/model/SSE/Sse.tsx diff --git a/dinky-web/src/pages/DataStudio/BottomContainer/TableData/index.tsx b/dinky-web/src/pages/DataStudio/BottomContainer/TableData/index.tsx index 0207862e98b..4bb5375da1c 100644 --- a/dinky-web/src/pages/DataStudio/BottomContainer/TableData/index.tsx +++ b/dinky-web/src/pages/DataStudio/BottomContainer/TableData/index.tsx @@ -17,7 +17,7 @@ * */ -import { SseData } from '@/models/Sse'; +import { SseData } from '@/model/SSE/Sse'; import { getCurrentData } from '@/pages/DataStudio/function'; import { StateType } from '@/pages/DataStudio/model'; import { SSE_TOPIC } from '@/pages/DevOps/constants'; diff --git a/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx b/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx index e783b0086ae..2ca57b82446 100644 --- a/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/FooterContainer/index.tsx @@ -26,6 +26,8 @@ import { l } from '@/utils/intl'; import { connect } from '@@/exports'; import { Button, GlobalToken, Space } from 'antd'; import React, { useEffect, useState } from 'react'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; +import {DataStudioLayoutState, DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; export type FooterContainerProps = { token: GlobalToken; @@ -38,10 +40,10 @@ type ButtonRoute = { isShow?: (type?: TabsPageType, subType?: string, data?: any) => boolean; }; -const FooterContainer: React.FC = (props) => { +const FooterContainer: React.FC = (props) => { const { - footContainer: { - memDetails, + footerContainer: { + memoryInfo, codeType, lineSeparator, codeEncoding, @@ -50,14 +52,14 @@ const FooterContainer: React.FC = (props) => { jobRunningMsg }, token, - tabs + tabInfo: { tabList,activeKey } } = props; const themeValue = useThemeValue(); const [viewJobRunning, setViewJobRunning] = useState(false); - const [memDetailInfo, setMemDetailInfo] = useState(memDetails); + const [memDetailInfo, setMemDetailInfo] = useState(memoryInfo); - const currentTab = getCurrentTab(tabs.panes ?? [], tabs.activeKey); + const currentTab = getCurrentTab(tabList ?? [], activeKey); useEffect(() => { const eventSource = getSseData('/api/sse/getJvmInfo'); @@ -191,7 +193,7 @@ const FooterContainer: React.FC = (props) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - footContainer: Studio.footContainer, - tabs: Studio.tabs +export default connect(({DataStudioMiddleState, DataStudioLayoutState }: { DataStudioMiddleState: DataStudioMiddleStateType;DataStudioLayoutState: DataStudioLayoutStateType }) => ({ + footerContainer: DataStudioLayoutState.footerContainer, + tabInfo: DataStudioMiddleState.tabInfo }))(FooterContainer); diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/Explain/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/Explain/index.tsx index 223a8f9d086..ebf6295071d 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/Explain/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/Explain/index.tsx @@ -27,6 +27,7 @@ import ProList from '@ant-design/pro-list'; import { Drawer, Space, Tag, Typography } from 'antd'; import React, { useEffect, useState } from 'react'; import { connect } from 'umi'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; const { Paragraph, Text } = Typography; @@ -45,15 +46,15 @@ type ExplainItem = { export type ExplainProps = { data: Partial; }; -const Explain: React.FC = (props: any) => { +const Explain: React.FC = (props: any) => { const [explainData, setExplainData] = useState([]); const [result, setResult] = useState({l('pages.datastudio.explain.validate')}); const [showModal, setShowModal] = useState(false); const [explainInfo, setExplainInfo] = useState(''); const { - tabs: { panes, activeKey } + tabInfo: { tabList, activeKey } } = props; - const current = getCurrentData(panes, activeKey); + const current = getCurrentData(tabList, activeKey); useEffect(() => { // let selectsql = null; @@ -235,6 +236,6 @@ const Explain: React.FC = (props: any) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs +export default connect(({ DataStudioMiddleState }: {DataStudioMiddleState: DataStudioMiddleStateType; }) => ({ + tabInfo: DataStudioMiddleState.tabInfo }))(Explain); diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/FlinkGraph/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/FlinkGraph/index.tsx index 14378b3d8d6..1591eb533ca 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/FlinkGraph/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/FlinkGraph/index.tsx @@ -20,8 +20,6 @@ import FlinkDag from '@/components/FlinkDag'; import { Jobs } from '@/types/DevOps/data'; import { Empty } from 'antd'; -import { StateType } from 'rmc-input-number'; -import { connect } from 'umi'; const FlinkGraph = (props: { data: Jobs.JobPlan }) => { const { data } = props; @@ -43,4 +41,4 @@ const FlinkGraph = (props: { data: Jobs.JobPlan }) => { ); }; -export default connect(({}: { Studio: StateType }) => ({}))(FlinkGraph); +export default FlinkGraph; diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx index 8c2b7d160cc..7c356215b6d 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx @@ -34,7 +34,7 @@ import { getJobPlan, onLineTask } from '@/pages/DataStudio/HeaderContainer/service'; -import { StateType, TabsPageSubType, TabsPageType, VIEW } from '@/pages/DataStudio/model'; +import { TabsPageSubType, TabsPageType, VIEW } from '@/pages/DataStudio/model'; import { JOB_LIFE_CYCLE, JOB_STATUS } from '@/pages/DevOps/constants'; import { ConfigStateType } from '@/pages/SettingCenter/GlobalSetting/model'; import { SettingConfigKeyEnum } from '@/pages/SettingCenter/GlobalSetting/SettingOverView/constants'; @@ -60,6 +60,7 @@ import { import { Breadcrumb, Descriptions, Modal, Space } from 'antd'; import { ButtonProps } from 'antd/es/button/button'; import React, { useEffect, useState } from 'react'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; const headerStyle: React.CSSProperties = { display: 'inline-flex', @@ -85,7 +86,7 @@ const HeaderContainer = (props: any) => { const { size, activeBreadcrumbTitle, - tabs: { panes, activeKey }, + tabInfo: { tabList, activeKey }, saveTabs, updateJobRunningMsg, queryDsConfig, @@ -102,8 +103,8 @@ const HeaderContainer = (props: any) => { ) ); - const currentData = getCurrentData(panes, activeKey); - const currentTab = getCurrentTab(panes, activeKey); + const currentData = getCurrentData(tabList, activeKey); + const currentTab = getCurrentTab(tabList, activeKey); useEffect(() => { queryDsConfig(SettingConfigKeyEnum.DOLPHIN_SCHEDULER.toLowerCase()); @@ -394,8 +395,8 @@ const HeaderContainer = (props: any) => { }; export default connect( - ({ Studio, Config }: { Studio: StateType; Config: ConfigStateType }) => ({ - tabs: Studio.tabs, + ({ DataStudioMiddleState, Config }: { DataStudioMiddleState: DataStudioMiddleStateType; Config: ConfigStateType }) => ({ + tabInfo: DataStudioMiddleState.tabInfo, dsConfig: Config.dsConfig }), mapDispatchToProps diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Catalog/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Catalog/index.tsx index 917223acacd..a3f02cd58fa 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Catalog/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Catalog/index.tsx @@ -76,9 +76,7 @@ const Catalog: React.FC = (props: connect) => { const [loading, setLoading] = useState(false); const [columnData, setColumnData] = useState([]); - BtnRoute['menu.datastudio.catalog'][0].onClick = () => { - refreshMetaStoreTables(); - }; + useEffect(() => { getCatalogs(); @@ -271,6 +269,10 @@ const Catalog: React.FC = (props: connect) => { } }; + BtnRoute['menu.datastudio.catalog'][0].onClick = () => { + refreshMetaStoreTables(); + }; + const onChangeMetaStoreCatalogs = (value: string) => { onRefreshTreeData(value); }; diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobModal/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobModal/index.tsx index 25caa98a20f..c7233e187dc 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobModal/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobModal/index.tsx @@ -22,6 +22,7 @@ import { JOB_TYPE } from '@/pages/DataStudio/LeftContainer/Project/constants'; import { isUDF } from '@/pages/DataStudio/LeftContainer/Project/function'; import { queryDataByParams } from '@/services/BusinessCrud'; import { API_CONSTANTS } from '@/services/endpoints'; +import { CascaderOption } from '@/types/Public/data'; import { Catalogue } from '@/types/Studio/data'; import { l } from '@/utils/intl'; import { ModalForm, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-components'; @@ -39,7 +40,7 @@ type JobModalProps = { const JobModal: React.FC = (props) => { const { onCancel, onSubmit, modalVisible, title, values } = props; const [jobType, setJobType] = React.useState(values.type || 'FlinkSql'); - const [udfTemplate, setUdfTemplate] = React.useState([]); + const [udfTemplate, setUdfTemplate] = React.useState([]); const [form] = Form.useForm(); /** @@ -62,10 +63,11 @@ const JobModal: React.FC = (props) => { const queryUdfTemplate = async () => { await queryDataByParams(API_CONSTANTS.UDF_TEMPLATE_TREE).then((res) => { - res.map((item: any) => { - if (item.value === jobType) res = item.children.map((item: any) => item); + (res as CascaderOption[]).map( item => { + if (item.value === jobType) res = item.children.map(item => item); + return item; }); - setUdfTemplate(res); + setUdfTemplate(res as CascaderOption[]); }); }; diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobTree/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobTree/index.tsx index 5752ec00271..26480b7b13d 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobTree/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Project/JobTree/index.tsx @@ -17,23 +17,25 @@ * */ -import { getCurrentTab } from '@/pages/DataStudio/function'; +import {getCurrentTab} from '@/pages/DataStudio/function'; import { buildProjectTree, generateList, getLeafKeyList, getParentKey } from '@/pages/DataStudio/LeftContainer/Project/function'; -import { StateType, STUDIO_MODEL, TabsItemType } from '@/pages/DataStudio/model'; -import { BtnRoute } from '@/pages/DataStudio/route'; -import { l } from '@/utils/intl'; -import { connect } from '@@/exports'; -import { Key } from '@ant-design/pro-components'; -import { Empty, Tree } from 'antd'; +import {StateType, TabsItemType} from '@/pages/DataStudio/model'; +import {BtnRoute} from '@/pages/DataStudio/route'; +import {l} from '@/utils/intl'; +import {connect} from '@@/exports'; +import {Key} from '@ant-design/pro-components'; +import {Empty, Tree} from 'antd'; import Search from 'antd/es/input/Search'; -import React, { useEffect, useState } from 'react'; +import React, {useEffect, useState} from 'react'; +import {DataStudioLeftTopSliderStateType} from "@/model/DataStudio/LeftContainer/data"; +import {DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; -const { DirectoryTree } = Tree; +const {DirectoryTree} = Tree; /** * props @@ -51,7 +53,7 @@ const JobTree: React.FC = (props) => { project: { data: projectData, expandKeys, selectKey }, onNodeClick, style, - height, + middleAreaHeight, onRightClick, selectKeyChange, onExpand, @@ -67,7 +69,7 @@ const JobTree: React.FC = (props) => { const [autoExpandParent, setAutoExpandParent] = useState(true); const onChangeSearch = (e: any) => { - let { value } = e.target; + let {value} = e.target; if (!value) { dispatch({ type: STUDIO_MODEL.updateProjectExpandKey, @@ -135,7 +137,7 @@ const JobTree: React.FC = (props) => { return ( <> = (props) => { {data.length ? ( onNodeClick(info)} onRightClick={onRightClick} @@ -164,7 +166,14 @@ const JobTree: React.FC = (props) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - height: Studio.toolContentHeight, - project: Studio.project +export default connect(({ + DataStudioLeftTopSliderState, + DataStudioLayoutState + }: { + Studio: StateType; + DataStudioLeftTopSliderState: DataStudioLeftTopSliderStateType; + DataStudioLayoutState: DataStudioLayoutStateType +}) => ({ + projectData: DataStudioLeftTopSliderState.project.projectData, + middleAreaHeight: DataStudioLayoutState.middleAreaHeight }))(JobTree); diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Project/ProjectTitle/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Project/ProjectTitle/index.tsx index 50c5c50b749..a7c3d13bdb2 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Project/ProjectTitle/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Project/ProjectTitle/index.tsx @@ -19,7 +19,7 @@ import Title from '@/components/Front/Title'; import FolderModal from '@/pages/DataStudio/LeftContainer/Project/FolderModal'; -import { StateType, STUDIO_MODEL_ASYNC } from '@/pages/DataStudio/model'; +import { StateType } from '@/pages/DataStudio/model'; import { BtnRoute } from '@/pages/DataStudio/route'; import { handleAddOrUpdate } from '@/services/BusinessCrud'; import { API_CONSTANTS } from '@/services/endpoints'; @@ -28,10 +28,12 @@ import { l } from '@/utils/intl'; import { connect } from '@umijs/max'; import { Space } from 'antd'; import React, { useState } from 'react'; +import {DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; +import {DataStudioLeftTopSliderStateModelAsync} from "@/model/DataStudio/LeftContainer/model"; const ProjectTitle: React.FC = (props) => { const { - leftContainer: { selectKey }, + leftTopSliderContainer: { selectKey }, dispatch } = props; @@ -61,7 +63,7 @@ const ProjectTitle: React.FC = (props) => { () => {}, () => { handleCancelCreate(); - dispatch({ type: STUDIO_MODEL_ASYNC.queryProject }); + dispatch({ type: DataStudioLeftTopSliderStateModelAsync.queryProjectData }); } ); }; @@ -95,6 +97,6 @@ const ProjectTitle: React.FC = (props) => { return <>{renderTitle()}; }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - leftContainer: Studio.leftContainer +export default connect(({ DataStudioLayoutState}: {DataStudioLayoutState: DataStudioLayoutStateType}) => ({ + leftTopSliderContainer: DataStudioLayoutState.leftTopSliderContainer, }))(ProjectTitle); diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Project/function.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Project/function.tsx index b6305954780..4fa7d6b16e3 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Project/function.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Project/function.tsx @@ -87,6 +87,26 @@ export const buildProjectTree = ( ? data.map((item: Catalogue) => { const currentPath = path ? [...path, item.name] : [item.name]; // 构造生命周期的值 + const stepValue = buildStepValue(item.task?.step ?? 0); + // 渲染生命周期的徽标 + const renderStepBadge = item.isLeaf && showBadge(item.type?? "") && ( + <> + + + ); const stepValue = buildStepValue(item.task?.step); // 渲染生命周期的 标记点 const renderPreFixState = item.isLeaf && showBadge(item.type) && ( @@ -139,7 +159,7 @@ export const isUDF = (jobType: string): boolean => { return jobType === 'Scala' || jobType === 'Python' || jobType === 'Java'; }; -export const buildStepValue = (step: number) => { +export const buildStepValue = (step = 0) => { // "success", "processing", "error", "default", "warning" // todo: 生命周期正在重构 后续在优化 switch (step) { @@ -182,7 +202,7 @@ export const buildStepValue = (step: number) => { } }; -export const showBadge = (type: string) => { +export const showBadge = (type ="") => { if (!type) { return false; } diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx index 7d721e17108..d4a1d74d98b 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx @@ -26,12 +26,7 @@ import { import FolderModal from '@/pages/DataStudio/LeftContainer/Project/FolderModal'; import JobModal from '@/pages/DataStudio/LeftContainer/Project/JobModal'; import JobTree from '@/pages/DataStudio/LeftContainer/Project/JobTree'; -import { - DataStudioParams, - StateType, - STUDIO_MODEL, - STUDIO_MODEL_ASYNC -} from '@/pages/DataStudio/model'; +import { STUDIO_MODEL } from '@/pages/DataStudio/model'; import { handleAddOrUpdate, handleOption, @@ -47,6 +42,9 @@ import { Modal, Typography } from 'antd'; import { MenuInfo } from 'rc-menu/es/interface'; import React, { Key, useEffect, useState } from 'react'; import { connect } from 'umi'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data.d"; +import {DataStudioLeftTopSliderStateModelAsync} from "@/model/DataStudio/LeftContainer/model"; +import {DataStudioMiddleStateModel} from "@/model/DataStudio/MiddleContainer/model"; const { Text } = Typography; @@ -130,7 +128,7 @@ const Project: React.FC = (props: connect) => { path.pop(); dispatch({ - type: STUDIO_MODEL.addTab, + type: DataStudioMiddleStateModel.addTab, payload: { icon: type, id: parentId + name, @@ -186,7 +184,7 @@ const Project: React.FC = (props: connect) => { options.url = API_CONSTANTS.SAVE_OR_UPDATE_CATALOGUE_URL; } - handleAddOrUpdate( + await handleAddOrUpdate( options.url, { ...values, @@ -226,6 +224,16 @@ const Project: React.FC = (props: connect) => { isCreateTask: false, isCut: false })); + dispatch({ type: DataStudioLeftTopSliderStateModelAsync.queryProjectData }); + if (projectState.isRename) { + // todo: 如果是重命名/修改(修改了名字), 则需要 更新 tab 的 label + dispatch({ + type: DataStudioMiddleStateModel.removeTab, + payload: { + key:values.id,title: values.name, + } + }); + } } ); }; @@ -240,8 +248,13 @@ const Project: React.FC = (props: connect) => { handleContextCancel(); if (!isLeaf) { await handleRemoveById(API_CONSTANTS.DELETE_CATALOGUE_BY_ID_URL, key, () => { - dispatch({ type: STUDIO_MODEL_ASYNC.queryProject }); + // dispatch({ type: STUDIO_MODEL_ASYNC.queryProject }); + dispatch({ type: DataStudioLeftTopSliderStateModelAsync.queryProjectData }); // TODO: 如果打开的 tag 中包含了这个 key 则更新 dav 的 tag 数据 删除此项 && 有一个 bug Dinky/src/pages/DataStudio/RightContainer/JobInfo/index.tsx:55 -> Cannot read properties of undefined (reading 'id') + dispatch({ + type: DataStudioMiddleStateModel.removeTab, + payload: key, + }) }); return; } @@ -263,8 +276,8 @@ const Project: React.FC = (props: connect) => { onOk: async () => { await handleRemoveById(API_CONSTANTS.DELETE_CATALOGUE_BY_ID_URL, key, () => { // TODO: 如果打开的 tag 中包含了这个 key 则更新 dav 的 tag 数据 删除此项 - // dispatch({ type: STUDIO_MODEL.removeTag, payload: taskId }); - dispatch({ type: STUDIO_MODEL_ASYNC.queryProject }); + dispatch({ type: DataStudioMiddleStateModel.removeTab, payload: key }); + dispatch({ type: DataStudioLeftTopSliderStateModelAsync.queryProjectData }); }); } }); @@ -304,7 +317,7 @@ const Project: React.FC = (props: connect) => { API_CONSTANTS.COPY_TASK_URL, l('right.menu.copy'), { ...projectState.value }, - () => dispatch({ type: STUDIO_MODEL_ASYNC.queryProject }) + () => dispatch({ type: DataStudioLeftTopSliderStateModelAsync.queryProjectData }) ); handleContextCancel(); }; @@ -343,7 +356,7 @@ const Project: React.FC = (props: connect) => { })); } ); - dispatch({ type: STUDIO_MODEL_ASYNC.queryProject }); + dispatch({ type: DataStudioLeftTopSliderStateModelAsync.queryProjectData }); handleContextCancel(); }; @@ -472,7 +485,8 @@ const Project: React.FC = (props: connect) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs, - project: Studio.project +export default connect(({ + DataStudioMiddleState }: { + DataStudioMiddleState: DataStudioMiddleStateType }) => ({ + tabInfo: DataStudioMiddleState.tabInfo }))(Project); diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/index.tsx index 7db42c332ee..5468110989d 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/index.tsx @@ -17,50 +17,69 @@ * */ -import { CircleBtn, CircleDataStudioButtonProps } from '@/components/CallBackButton/CircleBtn'; -import MovableSidebar, { MovableSidebarProps } from '@/components/Sidebar/MovableSidebar'; +import {CircleBtn, CircleDataStudioButtonProps} from '@/components/CallBackButton/CircleBtn'; +import MovableSidebar, {MovableSidebarProps} from '@/components/Sidebar/MovableSidebar'; import useThemeValue from '@/hooks/useThemeValue'; import ProjectTitle from '@/pages/DataStudio/LeftContainer/Project/ProjectTitle'; -import { StateType, STUDIO_MODEL, VIEW } from '@/pages/DataStudio/model'; -import { BtnRoute, LeftSide } from '@/pages/DataStudio/route'; -import { connect } from '@@/exports'; -import { Tabs } from 'antd'; +import {BtnRoute, LeftSide} from '@/pages/DataStudio/route'; import React from 'react'; +import {DataStudioLayoutStateType, LAYOUT_VIEW} from "@/model/DataStudio/LayoutState/data.d"; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data.d"; +import {connect} from '@umijs/max'; +import {Tabs} from "antd"; +import {DataStudioLayoutStateModel} from "@/model/DataStudio/LayoutState/model"; export type LeftContainerProps = { size: number; }; -const LeftContainer: React.FC = (props: any) => { +const LeftContainer: React.FC = (props) => { const { dispatch, size, - toolContentHeight, - leftContainer, - rightContainer, - tabs: { panes, activeKey } + // toolContentHeight, + middleAreaHeight, + // leftContainer, + // rightContainer, + leftTopSliderContainer, + rightTopSliderContainer, + tabInfo: {tabList, activeKey}, + // tabs: {panes, activeKey} } = props; const themeValue = useThemeValue(); - const MAX_WIDTH = size.width - 2 * VIEW.leftToolWidth - rightContainer.width - 700; + const MAX_WIDTH = size.width - 2 * LAYOUT_VIEW.leftToolWidth - rightTopSliderContainer.width - 700; /** * 侧边栏大小变化 * @param width */ const handleReSizeChange = (width: any) => { + // dispatch({ + // type: STUDIO_MODEL.updateLeftWidth, + // payload: width + // }); + dispatch({ - type: STUDIO_MODEL.updateLeftWidth, + type: DataStudioLayoutStateModel.updateLeftTopSliderContainerWidth, payload: width }); + + }; /** * 侧边栏最小化 */ const handleMinimize = () => { + // dispatch({ + // type: STUDIO_MODEL.updateSelectLeftKey, + // payload: '' + // }); + dispatch({ - type: STUDIO_MODEL.updateSelectLeftKey, + type: DataStudioLayoutStateModel.updateLeftTopSliderContainerSelectKey, payload: '' }); + }; /** @@ -75,32 +94,32 @@ const LeftContainer: React.FC = (props: any) => { * @type {{onResize: (event: any, direction: any, elementRef: {offsetWidth: any}) => void, visible: boolean, defaultSize: {width: any, height: any}, enable: {right: boolean}, minWidth: number, title: string, handlerMinimize: () => void, contentHeight: any, maxWidth: number}} */ const restMovableSidebarProps: MovableSidebarProps = { - contentHeight: toolContentHeight, + contentHeight: middleAreaHeight, onResize: (event: any, direction: any, elementRef: { offsetWidth: any }) => handleReSizeChange(elementRef.offsetWidth), - title: , + title: , handlerMinimize: () => handleMinimize(), handlerMaxsize: handleMaxsize, - visible: leftContainer.selectKey !== '', - defaultSize: { width: leftContainer.width, height: leftContainer.height }, + visible: leftTopSliderContainer.selectKey !== '', + defaultSize: {width: leftTopSliderContainer.width, height: leftTopSliderContainer.height}, minWidth: 160, maxWidth: MAX_WIDTH, - enable: { right: true }, - btnGroup: BtnRoute[leftContainer.selectKey] - ? BtnRoute[leftContainer.selectKey].map((item: CircleDataStudioButtonProps) => ( - item.onClick?.(panes, activeKey)} - key={item.title} - /> - )) + enable: {right: true}, + btnGroup: BtnRoute[leftTopSliderContainer.selectKey] + ? BtnRoute[leftTopSliderContainer.selectKey].map((item: CircleDataStudioButtonProps) => ( + item.onClick?.(tabList, activeKey)} + key={item.title} + /> + )) : [], - style: { borderInlineEnd: `1px solid ${themeValue.borderColor}` } + style: {borderInlineEnd: `1px solid ${themeValue.borderColor}`} }; const content = ( - + ); return ( @@ -111,9 +130,16 @@ const LeftContainer: React.FC = (props: any) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - leftContainer: Studio.leftContainer, - rightContainer: Studio.rightContainer, - toolContentHeight: Studio.toolContentHeight, - tabs: Studio.tabs +export default connect(({ + DataStudioLayoutState, + DataStudioMiddleModel + }: + { + DataStudioLayoutState: DataStudioLayoutStateType; + DataStudioMiddleModel: DataStudioMiddleStateType + }) => ({ + leftTopSliderContainer: DataStudioLayoutState.leftTopSliderContainer, + rightTopSliderContainer: DataStudioLayoutState.rightTopSliderContainer, + middleAreaHeight: DataStudioLayoutState.middleAreaHeight, + tabInfo: DataStudioMiddleModel.tabInfo }))(LeftContainer); diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx index cf9a0f3c248..737a8d57dd5 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx @@ -17,27 +17,27 @@ * */ -import { useEditor } from '@/hooks/useEditor'; -import { getCurrentTab } from '@/pages/DataStudio/function'; -import { TASK_VAR_FILTER } from '@/pages/DataStudio/MiddleContainer/Editor/constants'; +import {useEditor} from '@/hooks/useEditor'; +import {getCurrentTab} from '@/pages/DataStudio/function'; +import {TASK_VAR_FILTER} from '@/pages/DataStudio/MiddleContainer/Editor/constants'; import DiffModal from '@/pages/DataStudio/MiddleContainer/Editor/DiffModal'; -import { - DataStudioTabsItemType, - StateType, - STUDIO_MODEL, - TaskDataType -} from '@/pages/DataStudio/model'; -import { JOB_LIFE_CYCLE } from '@/pages/DevOps/constants'; -import { API_CONSTANTS } from '@/services/endpoints'; -import { convertCodeEditTheme } from '@/utils/function'; -import { l } from '@/utils/intl'; -import { connect, useRequest } from '@@/exports'; -import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons'; -import { Editor } from '@monaco-editor/react'; -import { Button, Spin } from 'antd'; -import { editor, KeyCode, KeyMod } from 'monaco-editor'; -import React, { useState } from 'react'; -import { format } from 'sql-formatter'; +import {DataStudioTabsItemType, TaskDataType} from '@/pages/DataStudio/model'; +import {JOB_LIFE_CYCLE} from '@/pages/DevOps/constants'; +import {API_CONSTANTS} from '@/services/endpoints'; +import {convertCodeEditTheme} from '@/utils/function'; +import {l} from '@/utils/intl'; +import {connect, useRequest} from '@@/exports'; +import {FullscreenExitOutlined, FullscreenOutlined} from '@ant-design/icons'; +import {Editor} from '@monaco-editor/react'; +import {Button, Spin} from 'antd'; +import {editor, KeyCode, KeyMod} from 'monaco-editor'; +import React, {useState} from 'react'; +import {format} from 'sql-formatter'; +import {DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; +import {DataStudioRightStateType} from "@/model/DataStudio/RightContainer/data"; +import {DataStudioMiddleStateModel} from "@/model/DataStudio/MiddleContainer/model"; +import {DataStudioLayoutStateModel} from "@/model/DataStudio/LayoutState/model"; export type EditorProps = { taskId: number; @@ -47,23 +47,24 @@ export type EditorProps = { const CodeEditor: React.FC = (props) => { const { taskId, - tabs: { panes, activeKey }, + tabInfo: {tabList, activeKey}, dispatch, - height + height, + footerContainer } = props; const [isModalOpen, setIsModalOpen] = useState(false); const [diff, setDiff] = useState([]); - const { fullscreen, setFullscreen } = useEditor(); - const [editorIns, setEditorIns] = useState(null); + const {fullscreen, setFullscreen} = useEditor(); + const [editorIns, setEditorIns] = useState(); - const currentTab = getCurrentTab(panes, activeKey) as DataStudioTabsItemType; + const currentTab = getCurrentTab(tabList, activeKey) as DataStudioTabsItemType; const currentData = currentTab.params.taskData; const loadTask = (cache: TaskDataType, serverParams: TaskDataType) => { if (!cache) { - currentTab.params.taskData = { ...serverParams, useResult: true, maxRowNum: 100 }; - dispatch({ type: STUDIO_MODEL.saveTabs, payload: { ...props.tabs } }); + currentTab.params.taskData = {...serverParams, useResult: true, maxRowNum: 100}; + dispatch({type: DataStudioMiddleStateModel.updateTabsTabList, payload: {...props.tabList}}); return; } const diff: any[] = []; @@ -71,7 +72,7 @@ const CodeEditor: React.FC = (props) => { if (TASK_VAR_FILTER.includes(key)) { cache[key] = serverParams[key]; } else if (JSON.stringify(serverParams[key]) !== JSON.stringify(cache[key])) { - diff.push({ key: key, server: serverParams[key], cache: cache[key] }); + diff.push({key: key, server: serverParams[key], cache: cache[key]}); } }); if (diff.length > 0) { @@ -80,25 +81,25 @@ const CodeEditor: React.FC = (props) => { } }; - const { loading, data } = useRequest( - { url: API_CONSTANTS.TASK, params: { id: taskId } }, - { onSuccess: (data: any) => loadTask(currentTab.params.taskData, data) } + const {loading, data} = useRequest( + {url: API_CONSTANTS.TASK, params: {id: taskId}}, + {onSuccess: (data: any) => loadTask(currentTab.params.taskData, data)} ); const upDateTask = (useServerVersion: boolean) => { if (useServerVersion) { - currentTab.params.taskData = { ...data, useResult: true, maxRowNum: 100 }; + currentTab.params.taskData = {...data, useResult: true, maxRowNum: 100}; currentTab.isModified = false; } else { currentTab.isModified = true; } - dispatch({ type: STUDIO_MODEL.saveTabs, payload: { ...props.tabs } }); + dispatch({type: DataStudioMiddleStateModel.updateTabsTabList, payload: {...props.tabList}}); setIsModalOpen(false); }; return ( -
+
= (props) => { value={currentTab?.params?.taskData?.statement} language={'sql'} options={{ - readOnlyMessage: { value: l('pages.datastudio.editor.onlyread') }, + readOnlyMessage: {value: l('pages.datastudio.editor.onlyread')}, readOnly: currentData?.step == JOB_LIFE_CYCLE.PUBLISH, scrollBeyondLastLine: false, wordWrap: 'on', @@ -133,15 +134,15 @@ const CodeEditor: React.FC = (props) => { editor.focus(); editor.onDidChangeCursorPosition((e) => { - props.footContainer.codePosition = [e.position.lineNumber, e.position.column]; + const {position: {lineNumber, column}} = e; dispatch({ - type: STUDIO_MODEL.saveFooterValue, - payload: { ...props.footContainer } + type: DataStudioLayoutStateModel.updateFooterContainerInfo, + payload: {...{...footerContainer, codePosition: [lineNumber, column]}} }); }); editor.addCommand(KeyMod.Alt | KeyCode.Digit3, () => { - editor?.trigger('anyString', 'editor.action.formatDocument'); + editor?.trigger('anyString', 'editor.action.formatDocument', {}); editor.setValue(format(editor.getValue())); }); setEditorIns(editor); @@ -155,8 +156,8 @@ const CodeEditor: React.FC = (props) => { } currentTab.isModified = true; dispatch({ - type: STUDIO_MODEL.saveTabs, - payload: { ...props.tabs } + type: DataStudioMiddleStateModel.updateTabsTabList, + payload: {...props.tabList} }); }} theme={convertCodeEditTheme()} @@ -174,9 +175,9 @@ const CodeEditor: React.FC = (props) => { style={{ color: '#fff' }} - icon={} + icon={} onClick={() => { - editorIns.layout(); + editorIns?.layout(); setFullscreen(false); }} /> @@ -186,9 +187,9 @@ const CodeEditor: React.FC = (props) => { style={{ color: '#fff' }} - icon={} + icon={} onClick={() => { - editorIns.layout(); + editorIns?.layout(); setFullscreen(true); }} /> @@ -199,7 +200,12 @@ const CodeEditor: React.FC = (props) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs, - footContainer: Studio.footContainer +export default connect(({DataStudioLayoutState, DataStudioMiddleState}: + { + DataStudioLayoutState: DataStudioLayoutStateType; + DataStudioMiddleState: DataStudioMiddleStateType; + DataStudioRightState: DataStudioRightStateType + }) => ({ + tabInfo: DataStudioMiddleState.tabInfo, + footerContainer: DataStudioLayoutState.footerContainer, }))(CodeEditor); diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx index 4ca6d5fbd56..43c01ff6f75 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx @@ -28,7 +28,7 @@ import Editor from '@/pages/DataStudio/MiddleContainer/Editor'; import { getTabIcon } from '@/pages/DataStudio/MiddleContainer/function'; import KeyBoard from '@/pages/DataStudio/MiddleContainer/KeyBoard'; import QuickGuide from '@/pages/DataStudio/MiddleContainer/QuickGuide'; -import { StateType, STUDIO_MODEL, TabsItemType, TabsPageType } from '@/pages/DataStudio/model'; +import { STUDIO_MODEL, TabsItemType, TabsPageType } from '@/pages/DataStudio/model'; import { RightSide } from '@/pages/DataStudio/route'; import RightTagsRouter from '@/pages/RegCenter/DataSource/components/DataSourceDetail/RightTagsRouter'; import { l } from '@/utils/intl'; @@ -37,6 +37,14 @@ import { ExclamationCircleFilled } from '@ant-design/icons'; import { ConfigProvider, Divider, Dropdown, Modal, Space, Tabs, Typography } from 'antd'; import { MenuInfo } from 'rc-menu/es/interface'; import React, { useState } from 'react'; +import {DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; +import {DataStudioMiddleStateType, TabsItem} from "@/model/DataStudio/MiddleContainer/data"; +import {DataStudioRightStateType} from "@/model/DataStudio/RightContainer/data"; +import {DataStudioMiddleStateModel} from "@/model/DataStudio/MiddleContainer/model"; +import {DataStudioLayoutStateModel} from "@/model/DataStudio/LayoutState/model"; +import {DataStudioFooterStateModel} from "@/model/DataStudio/FooterContainer/model"; +import {DataStudioRightStateModel} from "@/model/DataStudio/RightContainer/model"; +import {DataStudioLeftTopSliderStateModel} from "@/model/DataStudio/LeftContainer/model"; const { Text } = Typography; const { confirm } = Modal; @@ -45,9 +53,10 @@ type TargetKey = React.MouseEvent | React.KeyboardEvent | string; const MiddleContainer = (props: any) => { const { - tabs: { panes, activeKey }, - rightKey, - dispatch + tabInfo: { tabList, activeKey }, + rightTopSliderSelectKey, + dispatch, + middleContainer:{height : middleContainerHeight}, } = props; const themeValue = useThemeValue(); @@ -58,8 +67,8 @@ const MiddleContainer = (props: any) => { const [includeTab, setIncludeTab] = useState({}); const updateRightKey = (key: string) => { - const oldPane = panes.find((pane: TabsItemType) => pane.key === activeKey); - const newPane = panes.find((pane: TabsItemType) => pane.key === key); + const oldPane = tabList.find((pane: TabsItemType) => pane.key === activeKey); + const newPane = tabList.find((pane: TabsItemType) => pane.key === key); let oldRightSideAvailableKey: string[] = []; let newRightSideAvailableKey: string[] = []; @@ -80,19 +89,19 @@ const MiddleContainer = (props: any) => { } }); - if (!rightKey || newRightSideAvailableKey.includes(rightKey)) { + if (!rightTopSliderSelectKey || newRightSideAvailableKey.includes(rightTopSliderSelectKey)) { return; } if (newRightSideAvailableKey.length === 0) { dispatch({ - type: STUDIO_MODEL.updateSelectRightKey, + type: DataStudioLayoutStateModel.updateRightTopSliderContainerSelectKey, payload: '' }); return; } - const oldIndex = oldRightSideAvailableKey.findIndex((value) => value === rightKey); + const oldIndex = oldRightSideAvailableKey.findIndex((value) => value === rightTopSliderSelectKey); let selectKey: string; if (oldIndex >= newRightSideAvailableKey.length) { selectKey = newRightSideAvailableKey.pop() as string; @@ -101,13 +110,12 @@ const MiddleContainer = (props: any) => { } dispatch({ - type: STUDIO_MODEL.updateSelectRightKey, + type: DataStudioLayoutStateModel.updateRightTopSliderContainerSelectKey, payload: selectKey }); }; - const updateActiveKey = (item: TabsItemType) => { - const { key, value, treeKey } = item; + const updateActiveKey = (key: string, value: string) => { if (key === activeKey) { return; } @@ -115,28 +123,39 @@ const MiddleContainer = (props: any) => { setContextMenuVisible(false); updateRightKey(key); + /** + * 更新 tabs activeKey + */ dispatch({ - type: STUDIO_MODEL.updateTabsActiveKey, + type: DataStudioMiddleStateModel.updateTabsActiveKey, payload: key }); - // 这里如果加此项功能和定位功能重复 , 暂时注释 - // if (item.type === TabsPageType.project) { - // 更新左侧树选中的 key - // dispatch({ - // type: STUDIO_MODEL.updateProjectSelectKey, - // payload: [treeKey] - // }); - // } - - if (item.type === TabsPageType.metadata) { - // 替换掉 . 为 /, 因为再 tree 里选中的 key 是 / 分割的 - const name = value.toString().replace('.', '/'); - dispatch({ - type: STUDIO_MODEL.updateDatabaseSelectKey, - payload: [name] - }); - } + /** + * 更新 footer 信息 + */ + dispatch({ + type: DataStudioFooterStateModel.updateFooterConfigInfo, + payload: { + activeKey: key, + tabList: tabList + } + }); + /** + * 更新 右侧选中的key + */ + dispatch({ + type: DataStudioLayoutStateModel.updateRightTopSliderContainerSelectKey, + payload: '' + }) + + + // 替换掉 . 为 /, 因为再 tree 里选中的 key 是 / 分割的 + const name = value.toString().replace('.', '/'); + dispatch({ + type: DataStudioLeftTopSliderStateModel.updateDataSourceTreeSelectKey, + payload: [name] + }); }; /** @@ -144,11 +163,11 @@ const MiddleContainer = (props: any) => { */ const handleCloseAllTabs = () => { dispatch({ - type: STUDIO_MODEL.closeAllTabs + type: DataStudioMiddleStateModel.removeAllTabs }); setContextMenuVisible(false); dispatch({ - type: STUDIO_MODEL.updateSelectRightKey, + type: DataStudioLayoutStateModel.updateRightTopSliderContainerSelectKey, payload: '' }); }; @@ -158,7 +177,7 @@ const MiddleContainer = (props: any) => { */ const handleCloseOtherTabs = () => { dispatch({ - type: STUDIO_MODEL.closeOtherTabs, + type: DataStudioMiddleStateModel.removeOtherTabs, payload: includeTab }); setContextMenuVisible(false); @@ -170,7 +189,7 @@ const MiddleContainer = (props: any) => { const handleRightClick = (info: React.MouseEvent, item: TabsItemType) => { // 阻止默认右键事件 info.preventDefault(); - updateActiveKey(item); + updateActiveKey(item.key, item.label); // 设置选中的值 setIncludeTab(item); @@ -227,7 +246,7 @@ const MiddleContainer = (props: any) => { /** * render tabs */ - const tabItems = panes.map((item: TabsItemType) => { + const tabItems = tabList.map((item: TabsItemType) => { const renderContent = () => { if (isDataStudioTabsItemType(item)) { if (parseInt(activeKey) < 0) { @@ -261,7 +280,7 @@ const MiddleContainer = (props: any) => { key: item.key, label: ( updateActiveKey(item)} + onClick={() => updateActiveKey(item.key, item.label)} size={0} onContextMenu={(e) => handleRightClick(e, item)} key={item.key} @@ -269,7 +288,7 @@ const MiddleContainer = (props: any) => { {getTabIcon(item.icon, 16)} {item.label} - {item.isModified ? ' *' : ''} + {item.isModified ? '*' : ''} ), @@ -279,7 +298,7 @@ const MiddleContainer = (props: any) => { activeKey === item.key ? fullscreen ? document.body.clientHeight - : props.centerContentHeight - 40 + : middleContainerHeight - 40 : 0 } > @@ -294,21 +313,21 @@ const MiddleContainer = (props: any) => { * @param {TargetKey} targetKey */ const handleCloseTab = (targetKey: string) => { - if (panes.length === 1) { + if (tabList.length === 1) { dispatch({ - type: STUDIO_MODEL.updateSelectRightKey, + type: DataStudioLayoutStateModel.updateRightTopSliderContainerSelectKey, payload: '' }); } dispatch({ - type: STUDIO_MODEL.closeTab, + type: DataStudioMiddleStateModel.removeTab, payload: targetKey }); }; const closeTab = (targetKey: TargetKey) => { if (typeof targetKey == 'string') { - const tab = getCurrentTab(panes, targetKey); + const tab = getCurrentTab(tabList, targetKey); if (tab?.isModified) { confirm({ title: l('pages.datastudio.editor.notsave'), @@ -371,8 +390,9 @@ const MiddleContainer = (props: any) => { return <>{renderMiddleContent()}; }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs, - centerContentHeight: Studio.centerContentHeight, - rightKey: Studio.rightContainer.selectKey +export default connect(({ DataStudioLayoutState,DataStudioMiddleState}: +{ DataStudioLayoutState: DataStudioLayoutStateType ;DataStudioMiddleState:DataStudioMiddleStateType; DataStudioRightState:DataStudioRightStateType }) => ({ + tabInfo: DataStudioMiddleState.tabInfo, + middleContainer: DataStudioLayoutState.middleContainer, + rightTopSliderSelectKey: DataStudioLayoutState.rightTopSliderContainer.selectKey, }))(MiddleContainer); diff --git a/dinky-web/src/pages/DataStudio/RightContainer/HistoryVersion/index.tsx b/dinky-web/src/pages/DataStudio/RightContainer/HistoryVersion/index.tsx index 7bb152a5e7b..a205e3c625b 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/HistoryVersion/index.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/HistoryVersion/index.tsx @@ -18,7 +18,6 @@ */ import VersionList from '@/components/VersionList'; import { getCurrentData } from '@/pages/DataStudio/function'; -import { StateType } from '@/pages/DataStudio/model'; import { handleOption, handleRemoveById } from '@/services/BusinessCrud'; import { API_CONSTANTS } from '@/services/endpoints'; import { TaskVersionListItem } from '@/types/Studio/data'; @@ -30,13 +29,14 @@ import { Button, Card, Modal, Tag } from 'antd'; import moment from 'moment'; import React, { useState } from 'react'; import { connect } from 'umi'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data.d"; const HistoryVersion = (props: any) => { const { - tabs: { panes, activeKey } + tabInfo: { tabList, activeKey } } = props; - const current = getCurrentData(panes, activeKey); + const current = getCurrentData(tabList, activeKey); const versionList = useRequest({ url: API_CONSTANTS.GET_JOB_VERSION, params: { taskId: current?.id } @@ -145,6 +145,9 @@ const HistoryVersion = (props: any) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs +export default connect(({ + DataStudioMiddleState}: { + DataStudioMiddleState: DataStudioMiddleStateType +}) => ({ + tabInfo: DataStudioMiddleState.tabInfo }))(HistoryVersion); diff --git a/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx b/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx index 1b1f8cb6747..725ce99a33d 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx @@ -17,13 +17,9 @@ * */ -import { SAVE_POINT_TYPE } from '@/pages/DataStudio/constants'; -import { - getCurrentData, - getCurrentTab, - isDataStudioTabsItemType -} from '@/pages/DataStudio/function'; -import { SessionType, StateType, STUDIO_MODEL } from '@/pages/DataStudio/model'; +import {SAVE_POINT_TYPE} from '@/pages/DataStudio/constants'; +import {getCurrentData, getCurrentTab, isDataStudioTabsItemType} from '@/pages/DataStudio/function'; +import {SessionType} from '@/pages/DataStudio/model'; import { buildAlertGroupOptions, buildClusterConfigOptions, @@ -32,10 +28,9 @@ import { buildRunModelOptions, calculatorWidth } from '@/pages/DataStudio/RightContainer/JobConfig/function'; -import { AlertStateType } from '@/pages/RegCenter/Alert/AlertInstance/model'; -import { RUN_MODE, SWITCH_OPTIONS } from '@/services/constants'; -import { l } from '@/utils/intl'; -import { InfoCircleOutlined } from '@ant-design/icons'; +import {RUN_MODE, SWITCH_OPTIONS} from '@/services/constants'; +import {l} from '@/utils/intl'; +import {InfoCircleOutlined} from '@ant-design/icons'; import { ProForm, ProFormDigit, @@ -45,27 +40,32 @@ import { ProFormSwitch, ProFormText } from '@ant-design/pro-components'; -import { Badge, Space, Typography } from 'antd'; -import { useForm } from 'antd/es/form/Form'; -import { debounce } from 'lodash'; -import { useEffect } from 'react'; -import { connect } from 'umi'; +import {Badge, Space, Typography} from 'antd'; +import {useForm} from 'antd/es/form/Form'; +import {debounce} from 'lodash'; +import {useEffect} from 'react'; +import {connect} from 'umi'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data.d"; +import {DataStudioRightStateType} from "@/model/DataStudio/RightContainer/data.d"; +import {DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data.d"; +import {DataStudioMiddleStateModel} from "@/model/DataStudio/MiddleContainer/model"; +import rightContainer from "@/pages/DataStudio/RightContainer"; -const { Text } = Typography; +const {Text} = Typography; const JobConfig = (props: any) => { const { - sessionCluster, - clusterConfiguration, + sessionClusterData, + clusterConfigurationData, dispatch, - tabs: { panes, activeKey }, - env, - group, - rightContainer, + tabInfo: {tabList, activeKey}, + envData, + alertGroupData, + rightTopSliderContainer, flinkConfigOptions } = props; - const current = getCurrentData(panes, activeKey); + const current = getCurrentData(tabList, activeKey); const currentSession: SessionType = { connectors: [], @@ -84,7 +84,7 @@ const JobConfig = (props: any) => { }, [current]); const onValuesChange = (change: { [key in string]: any }, all: any) => { - const pane = getCurrentTab(panes, activeKey); + const pane = getCurrentTab(tabList, activeKey); if (!isDataStudioTabsItemType(pane)) { return; } @@ -104,8 +104,8 @@ const JobConfig = (props: any) => { }); pane.isModified = true; dispatch({ - type: STUDIO_MODEL.saveTabs, - payload: { ...props.tabs } + type: DataStudioMiddleStateModel.updateTabsTabList, + payload: {...props.tabList} }); }; @@ -116,12 +116,12 @@ const JobConfig = (props: any) => { const statusElement = currentSession.sessionConfig?.clusterId ? ( - + {currentSession.sessionConfig.clusterName} ) : ( - + {l('pages.devops.jobinfo.localenv')} ); @@ -130,14 +130,14 @@ const JobConfig = (props: any) => { statusElement ) : ( { ); return ( -
+
{ alertGroupId: 0 }} className={'data-studio-form'} - style={{ paddingInline: '15px', overflow: 'scroll' }} + style={{paddingInline: '15px', overflow: 'scroll'}} form={form} submitter={false} layout='vertical' @@ -186,7 +186,7 @@ const JobConfig = (props: any) => { tooltip={l('pages.datastudio.label.jobConfig.clusterConfig.tip1', '', { type: current?.type })} - options={buildClusterConfigOptions(current, clusterConfiguration)} + options={buildClusterConfigOptions(current, clusterConfigurationData)} /> )} @@ -194,7 +194,7 @@ const JobConfig = (props: any) => { name='envId' label={l('pages.datastudio.label.jobConfig.flinksql.env')} tooltip={l('pages.datastudio.label.jobConfig.flinksql.env.tip1')} - options={buildEnvOptions(env)} + options={buildEnvOptions(envData)} showSearch /> @@ -214,7 +214,7 @@ const JobConfig = (props: any) => { valuePropName='checked' tooltip={{ title: l('pages.datastudio.label.jobConfig.insert.tip'), - icon: + icon: }} {...SWITCH_OPTIONS()} /> @@ -224,7 +224,7 @@ const JobConfig = (props: any) => { valuePropName='checked' tooltip={{ title: l('pages.datastudio.label.jobConfig.fragment.tip'), - icon: + icon: }} {...SWITCH_OPTIONS()} /> @@ -234,7 +234,7 @@ const JobConfig = (props: any) => { valuePropName='checked' tooltip={{ title: l('pages.datastudio.label.jobConfig.batchmode.tip'), - icon: + icon: }} {...SWITCH_OPTIONS()} /> @@ -260,7 +260,7 @@ const JobConfig = (props: any) => { label={l('pages.datastudio.label.jobConfig.alertGroup')} name='alertGroupId' placeholder={l('pages.datastudio.label.jobConfig.alertGroup.tip')} - options={buildAlertGroupOptions(group)} + options={buildAlertGroupOptions(alertGroupData)} /> { name={['configJson', 'customConfig']} copyIconProps={false} creatorButtonProps={{ - style: { width: '100%' }, + style: {width: '100%'}, creatorButtonText: l('pages.datastudio.label.jobConfig.addConfig') }} > @@ -313,12 +313,20 @@ const JobConfig = (props: any) => { ); }; -export default connect(({ Studio, Alert }: { Studio: StateType; Alert: AlertStateType }) => ({ - sessionCluster: Studio.sessionCluster, - clusterConfiguration: Studio.clusterConfiguration, - rightContainer: Studio.rightContainer, - tabs: Studio.tabs, - env: Studio.env, - group: Alert.group, - flinkConfigOptions: Studio.flinkConfigOptions +export default connect(({ + DataStudioMiddleState, + DataStudioRightState, + DataStudioLayoutState, + }: { + DataStudioMiddleState: DataStudioMiddleStateType; + DataStudioRightState: DataStudioRightStateType; + DataStudioLayoutState: DataStudioLayoutStateType; +}) => ({ + tabInfo: DataStudioMiddleState.tabInfo, + rightTopSliderContainer: DataStudioLayoutState.rightTopSliderContainer, + sessionClusterData: DataStudioRightState.sessionClusterData, + clusterConfigurationData: DataStudioRightState.clusterConfigurationData, + envData: DataStudioRightState.envData, + alertGroupData: DataStudioRightState.alertGroupData, + flinkConfigOptions: DataStudioRightState.flinkConfigOptions }))(JobConfig); diff --git a/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/service.tsx b/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/service.tsx index 0c7e5f2e9b3..706a72250f2 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/service.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/service.tsx @@ -32,3 +32,8 @@ export function getClusterConfigurationData() { export function getFlinkConfigs() { return queryDataByParams('/api/flinkConf/configOptions'); } + +export function getAlertGroupData() { + return queryDataByParams('/api/alertGroup/listEnabledAll'); +} + diff --git a/dinky-web/src/pages/DataStudio/RightContainer/JobInfo/index.tsx b/dinky-web/src/pages/DataStudio/RightContainer/JobInfo/index.tsx index 982666bd287..5772a4d62bf 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/JobInfo/index.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/JobInfo/index.tsx @@ -24,18 +24,20 @@ import { Descriptions } from 'antd'; import Paragraph from 'antd/es/typography/Paragraph'; import { useEffect, useState } from 'react'; import { connect } from 'umi'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; const JobInfo = (props: any) => { const { - tabs: { panes, activeKey, activeBreadcrumbTitle } + tabInfo: { tabList, activeKey } } = props; + const [currentInfo, setCurrentInfo] = useState>( - getCurrentData(panes, activeKey) ?? {} + getCurrentData(tabList, activeKey) ?? {} ); useEffect(() => { - setCurrentInfo({ ...getCurrentData(panes, activeKey) }); - }, [activeKey, activeBreadcrumbTitle]); + setCurrentInfo({ ...currentInfo, ...getCurrentData(tabList, activeKey) }); + }, [activeKey, tabList]); return (
@@ -65,6 +67,9 @@ const JobInfo = (props: any) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs +export default connect(({ + DataStudioMiddleState}: { + DataStudioMiddleState: DataStudioMiddleStateType +}) => ({ + tabInfo: DataStudioMiddleState.tabInfo }))(JobInfo); diff --git a/dinky-web/src/pages/DataStudio/RightContainer/SavePoints/index.tsx b/dinky-web/src/pages/DataStudio/RightContainer/SavePoints/index.tsx index 744ebf9a753..27cc5f24384 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/SavePoints/index.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/SavePoints/index.tsx @@ -18,44 +18,44 @@ */ import { getCurrentData } from '@/pages/DataStudio/function'; -import { StateType } from '@/pages/DataStudio/model'; import { postAll } from '@/services/api'; import { SavePoint } from '@/types/Studio/data'; import { l } from '@/utils/intl'; -import { ActionType, ProDescriptions, ProTable } from '@ant-design/pro-components'; +import {ActionType, ProColumns, ProDescriptions, ProTable} from '@ant-design/pro-components'; import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions'; import { Drawer } from 'antd'; import { useRef, useState } from 'react'; import { connect } from 'umi'; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; const url = '/api/savepoints'; const SavePoints = (props: any) => { const { - tabs: { panes, activeKey } + tabInfo: { tabList, activeKey } } = props; - const current = getCurrentData(panes, activeKey); + const current = getCurrentData(tabList, activeKey); const [row, setRow] = useState(); const actionRef = useRef(); actionRef.current?.reloadAndRest?.(); - const columns: ProDescriptionsItemProps[] = [ - { - title: l('pages.task.savePointPath'), - dataIndex: 'path', - hideInForm: true, - hideInSearch: true - }, + const columns: ProColumns[] | ProDescriptionsItemProps[] = [ { title: l('global.table.createTime'), dataIndex: 'createTime', valueType: 'dateTime', hideInForm: true, hideInSearch: true, - render: (dom, entity) => { + render: (dom: any, entity: SavePoint) => { return setRow(entity)}>{dom}; } - } + }, + { + title: l('pages.task.savePointPath'), + dataIndex: 'path', + hideInForm: true, + hideInSearch: true + }, ]; return ( @@ -64,9 +64,9 @@ const SavePoints = (props: any) => { actionRef={actionRef} rowKey='id' request={(params, sorter, filter) => - postAll(url, { taskId: current.key, ...params, sorter, filter }) + postAll(url, { taskId: current?.key, ...params, sorter, filter }) } - columns={columns} + columns={columns as ProColumns[]} search={false} /> { params={{ id: row?.name }} - columns={columns} + columns={columns as ProDescriptionsItemProps[]} /> )} @@ -95,6 +95,9 @@ const SavePoints = (props: any) => { ); }; -export default connect(({ Studio }: { Studio: StateType }) => ({ - tabs: Studio.tabs +export default connect(({ + DataStudioMiddleState}: { + DataStudioMiddleState: DataStudioMiddleStateType +}) => ({ + tabInfo: DataStudioMiddleState.tabInfo }))(SavePoints); diff --git a/dinky-web/src/pages/DataStudio/RightContainer/index.tsx b/dinky-web/src/pages/DataStudio/RightContainer/index.tsx index a2a6d979903..f36d95d9f8c 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/index.tsx @@ -20,13 +20,15 @@ import Title from '@/components/Front/Title'; import MovableSidebar from '@/components/Sidebar/MovableSidebar'; import useThemeValue from '@/hooks/useThemeValue'; -import { mapDispatchToProps } from '@/pages/DataStudio/function'; -import { StateType, VIEW } from '@/pages/DataStudio/model'; -import { RightSide } from '@/pages/DataStudio/route'; -import { l } from '@/utils/intl'; -import { connect } from '@@/exports'; -import { Tabs } from 'antd'; +import {mapDispatchToProps} from '@/pages/DataStudio/function'; +import {VIEW} from '@/pages/DataStudio/model'; +import {RightSide} from '@/pages/DataStudio/route'; +import {l} from '@/utils/intl'; +import {connect} from '@@/exports'; +import {Tabs} from 'antd'; import React from 'react'; +import {DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; +import {DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; export type RightContainerProps = { size: number; @@ -36,17 +38,17 @@ const RightContainer: React.FC = (prop: any) => { const themeValue = useThemeValue(); const { size, - leftContainer, - rightContainer, - toolContentHeight, + leftTopSliderContainer, + rightTopSliderContainer, + middleAreaHeight, updateRightWidth, updateSelectRightKey, - tabs + tabInfo: {tabList} } = prop; - const maxWidth = size.width - 2 * VIEW.leftToolWidth - leftContainer.width - 600; + const maxWidth = size.width - 2 * VIEW.leftToolWidth - leftTopSliderContainer.width - 600; return ( = (prop: any) => { offsetWidth: any; } ) => updateRightWidth(elementRef.offsetWidth)} - title={{l(rightContainer.selectKey)}} + title={{l(rightTopSliderContainer.selectKey)}} handlerMinimize={() => updateSelectRightKey('')} handlerMaxsize={() => updateRightWidth(maxWidth)} - visible={rightContainer.selectKey !== ''} + visible={rightTopSliderContainer.selectKey !== ''} defaultSize={{ - width: rightContainer.width, - height: rightContainer.height + width: rightTopSliderContainer.width, + height: rightTopSliderContainer.height }} minWidth={200} maxWidth={maxWidth} - enable={{ left: true }} - style={{ borderInlineStart: `1px solid ${themeValue.borderColor}` }} + enable={{left: true}} + style={{borderInlineStart: `1px solid ${themeValue.borderColor}`}} > - {tabs.panes.length > 0 ? ( + {tabList.panes.length > 0 ? ( ) : ( <> @@ -79,15 +81,33 @@ const RightContainer: React.FC = (prop: any) => { ); }; +// +// export default connect( +// ({Studio}: { Studio: StateType }) => ({ +// leftContainer: Studio.leftContainer, +// rightContainer: Studio.rightContainer, +// bottomContainer: Studio.bottomContainer, +// activeBreadcrumbTitle: Studio.tabs.activeBreadcrumbTitle, +// tabs: Studio.tabs, +// toolContentHeight: Studio.toolContentHeight +// }), +// mapDispatchToProps +// )(RightContainer); + export default connect( - ({ Studio }: { Studio: StateType }) => ({ - leftContainer: Studio.leftContainer, - rightContainer: Studio.rightContainer, - bottomContainer: Studio.bottomContainer, - activeBreadcrumbTitle: Studio.tabs.activeBreadcrumbTitle, - tabs: Studio.tabs, - toolContentHeight: Studio.toolContentHeight + ({ + DataStudioLayoutState, + DataStudioMiddleState + }: + { DataStudioLayoutState: DataStudioLayoutStateType; + DataStudioMiddleState: DataStudioMiddleStateType; + }) => ({ + leftTopSliderContainer: DataStudioLayoutState.leftTopSliderContainer, + rightTopSliderContainer: DataStudioLayoutState.rightTopSliderContainer, + leftBottomSliderContainer: DataStudioLayoutState.leftBottomSliderContainer, + tabInfo: DataStudioMiddleState.tabInfo, + middleAreaHeight: DataStudioLayoutState.middleAreaHeight }), mapDispatchToProps )(RightContainer); diff --git a/dinky-web/src/pages/DataStudio/function.ts b/dinky-web/src/pages/DataStudio/function.ts index 951ba600221..05615eb9666 100644 --- a/dinky-web/src/pages/DataStudio/function.ts +++ b/dinky-web/src/pages/DataStudio/function.ts @@ -20,7 +20,6 @@ import { DataStudioTabsItemType, EnvType, - FooterType, JobRunningMsgType, MetadataTabsItemType, STUDIO_MODEL, @@ -28,66 +27,72 @@ import { TabsPageType, TaskDataType } from '@/pages/DataStudio/model'; -import { CONFIG_MODEL_ASYNC } from '@/pages/SettingCenter/GlobalSetting/model'; -import { Cluster, DataSources } from '@/types/RegCenter/data'; -import { Dispatch } from '@@/plugin-dva/types'; +import {CONFIG_MODEL_ASYNC} from '@/pages/SettingCenter/GlobalSetting/model'; +import {Cluster, DataSources} from '@/types/RegCenter/data.d'; +import {Dispatch} from '@@/plugin-dva/types'; +import {FooterConfigInfo, InitFooterConfigInfo} from "@/model/DataStudio/FooterContainer/data.d"; +import {DataStudioLayoutStateModel} from "@/model/DataStudio/LayoutState/model"; +import {DataStudioLeftTopSliderStateModel} from "@/model/DataStudio/LeftContainer/model"; +import {DataStudioRightStateModelAsync} from "@/model/DataStudio/RightContainer/model"; +import {DataStudioMiddleStateModel} from "@/model/DataStudio/MiddleContainer/model"; export const mapDispatchToProps = (dispatch: Dispatch) => ({ updateToolContentHeight: (key: number) => dispatch({ - type: STUDIO_MODEL.updateToolContentHeight, + type: DataStudioLayoutStateModel.updateMiddleContainerHeight, payload: key }), updateCenterContentHeight: (key: number) => dispatch({ - type: STUDIO_MODEL.updateCenterContentHeight, + type: DataStudioLayoutStateModel.updateMiddleAreaHeight, payload: key }), updateSelectLeftKey: (key: string) => dispatch({ - type: STUDIO_MODEL.updateSelectLeftKey, + type: DataStudioLayoutStateModel.updateLeftTopSliderContainerSelectKey, payload: key }), updateLeftWidth: (width: number) => dispatch({ - type: STUDIO_MODEL.updateLeftWidth, + type: DataStudioLayoutStateModel.updateLeftTopSliderContainerWidth, payload: width }), updateSelectRightKey: (key: string) => dispatch({ - type: STUDIO_MODEL.updateSelectRightKey, + type: DataStudioLayoutStateModel.updateRightTopSliderContainerSelectKey, payload: key }), updateRightWidth: (width: number) => dispatch({ - type: STUDIO_MODEL.updateRightWidth, + type: DataStudioLayoutStateModel.updateRightTopSliderContainerWidth, payload: width }), updateSelectBottomKey: (key: string) => dispatch({ - type: STUDIO_MODEL.updateSelectBottomKey, + type: DataStudioLayoutStateModel.updateLeftBottomSliderContainerSelectKey, payload: key }), updateSelectBottomSubKey: (key: string) => dispatch({ - type: STUDIO_MODEL.updateSelectBottomSubKey, + type: DataStudioLayoutStateModel.updateLeftBottomSliderContainerSelectSubKey, payload: key }), updateBottomHeight: (height: number) => dispatch({ - type: STUDIO_MODEL.updateBottomHeight, + type: DataStudioLayoutStateModel.updateLeftBottomSliderContainerHeight, payload: height }), saveDataBase: (data: DataSources.DataSource[]) => dispatch({ - type: STUDIO_MODEL.saveDataBase, + type: DataStudioLeftTopSliderStateModel.updateDataSourceTreeData, payload: data }), saveProject: (data: any[]) => dispatch({ - type: STUDIO_MODEL.saveProject, + type: DataStudioLeftTopSliderStateModel.updateProjectTreeData, payload: data }), + //todo: 重构 updateBottomConsole: (data: string) => dispatch({ type: STUDIO_MODEL.updateBottomConsole, @@ -95,24 +100,25 @@ export const mapDispatchToProps = (dispatch: Dispatch) => ({ }), saveSession: (data: Cluster.Instance[]) => dispatch({ - type: STUDIO_MODEL.saveSession, + type: DataStudioRightStateModelAsync.querySessionClusterData, payload: data }), saveEnv: (data: EnvType[]) => dispatch({ - type: STUDIO_MODEL.saveEnv, + type: DataStudioRightStateModelAsync.queryEnvData, payload: data }), saveTabs: (data: TabsItemType[]) => dispatch({ - type: STUDIO_MODEL.saveTabs, + type: DataStudioMiddleStateModel.updateTabsTabList, payload: data }), saveClusterConfiguration: (data: Cluster.Config[]) => dispatch({ - type: STUDIO_MODEL.saveClusterConfiguration, + type: DataStudioRightStateModelAsync.queryClusterConfigurationData, payload: data }), + //todo: 重构 updateJobRunningMsg: (data: JobRunningMsgType) => dispatch({ type: STUDIO_MODEL.updateJobRunningMsg, @@ -175,12 +181,7 @@ export const getCurrentData = ( return isDataStudioTabsItemType(item) ? item.params.taskData : undefined; }; -export const getFooterValue = (panes: any, activeKey: string): Partial => { +export const getFooterValue = (panes: any, activeKey: string): FooterConfigInfo | {} => { const currentTab = getCurrentTab(panes, activeKey); - return isDataStudioTabsItemType(currentTab) - ? { - codePosition: [1, 1], - codeType: currentTab.subType - } - : {}; + return isDataStudioTabsItemType(currentTab) ?? InitFooterConfigInfo; }; diff --git a/dinky-web/src/pages/DataStudio/index.tsx b/dinky-web/src/pages/DataStudio/index.tsx index b56ab67893f..b3730050ffe 100644 --- a/dinky-web/src/pages/DataStudio/index.tsx +++ b/dinky-web/src/pages/DataStudio/index.tsx @@ -23,10 +23,8 @@ import FooterContainer from '@/pages/DataStudio/FooterContainer'; import { mapDispatchToProps } from '@/pages/DataStudio/function'; import SecondHeaderContainer from '@/pages/DataStudio/HeaderContainer'; import LeftContainer from '@/pages/DataStudio/LeftContainer'; -import { getDataSourceList } from '@/pages/DataStudio/LeftContainer/DataSource/service'; -import { getTaskData } from '@/pages/DataStudio/LeftContainer/Project/service'; import MiddleContainer from '@/pages/DataStudio/MiddleContainer'; -import { StateType, TabsItemType, TabsPageType, VIEW } from '@/pages/DataStudio/model'; +import { TabsItemType, TabsPageType, VIEW } from '@/pages/DataStudio/model'; import RightContainer from '@/pages/DataStudio/RightContainer'; import { getClusterConfigurationData, @@ -39,6 +37,10 @@ import { Layout, Menu, theme } from 'antd'; import { useEffect, useState } from 'react'; import { PersistGate } from 'redux-persist/integration/react'; import { connect, getDvaApp } from 'umi'; +import {DataStudioMiddleState, DataStudioMiddleStateType} from "@/model/DataStudio/MiddleContainer/data"; +import {DataStudioLayoutState, DataStudioLayoutStateType} from "@/model/DataStudio/LayoutState/data"; +import {DataStudioLeftTopSliderStateModelAsync} from "@/model/DataStudio/LeftContainer/model"; +import {DataStudioRightStateModelAsync} from "@/model/DataStudio/RightContainer/model"; const { Sider, Content } = Layout; @@ -46,28 +48,23 @@ const { useToken } = theme; const DataStudio = (props: any) => { const { - bottomContainer, - leftContainer, - rightContainer, - saveDataBase, - saveProject, + leftBottomSliderContainer, + leftTopSliderContainer, + rightTopSliderContainer, updateToolContentHeight, - saveSession, - saveEnv, updateCenterContentHeight, updateSelectLeftKey, updateSelectRightKey, updateSelectBottomKey, - saveClusterConfiguration, - activeBreadcrumbTitle, updateSelectBottomSubKey, - tabs + tabInfo:{ activeBreadcrumbTitle,tabList,activeKey}, + dispatch, } = props; const { token } = useToken(); const themeValue = useThemeValue(); const app = getDvaApp(); // 获取dva的实例 const persist = app._store.persist; - const bottomHeight = bottomContainer.selectKey === '' ? 0 : bottomContainer.height; + const bottomHeight = leftBottomSliderContainer.selectKey === '' ? 0 : leftBottomSliderContainer.height; const { fullscreen } = useEditor(); @@ -98,19 +95,27 @@ const DataStudio = (props: any) => { }, []); const loadData = async () => { - Promise.all([ - getDataSourceList(), - getTaskData(), - getSessionData(), - getEnvData(), - getClusterConfigurationData() - ]).then((res) => { - saveDataBase(res[0]); - saveProject(res[1]); - saveSession(res[2]); - saveEnv(res[3]); - saveClusterConfiguration(res[4]); - }); + dispatch({ + type: DataStudioLeftTopSliderStateModelAsync.queryDataSourceData, + }) + dispatch({ + type: DataStudioLeftTopSliderStateModelAsync.queryProjectData, + }) + dispatch({ + type: DataStudioRightStateModelAsync.querySessionClusterData, + }) + dispatch({ + type: DataStudioRightStateModelAsync.queryClusterConfigurationData, + }) + dispatch({ + type: DataStudioRightStateModelAsync.queryEnvData, + }) + dispatch({ + type: DataStudioRightStateModelAsync.queryFlinkConfigOptions, + }) + dispatch({ + type: DataStudioRightStateModelAsync.queryAlertGroupData, + }) }; useEffect(() => { @@ -123,7 +128,7 @@ const DataStudio = (props: any) => { const LeftTopMenu = ( AuthorizedObject({ path: x.auth, children: x, access })).map( (x) => ({ key: x.key, @@ -136,14 +141,14 @@ const DataStudio = (props: any) => { borderBlockStart: `1px solid ${themeValue.borderColor}`, borderInlineEnd: `1px solid ${themeValue.borderColor}` }} - onClick={(item) => updateSelectLeftKey(item.key === leftContainer.selectKey ? '' : item.key)} + onClick={(item) => updateSelectLeftKey(item.key === leftTopSliderContainer.selectKey ? '' : item.key)} /> ); const LeftBottomMenu = ( AuthorizedObject({ path: x.auth, children: x, access }) ).map((x) => ({ @@ -158,10 +163,10 @@ const DataStudio = (props: any) => { borderInlineEnd: `1px solid ${themeValue.borderColor}` }} onClick={(item) => { - updateSelectBottomKey(item.key === bottomContainer.selectKey ? '' : item.key); + updateSelectBottomKey(item.key === leftBottomSliderContainer.selectKey ? '' : item.key); if ( - bottomContainer.selectKey !== '' && - !bottomContainer.selectSubKey[item.key] && + leftBottomSliderContainer.selectKey !== '' && + !leftBottomSliderContainer.selectSubKey[item.key] && LeftBottomMoreTabs[item.key] ) { updateSelectBottomSubKey(LeftBottomMoreTabs[item.key][0].key); @@ -172,7 +177,7 @@ const DataStudio = (props: any) => { const RightTopMenu = ( { if (!x.isShow) { return true; } - if (parseInt(tabs.activeKey) < 0) { + if (parseInt(tabList.activeKey) < 0) { return TabsPageType.None; } - const v = (tabs.panes as TabsItemType[]).find((item) => item.key === tabs.activeKey); + const v = (tabList as TabsItemType[]).find((item) => item.key === activeKey); return x.isShow(v?.type ?? TabsPageType.None, v?.subType); }) .map((x) => { return { key: x.key, label: x.label, icon: x.icon }; })} onClick={(item) => - updateSelectRightKey(item.key === rightContainer.selectKey ? '' : item.key) + updateSelectRightKey(item.key === rightTopSliderContainer.selectKey ? '' : item.key) } /> ); @@ -227,7 +232,7 @@ const DataStudio = (props: any) => { @@ -249,12 +254,17 @@ const DataStudio = (props: any) => { }; export default connect( - ({ Studio }: { Studio: StateType }) => ({ - leftContainer: Studio.leftContainer, - rightContainer: Studio.rightContainer, - bottomContainer: Studio.bottomContainer, - activeBreadcrumbTitle: Studio.tabs.activeBreadcrumbTitle, - tabs: Studio.tabs + ({ + DataStudioMiddleState, + DataStudioLayoutState + }: { + DataStudioMiddleState: DataStudioMiddleStateType; + DataStudioLayoutState: DataStudioLayoutStateType; + }) => ({ + leftTopSliderContainer: DataStudioLayoutState.leftTopSliderContainer, + rightTopSliderContainer: DataStudioLayoutState.rightTopSliderContainer, + leftBottomSliderContainer: DataStudioLayoutState.leftBottomSliderContainer, + tabInfo: DataStudioMiddleState.tabInfo, }), mapDispatchToProps )(DataStudio); diff --git a/dinky-web/src/pages/DevOps/JobDetail/JobMetrics/JobChart/JobChart.tsx b/dinky-web/src/pages/DevOps/JobDetail/JobMetrics/JobChart/JobChart.tsx index f5a61c41204..69a3812d353 100644 --- a/dinky-web/src/pages/DevOps/JobDetail/JobMetrics/JobChart/JobChart.tsx +++ b/dinky-web/src/pages/DevOps/JobDetail/JobMetrics/JobChart/JobChart.tsx @@ -18,7 +18,7 @@ */ import FlinkChart from '@/components/FlinkChart'; -import { SseData } from '@/models/Sse'; +import { SseData } from '@/model/SSE/Sse'; import { SSE_TOPIC } from '@/pages/DevOps/constants'; import { JobMetricsItem } from '@/pages/DevOps/JobDetail/data'; import { getMetricsData } from '@/pages/DevOps/JobDetail/JobMetrics/service'; diff --git a/dinky-web/src/pages/Metrics/index.tsx b/dinky-web/src/pages/Metrics/index.tsx index 5e5a585ead4..9d7c5cb0b5f 100644 --- a/dinky-web/src/pages/Metrics/index.tsx +++ b/dinky-web/src/pages/Metrics/index.tsx @@ -18,7 +18,7 @@ */ import FlinkChart from '@/components/FlinkChart'; -import { SseData } from '@/models/Sse'; +import { SseData } from '@/model/SSE/Sse'; import { SSE_TOPIC } from '@/pages/DevOps/constants'; import Job from '@/pages/Metrics/Job'; import { ChartData, MetricsLayout } from '@/pages/Metrics/Job/data'; diff --git a/dinky-web/src/pages/Other/Login/index.tsx b/dinky-web/src/pages/Other/Login/index.tsx index 586716195dc..83a473e5629 100644 --- a/dinky-web/src/pages/Other/Login/index.tsx +++ b/dinky-web/src/pages/Other/Login/index.tsx @@ -42,7 +42,7 @@ const Login: React.FC = () => { const [localStorageOfToken, setLocalStorageOfToken] = useLocalStorage('token', ''); - const { reconnectSse } = useModel('Sse', (model: any) => ({ reconnectSse: model.reconnectSse })); + // const { reconnectSse } = useModel('Sse', (model: any) => ({ reconnectSse: model.reconnectSse })); const containerClassName = useEmotionCss(() => { return { @@ -99,7 +99,7 @@ const Login: React.FC = () => { /** * Redirect to home page && reconnect Global Sse */ - reconnectSse(); + // reconnectSse(); gotoRedirectUrl(); } else { ErrorMessage(l('login.chooseTenantFailed')); diff --git a/dinky-web/src/pages/RegCenter/DataSource/components/DataSourceList/index.tsx b/dinky-web/src/pages/RegCenter/DataSource/components/DataSourceList/index.tsx index bcfbdb98462..8303a0ff88b 100644 --- a/dinky-web/src/pages/RegCenter/DataSource/components/DataSourceList/index.tsx +++ b/dinky-web/src/pages/RegCenter/DataSource/components/DataSourceList/index.tsx @@ -27,9 +27,10 @@ import { renderDBIcon } from '@/pages/RegCenter/DataSource/components/function'; import { handleTest, saveOrUpdateHandle } from '@/pages/RegCenter/DataSource/service'; import { queryList } from '@/services/api'; import { + getDataByParams, handleOption, handlePutDataByParams, - handleRemoveById, + handleRemoveById, queryDataByParams, updateDataByParam } from '@/services/BusinessCrud'; import { PROTABLE_OPTIONS_PUBLIC, PRO_LIST_CARD_OPTIONS } from '@/services/constants'; @@ -65,6 +66,15 @@ const DataSourceTable: React.FC = (props) => { const [formValues, setFormValues] = useState>({}); const actionRef = React.useRef(); + /** + * execute query list + * set list + */ + const queryDataSourceList = async () => { + const res = await queryDataByParams(API_CONSTANTS.DATASOURCE); + setDataSource(res as DataSources.DataSource[] ?? []); + }; + /** * query list */ @@ -72,14 +82,7 @@ const DataSourceTable: React.FC = (props) => { queryDataSourceList(); }, []); - /** - * execute query list - * set list - */ - const queryDataSourceList = async () => { - const res = await queryList(API_CONSTANTS.DATASOURCE); - setDataSource(res.data); - }; + /** * extra callback @@ -266,7 +269,7 @@ const DataSourceTable: React.FC = (props) => { <> {...PROTABLE_OPTIONS_PUBLIC} - {...(PRO_LIST_CARD_OPTIONS as any)} + {... PRO_LIST_CARD_OPTIONS} loading={loading} tooltip={l('rc.ds.enter')} actionRef={actionRef} diff --git a/dinky-web/src/pages/RegCenter/DataSource/service.tsx b/dinky-web/src/pages/RegCenter/DataSource/service.tsx index d9916fc4c97..b05fe465431 100644 --- a/dinky-web/src/pages/RegCenter/DataSource/service.tsx +++ b/dinky-web/src/pages/RegCenter/DataSource/service.tsx @@ -35,5 +35,5 @@ export const handleTest = async (item: Partial) => { * @param item */ export const saveOrUpdateHandle = async (item: Partial) => { - await handleAddOrUpdate(API_CONSTANTS.DATASOURCE, item); + await handleAddOrUpdate(API_CONSTANTS.DATASOURCE_ADD_UPDATE, item); }; diff --git a/dinky-web/src/pages/RegCenter/Document/components/DocumentProTable/index.tsx b/dinky-web/src/pages/RegCenter/Document/components/DocumentProTable/index.tsx index f3a83951757..80c2f263373 100644 --- a/dinky-web/src/pages/RegCenter/Document/components/DocumentProTable/index.tsx +++ b/dinky-web/src/pages/RegCenter/Document/components/DocumentProTable/index.tsx @@ -150,7 +150,7 @@ const DocumentTableList: React.FC = () => { hideInTable: true, hideInSearch: true, render: (_, record) => { - return ; + return ; } }, { diff --git a/dinky-web/src/services/endpoints.tsx b/dinky-web/src/services/endpoints.tsx index ee89ac0a6c2..3e3ed921297 100644 --- a/dinky-web/src/services/endpoints.tsx +++ b/dinky-web/src/services/endpoints.tsx @@ -98,6 +98,7 @@ export enum API_CONSTANTS { // --- global variable --- // global variable list GLOBAL_VARIABLE = '/api/fragment', + GLOBAL_VARIABLE_ENABLE_LIST = '/api/fragment/listEnable', // delete global variable by id GLOBAL_VARIABLE_DELETE = '/api/fragment/delete', // global variable enable or disable @@ -121,7 +122,9 @@ export enum API_CONSTANTS { CLUSTER_CONFIGURATION_TEST = '/api/clusterConfiguration/testConnect', // datasource registries list - DATASOURCE = '/api/database', + DATASOURCE = '/api/database/listAll', + // datasource registries add or update + DATASOURCE_ADD_UPDATE = '/api/database/saveOrUpdateDataBase', // datasource registries delete DATASOURCE_DELETE = '/api/database/delete', // datasource registries enable or disable diff --git a/dinky-web/src/types/Public/data.ts b/dinky-web/src/types/Public/data.ts index 01d5df475cc..c0dcfac17e2 100644 --- a/dinky-web/src/types/Public/data.ts +++ b/dinky-web/src/types/Public/data.ts @@ -54,3 +54,13 @@ export type ExcludeNameAndEnableColumns = { createTime: Date; updateTime: Date; }; + + +/** + * 级联选择器的数据类型 + */ +export interface CascaderOption { + value: string; + label: string; + children: CascaderOption[]; +}