diff --git a/designer-base/src/main/java/com/fr/design/data/MapCompareUtils.java b/designer-base/src/main/java/com/fr/design/data/MapCompareUtils.java index f7492aefc..57fba6178 100644 --- a/designer-base/src/main/java/com/fr/design/data/MapCompareUtils.java +++ b/designer-base/src/main/java/com/fr/design/data/MapCompareUtils.java @@ -16,6 +16,8 @@ public final class MapCompareUtils { /** * 对比两个map 查找出相比orig,other中有哪些是新增的、删除的或者被修改的,并分别进行处理 * + * 对比时默认用equals方法来判断是否为 EntryEventKind#UPDATED + * * @param orig 原始map * @param other 参考的新map * @param eventHandler 有区别时的事件处理器 @@ -24,12 +26,29 @@ public final class MapCompareUtils { */ public static void contrastMapEntries(@NotNull Map orig, @NotNull Map other, @NotNull EventHandler eventHandler) { + contrastMapEntries(orig, other, eventHandler, UpdateRule.DEFAULT); + } + + /** + * 对比两个map 查找出相比orig,other中有哪些是新增的、删除的或者被修改的,并分别进行处理 + * + * 对比时用自定义的规则来判断是否为 EntryEventKind#UPDATED + * + * @param orig 原始map + * @param other 参考的新map + * @param eventHandler 有区别时的事件处理器 + * @param updateRule 自定义的Update事件判定规则 + * @param + * @param + */ + public static void contrastMapEntries(@NotNull Map orig, @NotNull Map other, @NotNull EventHandler eventHandler, @NotNull UpdateRule updateRule) { + Map copiedOrig = new LinkedHashMap<>(orig); other.forEach((k, v) -> { V existedV = copiedOrig.remove(k); if (existedV != null) { - if (!v.equals(existedV)) { + if (updateRule.needUpdate(existedV, v)) { eventHandler.on(EntryEventKind.UPDATED, k, v); } } else { @@ -41,10 +60,31 @@ public final class MapCompareUtils { } + /** + * 事件处理器,对应比较后的三种结果的事件处理 + * @param + * @param + */ public interface EventHandler { void on(EntryEventKind entryEventKind, K k, V v); } + /** + * 判定 数据被修改 的判定规则 + * @param + * @param + */ + public interface UpdateRule { + + EntryEventKind eventKind = EntryEventKind.UPDATED; + + UpdateRule DEFAULT = new UpdateRule() {}; + + default boolean needUpdate(V origin, V v) { + return !v.equals(origin); + } + } + public enum EntryEventKind { ADDED, REMOVED, diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java index 31630b85c..09c82ade6 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java @@ -2,6 +2,7 @@ package com.fr.design.data.datapane; import com.fr.base.TableData; import com.fr.data.TableDataSource; +import com.fr.data.impl.DBTableData; import com.fr.data.impl.TableDataSourceDependent; import com.fr.design.DesignModelAdapter; import com.fr.design.ExtraDesignClassManager; @@ -10,8 +11,10 @@ import com.fr.design.constants.UIConstants; import com.fr.design.data.BasicTableDataTreePane; import com.fr.design.data.BasicTableDataUtils; import com.fr.design.data.DesignTableDataManager; +import com.fr.design.data.datapane.auth.TableDataAuthHelper; import com.fr.design.data.tabledata.StoreProcedureWorkerListener; import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane; +import com.fr.design.data.tabledata.tabledatapane.loading.TableDataLoadingPane; import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicPane; @@ -31,6 +34,7 @@ import com.fr.design.menu.ToolBarDef; import com.fr.general.ComparatorUtils; import com.fr.general.GeneralContext; import com.fr.general.NameObject; +import com.fr.log.FineLoggerFactory; import com.fr.plugin.context.PluginContext; import com.fr.plugin.injectable.PluginModule; import com.fr.plugin.manage.PluginFilter; @@ -42,6 +46,7 @@ import javax.swing.BorderFactory; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import javax.swing.ToolTipManager; import java.awt.BorderLayout; import java.awt.GridLayout; @@ -49,6 +54,7 @@ import java.awt.dnd.DnDConstants; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -239,7 +245,11 @@ public class TableDataTreePane extends BasicTableDataTreePane { doPropertyChange(dg, nPanel, oldName); } }); - dg.setVisible(true); + // 有些数据集(DBTableData)面板的初始化过程中是包含了SwingWorker处理(查询数据连接、查表等)的 + // 如果这里直接setVisible,可能阻塞SwingWorker的done方法,导致面板渲染出现问题 + SwingUtilities.invokeLater(() -> { + dg.setVisible(true); + }); } private class EditAction extends UpdateAction { @@ -249,13 +259,60 @@ public class TableDataTreePane extends BasicTableDataTreePane { this.setSmallIcon("/com/fr/design/images/control/edit"); } + @Override public void actionPerformed(ActionEvent e) { final NameObject selectedNO = dataTree.getSelectedNameObject(); if (selectedNO == null) { return; } - DesignTableDataManager.removeSelectedColumnNames(selectedNO.getName()); - dgEdit(((AbstractTableDataWrapper) selectedNO.getObject()).creatTableDataPane(), selectedNO.getName(), false); + + String dsName = selectedNO.getName(); + DesignTableDataManager.removeSelectedColumnNames(dsName); + + AbstractTableDataWrapper wrapper = (AbstractTableDataWrapper) selectedNO.getObject(); + + AbstractTableDataPane tableDataPane = wrapper.creatTableDataPane(); + + if (TableDataAuthHelper.needCheckAuthWhenEdit(wrapper.getTableData())) { + // 先打开一个Loading面板 + TableDataLoadingPane loadingPane = new TableDataLoadingPane(); + BasicDialog loadingDialog = loadingPane.showLargeWindow(SwingUtilities.getWindowAncestor(TableDataTreePane.this), null); + // 查询权限 + new SwingWorker() { + @Override + protected Boolean doInBackground() throws Exception { + // 获取无权限连接名称集合 + Collection noAuthConnections = TableDataAuthHelper.getNoAuthConnections(); + // 获取当前数据集对应的数据连接名称 + String connectionName = TableDataAuthHelper.getConnectionNameByDBTableData((DBTableData) wrapper.getTableData()); + return !noAuthConnections.contains(connectionName); + } + + @Override + protected void done() { + try { + Boolean hasAuth = get(); + if (hasAuth) { + // 有权限时,关闭Loading面板,打开编辑面板 + loadingDialog.setVisible(false); + dgEdit(tableDataPane, dsName, false); + } else { + // 无权限时,给出无权限提示 + loadingPane.switchTo(TableDataLoadingPane.NO_AUTH_PANE_NAME); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error("loading connection error in remote design", e.getMessage()); + // 查询权限失败时,给出报错提示 + loadingPane.switchTo(TableDataLoadingPane.ERROR_NAME); + } + } + }.execute(); + loadingDialog.setVisible(true); + } else { + // 无需检查权限时,直接打开数据库查询编辑面板 + //下面创建creatTableDataPane后会直接populate,所以populate时不能用后设置的一些参数,比如name + dgEdit(tableDataPane, dsName, false); + } } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/auth/TableDataAuthHelper.java b/designer-base/src/main/java/com/fr/design/data/datapane/auth/TableDataAuthHelper.java new file mode 100644 index 000000000..382e04dd9 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/auth/TableDataAuthHelper.java @@ -0,0 +1,57 @@ +package com.fr.design.data.datapane.auth; + +import com.fr.base.TableData; +import com.fr.data.impl.Connection; +import com.fr.data.impl.DBTableData; +import com.fr.data.impl.NameDatabaseConnection; +import com.fr.stable.StringUtils; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.connection.DBConnectAuth; + +import java.util.Collection; +import java.util.Collections; + +/** + * 数据连接权限相关的工具类 + * @author Yvan + */ +public class TableDataAuthHelper { + + /** + * 编辑数据集时是否需要检查权限 + * @param tableData + * @return + */ + public static boolean needCheckAuthWhenEdit(TableData tableData) { + // 远程设计下,编辑DBTableData时需要判断权限 + return !WorkContext.getCurrent().isLocal() && tableData instanceof DBTableData; + } + + /** + * 获取无权限数据连接集合 + * 远程下需要调用RPC,为耗时操作,谨慎使用 + * @return + */ + public static Collection getNoAuthConnections() { + // 获取无权限连接集合 + Collection noAuthConnections = WorkContext.getCurrent().get(DBConnectAuth.class).getNoAuthConnections(); + return noAuthConnections == null ? Collections.emptyList() : noAuthConnections; + } + + /** + * 通过数据集获取其数据连接的名称 + * + * 注意: + * 1. Connection接口本身是不提供名称的,只有我们内部为了使用方便,将其包装成了NameDataBaseConnection + * 如果不是NameDataBaseConnection类型,则无名称,因此这里只能用判断类型的方式获取名称 + * 2. 仅支持DBTableData获取连接名 + * @return + */ + public static String getConnectionNameByDBTableData(DBTableData tableData) { + Connection database = tableData.getDatabase(); + if (database instanceof NameDatabaseConnection) { + return ((NameDatabaseConnection) database).getName(); + } + return StringUtils.EMPTY; + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java index 81147d42d..9fa03aca1 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java @@ -200,18 +200,34 @@ public class ConnectionComboBoxPanel extends ItemEditableComboBoxPanel { if (connection instanceof NameDatabaseConnection) { this.setSelectedItem(((NameDatabaseConnection) connection).getName()); } else { - String s = DesignerEnvManager.getEnvManager().getRecentSelectedConnection(); - if (StringUtils.isNotBlank(s)) { - // 之前的写法有多线程问题,nameList异步尚未初始化完成的时候,这里可能无法匹配设置数据连接名称,导致DBTableDataPane打开后连接面板空白 - // 这里的需求无非是设置上一次使用的数据连接,做个简单检查这个连接是否存在即可,存在就设置 - if (nameList.contains(s)) { - this.setSelectedItem(s); - } - } - // alex:如果这个ComboBox还是没有选中,那么选中第一个 - if (StringUtils.isBlank(this.getSelectedItem()) && this.getConnectionSize() > 0) { - this.setSelectedItem(this.getConnection(0)); + setRecentConnection(); + } + } + + /** + * 下拉框选项设置成最近选择的connection,如果最近选择不存在,则选择列表中的第一个 + */ + protected void setRecentConnection() { + String s = DesignerEnvManager.getEnvManager().getRecentSelectedConnection(); + if (StringUtils.isNotBlank(s)) { + // 之前的写法有多线程问题,nameList异步尚未初始化完成的时候,这里可能无法匹配设置数据连接名称,导致DBTableDataPane打开后连接面板空白 + // 这里的需求无非是设置上一次使用的数据连接,做个简单检查这个连接是否存在即可,存在就设置 + if (nameList.contains(s)) { + this.setSelectedItem(s); } } + // alex:如果这个ComboBox还是没有选中,那么选中第一个 + if (StringUtils.isBlank(this.getSelectedItem()) && this.getConnectionSize() > 0) { + this.setSelectedItem(this.getConnection(0)); + } + } + + /** + * 是否无选中状态(空白item也视为无选中) + * @return + */ + protected boolean isSelectedItemEmpty() { + String selectedItem = this.getSelectedItem(); + return selectedItem == null || StringUtils.equals(selectedItem, EMPTY.toString()); } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java index 3e03e6a5d..63a4a47cb 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java @@ -1,10 +1,9 @@ package com.fr.design.data.datapane.connect; -import com.fr.base.TemplateUtils; -import com.fr.data.core.db.JDBCSecurityChecker; import com.fr.data.impl.Connection; import com.fr.data.impl.JDBCDatabaseConnection; import com.fr.data.impl.JNDIDatabaseConnection; +import com.fr.data.operator.DataOperator; import com.fr.design.ExtraDesignClassManager; import com.fr.design.data.MapCompareUtils; import com.fr.design.dialog.FineJOptionPane; @@ -179,9 +178,35 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh default: break; } + }, new MapCompareUtils.UpdateRule() { + @Override + public boolean needUpdate(Connection origin, Connection connection) { + return needUpdate0(origin, connection); + } + + /** + * 是否需要更新处理 + * 1. Connection本身equals为false,代表字段修改 + * 2. 非内置的Connection,即插件提供的Connection + * todo 原本一个equals方法就可以搞定,但是插件里面没有实现equals,结果导致不能正确判断,只能主代码里做兼容,很恶心,先记个todo,以后看有没有办法改掉 + * @param origin + * @param connection + * @return + */ + private boolean needUpdate0(Connection origin, Connection connection) { + return !connection.equals(origin) || !isEmbedConnection(connection); + } + + /** + * 是否是主工程里内置的Connection + * @return + */ + private boolean isEmbedConnection(Connection connection) { + return connection instanceof JDBCDatabaseConnection || connection instanceof JNDIDatabaseConnection; + } }); - this.checkSecurity(addedOrUpdatedConnections); + this.validateConnections(addedOrUpdatedConnections); alterConnections(removedConnNames, addedOrUpdatedConnections); } @@ -190,17 +215,14 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh addedOrUpdatedConnections.forEach((name, conn) -> ConnectionConfig.getInstance().addConnection(name, conn)); } - private void checkSecurity(Map addedOrUpdatedConnections) throws Exception { + private void validateConnections(Map addedOrUpdatedConnections) throws Exception { for (Map.Entry entry : addedOrUpdatedConnections.entrySet()) { Connection connection = entry.getValue(); - if (connection instanceof JDBCDatabaseConnection) { - try { - JDBCSecurityChecker.checkURL(TemplateUtils.render(((JDBCDatabaseConnection) connection).getURL())); - JDBCSecurityChecker.checkValidationQuery(((JDBCDatabaseConnection) connection).getDbcpAttr().getValidationQuery()); - } catch (SQLException e) { - throw new SQLException(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Invalid_Config", entry.getKey()) + ", " + e.getMessage(), e.getCause()); - } + try { + DataOperator.getInstance().validateConnectionSettings(connection); + } catch (SQLException e) { + throw new SQLException(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Invalid_Config", entry.getKey()) + ", " + e.getMessage(), e.getCause()); } } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionTableProcedurePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionTableProcedurePane.java index a979a20cd..613eed9a4 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionTableProcedurePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionTableProcedurePane.java @@ -1,26 +1,32 @@ package com.fr.design.data.datapane.connect; import com.fr.base.BaseUtils; +import com.fr.base.svg.IconUtils; import com.fr.data.core.db.TableProcedure; import com.fr.data.impl.AbstractDatabaseConnection; import com.fr.data.impl.Connection; import com.fr.design.border.UIRoundedBorder; import com.fr.design.constants.UIConstants; +import com.fr.design.data.tabledata.tabledatapane.DBTableDataPane; +import com.fr.design.data.tabledata.tabledatapane.loading.SwitchableTableDataPane; import com.fr.design.dialog.BasicPane; import com.fr.design.gui.icheckbox.UICheckBox; import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.ilist.TableViewList; import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.general.GeneralContext; import com.fr.stable.ArrayUtils; +import javax.swing.BorderFactory; import javax.swing.JPanel; import javax.swing.ToolTipManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -45,7 +51,39 @@ public class ConnectionTableProcedurePane extends BasicPane { private java.util.List listeners = new java.util.ArrayList(); public ConnectionTableProcedurePane() { + init(null); + } + + /** + * 传入父容器 + * @param parent + */ + public ConnectionTableProcedurePane(SwitchableTableDataPane parent) { + init(parent); + } + + private void init(SwitchableTableDataPane parent) { this.setLayout(new BorderLayout(4, 4)); + // 初始化数据连接下拉框 + initConnectionComboBox(parent); + // 初始化中间的面板 + JPanel centerPane = initCenterPane(); + this.add(connectionComboBox, BorderLayout.NORTH); + this.add(centerPane, BorderLayout.CENTER); + this.setPreferredSize(new Dimension(WIDTH, getPreferredSize().height)); + addKeyMonitor(); + } + + private JPanel initCenterPane() { + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + // 搜索面板 + centerPane.add(createSearchPane(), BorderLayout.NORTH); + // 数据库表视图面板 + centerPane.add(createTableViewBorderPane(), BorderLayout.CENTER); + return centerPane; + } + + private void initConnectionComboBox(SwitchableTableDataPane parent) { connectionComboBox = new ConnectionComboBoxPanel(com.fr.data.impl.Connection.class) { @Override @@ -60,10 +98,34 @@ public class ConnectionTableProcedurePane extends BasicPane { search(true); } } + + @Override + protected void afterRefreshItems() { + // 刷新完成后,如果未选中(在nameList初始化完成之前可能会出现),则尝试再次设置 + if (isSelectedItemEmpty()) { + setRecentConnection(); + } + // 获取数据连接之后,让父容器切换面板 + if (parent != null) { + parent.switchTo(SwitchableTableDataPane.CONTENT_PANE_NAME); + } + } + + @Override + protected void refreshItemsError() { + // 获取数据连接出现错误时,也让父容器从Loading面板切换至内容面板 + if (parent != null) { + parent.switchTo(SwitchableTableDataPane.CONTENT_PANE_NAME); + } + } }; + connectionComboBox.addComboBoxActionListener(filter); + } + + private JPanel createTableViewBorderPane() { tableViewList = new TableViewList(); ToolTipManager.sharedInstance().registerComponent(tableViewList); - connectionComboBox.addComboBoxActionListener(filter); + tableViewList.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent evt) { if (evt.getClickCount() >= 2) { @@ -80,23 +142,50 @@ public class ConnectionTableProcedurePane extends BasicPane { } } }); - JPanel filterPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + UIScrollPane tableViewListPane = new UIScrollPane(tableViewList); + tableViewListPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC)); + JPanel tableViewBorderPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + tableViewBorderPane.add(tableViewListPane, BorderLayout.CENTER); JPanel checkBoxgroupPane = createCheckBoxgroupPane(); if (checkBoxgroupPane != null) { - filterPane.add(createCheckBoxgroupPane(), BorderLayout.NORTH); + tableViewBorderPane.add(createCheckBoxgroupPane(), BorderLayout.SOUTH); } + return tableViewBorderPane; + } + + /** + * 创建搜索Panel,用于搜索表或视图 + * @return + */ + private JPanel createSearchPane() { + JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane(); JPanel searchPane = new JPanel(new BorderLayout(10, 0)); + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + searchPane.setBackground(Color.WHITE); searchField = new UITextField(); - searchPane.add(searchField, BorderLayout.CENTER); + searchField.setBorderPainted(false); + searchField.setPlaceholder(Toolkit.i18nText("Fine-Design_Basic_Table_Search")); searchField.getDocument().addDocumentListener(searchListener); - filterPane.add(searchPane, BorderLayout.CENTER); - UIScrollPane tableViewListPane = new UIScrollPane(tableViewList); - tableViewListPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC)); - this.add(connectionComboBox, BorderLayout.NORTH); - this.add(tableViewListPane, BorderLayout.CENTER); - this.add(filterPane, BorderLayout.SOUTH); - this.setPreferredSize(new Dimension(WIDTH, getPreferredSize().height)); - addKeyMonitor(); + searchField.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + super.mouseEntered(e); + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.CHECKBOX_HOVER_SELECTED)); + } + + @Override + public void mouseExited(MouseEvent e) { + super.mouseExited(e); + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + } + }); + // 搜索图标 + UILabel searchLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/search")); + searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); + searchPane.add(searchField, BorderLayout.CENTER); + searchPane.add(searchLabel, BorderLayout.EAST); + panel.add(searchPane, BorderLayout.CENTER); + return panel; } protected void filter(Connection connection, String conName, List nameList) { @@ -224,4 +313,4 @@ public class ConnectionTableProcedurePane extends BasicPane { */ public void actionPerformed(TableProcedure target); } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java index d0738e57b..a430a13bd 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java @@ -127,10 +127,12 @@ public abstract class ItemEditableComboBoxPanel extends JPanel { itemComboBox.setMaximumRowCount(itemComboBox.getMaximumRowCount() + 1); itemComboBox.setMaximumRowCount(itemComboBox.getMaximumRowCount() - 1); } + afterRefreshItems(); } catch (Exception e) { if (!(e instanceof CancellationException)) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } + refreshItemsError(); } } @@ -160,6 +162,20 @@ public abstract class ItemEditableComboBoxPanel extends JPanel { */ protected abstract java.util.Iterator items(); + /** + * 刷新ComboBox.items之后 + */ + protected void afterRefreshItems() { + // 空实现,供子类重写 + } + + /** + * 刷新ComboBox.items时出现异常 + */ + protected void refreshItemsError() { + // 空实现,供子类重写 + } + /* * 弹出对话框编辑Items */ diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java index fecf9c079..f1b804377 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java @@ -18,6 +18,8 @@ import com.fr.design.data.datapane.connect.ConnectionTableProcedurePane.DoubleCl import com.fr.design.data.datapane.preview.PreviewTablePane; import com.fr.design.data.datapane.preview.sql.PreviewPerformedSqlPane; import com.fr.design.data.datapane.sqlpane.SQLEditPane; +import com.fr.design.data.tabledata.tabledatapane.loading.SwitchableTableDataPane; +import com.fr.design.data.tabledata.tabledatapane.loading.TipsPane; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.DialogActionAdapter; @@ -30,6 +32,7 @@ import com.fr.design.gui.itableeditorpane.UITableEditorPane; import com.fr.design.gui.itoolbar.UIToolbar; import com.fr.design.gui.syntax.ui.rsyntaxtextarea.SyntaxConstants; import com.fr.design.gui.syntax.ui.rtextarea.RTextScrollPane; +import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; import com.fr.design.menu.SeparatorDef; import com.fr.design.menu.ToolBarDef; @@ -54,6 +57,7 @@ import javax.swing.JToolBar; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import java.awt.BorderLayout; +import java.awt.CardLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; @@ -64,7 +68,7 @@ import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; -public class DBTableDataPane extends AbstractTableDataPane { +public class DBTableDataPane extends AbstractTableDataPane implements SwitchableTableDataPane { private static final int BOTTOM = 6; private static final String PREVIEW_BUTTON = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Preview"); private static final String REFRESH_BUTTON = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Refresh"); @@ -78,8 +82,42 @@ public class DBTableDataPane extends AbstractTableDataPane { private String pageQuery = null; private DBTableData dbTableData; + private CardLayout card; + /** 数据库查询面板真正的内容面板 */ + private JPanel contentPane; + /** 加载中面板 */ + private JPanel loadingPane; + + public DBTableDataPane() { + initCards(); + initContentPane(); + } + + /** + * 初始化cardLayout以及LoadingPane等,并且布局切到LoadingPane + */ + protected void initCards() { + card = new CardLayout(); + setLayout(card); + + loadingPane = new TipsPane(true); + contentPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + + add(LOADING_PANE_NAME, loadingPane); + add(CONTENT_PANE_NAME, contentPane); + switchTo(LOADING_PANE_NAME); + } + + /** + * 初始化内容面板 + */ + protected void initContentPane() { + init(); + initMainSplitPane(); + } + private void init() { - this.setLayout(new BorderLayout(4, 4)); + contentPane.setLayout(new BorderLayout(4, 4)); sqlTextPane = new SQLEditPane(); sqlTextPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_SQL); @@ -93,7 +131,7 @@ public class DBTableDataPane extends AbstractTableDataPane { editorPane = new UITableEditorPane(model); // 左边的Panel,上面是选择DatabaseConnection的ComboBox,下面DatabaseConnection对应的Table - connectionTableProcedurePane = new ConnectionTableProcedurePane() { + connectionTableProcedurePane = new ConnectionTableProcedurePane(this) { @Override protected void filter(Connection connection, String conName, List nameList) { @@ -194,15 +232,23 @@ public class DBTableDataPane extends AbstractTableDataPane { JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, connectionTableProcedurePane, sqlSplitPane); mainSplitPane.setBorder(BorderFactory.createLineBorder(GUICoreUtils.getTitleLineBorderColor())); mainSplitPane.setOneTouchExpandable(true); - this.add(mainSplitPane, BorderLayout.CENTER); + contentPane.add(mainSplitPane, BorderLayout.CENTER); } - public DBTableDataPane() { - init(); - initMainSplitPane(); + @Override + public void switchTo(String panelName) { + try { + if (panelName != null) { + card.show(this, panelName); + } + } catch (IllegalArgumentException ingore) { + // 有些直接继承此面板或者替换掉此面板的插件,在未适配此功能时会出现报错,因为不是CardLayout,无法切换,这里处理下 + FineLoggerFactory.getLogger().info("cannot switch pane by {}", this.getClass().getName()); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } } - private boolean isPreviewOrRefreshButton(FocusEvent e) { if (e.getOppositeComponent() != null) { String name = e.getOppositeComponent().getName(); diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/SwitchableTableDataPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/SwitchableTableDataPane.java new file mode 100644 index 000000000..221ee1cbb --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/SwitchableTableDataPane.java @@ -0,0 +1,23 @@ +package com.fr.design.data.tabledata.tabledatapane.loading; + + + +/** + * 可切换的DBTableData对应的数据集面板,需要使用CardLayout布局 + * 主要是给插件适配用的 + * @author Yvan + */ +public interface SwitchableTableDataPane { + + /** Loading面板 */ + String LOADING_PANE_NAME = "Loading"; + /** 内容面板 */ + String CONTENT_PANE_NAME = "Content"; + + /** + * 根据面板名称切换面板 + * @param paneName 面板名称 + */ + void switchTo(String paneName); + +} diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/TableDataLoadingPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/TableDataLoadingPane.java new file mode 100644 index 000000000..c3f12c3e7 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/TableDataLoadingPane.java @@ -0,0 +1,54 @@ +package com.fr.design.data.tabledata.tabledatapane.loading; + +import com.fr.design.dialog.BasicPane; +import com.fr.design.i18n.Toolkit; + +import javax.swing.JPanel; +import java.awt.CardLayout; + +/** + * @author Yvan + */ +public class TableDataLoadingPane extends BasicPane { + + /** Loading面板 */ + public static final String LOADING_PANE_NAME = "Loading"; + /** 无权限提示面板 */ + public static final String NO_AUTH_PANE_NAME = "NoAuthority"; + /** 错误提示面板 */ + public static final String ERROR_NAME = "Error"; + + private CardLayout card; + + /** 加载中面板 */ + private JPanel loadingPane; + /** 错误提示面板 */ + private JPanel errorPane; + /** 数据连接无权限面板 */ + private JPanel noAuthorityPane; + + public TableDataLoadingPane() { + initPanes(); + } + + private void initPanes() { + card = new CardLayout(); + this.setLayout(card); + loadingPane = new TipsPane(true); + errorPane = new TipsPane(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Error")); + noAuthorityPane = new TipsPane(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_No_Auth")); + add(LOADING_PANE_NAME, loadingPane); + add(NO_AUTH_PANE_NAME, noAuthorityPane); + add(ERROR_NAME, errorPane); + switchTo(LOADING_PANE_NAME); + } + + public void switchTo(String panelName) { + card.show(this, panelName); + } + + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Basic_DS-Database_Query"); + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/TipsPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/TipsPane.java new file mode 100644 index 000000000..40fcb6ef2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/loading/TipsPane.java @@ -0,0 +1,45 @@ +package com.fr.design.data.tabledata.tabledatapane.loading; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; + +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.SwingConstants; +import java.awt.BorderLayout; + +/** + * 提示面板,支持自定义提示,支持进度条配置可选 + * @author Yvan + */ +public class TipsPane extends JPanel { + + /** + * 默认提示 + */ + private static final String LOADING = Toolkit.i18nText("Fine-Design_Basic_Loading_And_Waiting"); + + public TipsPane () { + this(LOADING, false); + } + + public TipsPane (String tip) { + this(tip, false); + } + + public TipsPane (boolean needProgressBar) { + this(LOADING, needProgressBar); + } + + public TipsPane (String tips, boolean needProgressBar) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + UILabel tipsLabel = new UILabel(tips, SwingConstants.CENTER); + this.add(tipsLabel, BorderLayout.CENTER); + if (needProgressBar) { + JProgressBar progressBar = new JProgressBar(); + progressBar.setIndeterminate(true); + this.add(progressBar, BorderLayout.SOUTH); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallFromDiskCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallFromDiskCallback.java index 92fdd4878..ea591f3bf 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallFromDiskCallback.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallFromDiskCallback.java @@ -3,6 +3,7 @@ package com.fr.design.extra.exe.callback; import com.fr.design.bridge.exec.JSCallback; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginOperateUtils; +import com.fr.design.extra.exe.callback.handle.PluginCallBackHelper; import com.fr.design.i18n.Toolkit; import com.fr.log.FineLoggerFactory; @@ -73,7 +74,7 @@ public class InstallFromDiskCallback extends AbstractPluginTaskCallback { }else { jsCallback.execute("failed"); FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Plugin_Install_Failed")); - FineJOptionPane.showMessageDialog(null, pluginInfo, Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), FineJOptionPane.ERROR_MESSAGE); + PluginCallBackHelper.showErrorMessage(result, pluginInfo); } } } diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallOnlineCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallOnlineCallback.java index 0b1eb33fb..09c360b4f 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallOnlineCallback.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/InstallOnlineCallback.java @@ -4,6 +4,7 @@ import com.fr.design.bridge.exec.JSCallback; import com.fr.design.bridge.exec.JSExecutor; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginOperateUtils; +import com.fr.design.extra.exe.callback.handle.PluginCallBackHelper; import com.fr.design.i18n.Toolkit; import com.fr.log.FineLoggerFactory; @@ -63,7 +64,7 @@ public class InstallOnlineCallback extends AbstractDealPreTaskCallback { } else { jsCallback.execute("failed"); FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Plugin_Install_Failed")); - FineJOptionPane.showMessageDialog(null, pluginInfo, Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), FineJOptionPane.ERROR_MESSAGE); + PluginCallBackHelper.showErrorMessage(result, pluginInfo); } } diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateFromDiskCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateFromDiskCallback.java index 21c7871f5..31bf482eb 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateFromDiskCallback.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateFromDiskCallback.java @@ -3,6 +3,7 @@ package com.fr.design.extra.exe.callback; import com.fr.design.bridge.exec.JSCallback; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginOperateUtils; +import com.fr.design.extra.exe.callback.handle.PluginCallBackHelper; import com.fr.design.i18n.Toolkit; import com.fr.log.FineLoggerFactory; @@ -72,7 +73,7 @@ public class UpdateFromDiskCallback extends AbstractPluginTaskCallback { }else { jsCallback.execute("failed"); FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Plugin_Update_Failed")); - FineJOptionPane.showMessageDialog(null, pluginInfo, Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), FineJOptionPane.ERROR_MESSAGE); + PluginCallBackHelper.showErrorMessage(result, pluginInfo); } } } diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateOnlineCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateOnlineCallback.java index 8dbad6a54..3469ed1a5 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateOnlineCallback.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/UpdateOnlineCallback.java @@ -3,6 +3,7 @@ package com.fr.design.extra.exe.callback; import com.fr.design.bridge.exec.JSCallback; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginOperateUtils; +import com.fr.design.extra.exe.callback.handle.PluginCallBackHelper; import com.fr.design.i18n.Toolkit; import com.fr.log.FineLoggerFactory; @@ -38,7 +39,7 @@ public class UpdateOnlineCallback extends AbstractDealPreTaskCallback { } else { jsCallback.execute("failed"); FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Plugin_Update_Failed")); - FineJOptionPane.showMessageDialog(null, pluginInfo, Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), FineJOptionPane.ERROR_MESSAGE); + PluginCallBackHelper.showErrorMessage(result, pluginInfo); } } } diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/handle/PluginCallBackHelper.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/handle/PluginCallBackHelper.java new file mode 100644 index 000000000..fc5f957f6 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/handle/PluginCallBackHelper.java @@ -0,0 +1,70 @@ +package com.fr.design.extra.exe.callback.handle; + +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.i18n.Toolkit; +import com.fr.plugin.error.PluginErrorCode; +import com.fr.plugin.manage.control.PluginTaskResult; + +/** + * 帮助处理插件操作(安装、更新等)的弹窗 + * @author Yvan + */ +public class PluginCallBackHelper { + + private static final String NEW_LINE_TAG = "
"; + private static final String REFERENCE = Toolkit.i18nText("Fine-Design_Basic_Plugin_File_Validate_Reference"); + private static final String HELP_DOCUMENT_NAME = Toolkit.i18nText("Fine-Design_Basic_Plugin_File_Validate_HELP_DOCUMENT_NAME"); + private static final String CONNECTOR = "-"; + private static final String HELP_DOCUMENT_LINK = Toolkit.i18nText("Fine-Design_Basic_Plugin_File_Validate_HELP_DOCUMENT_LINK"); + + + /** + * 展示插件操作失败后的提示弹窗 + * @param result + * @param pluginInfo + */ + public static void showErrorMessage(PluginTaskResult result, String pluginInfo) { + // 单独处理下插件完整性校验失败的提示 + if (PluginCallBackHelper.needHandleInvalidatePackage(result)) { + MessageWithLink messageWithLink = PluginCallBackHelper.generate4InvalidatePackage(pluginInfo); + PluginTaskResultErrorDialog resultDialog = new PluginTaskResultErrorDialog(null, messageWithLink); + resultDialog.showResult(); + } else { + FineJOptionPane.showMessageDialog(null, pluginInfo, Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), FineJOptionPane.ERROR_MESSAGE); + } + } + + /** + * 判断是否需要处理 插件安装包校验失败 导致的安装失败任务 + * @param result + * @return + */ + private static boolean needHandleInvalidatePackage(PluginTaskResult result) { + return !result.isSuccess() && result.getCode() == PluginErrorCode.InstallPackageValidateFailed; + } + + /** + * 根据插件原始报错信息,构建MessageWithLink + * @param originInfo + * @return + */ + private static MessageWithLink generate4InvalidatePackage(String originInfo) { + + return new MessageWithLink(getSupplementaryMessage(originInfo), HELP_DOCUMENT_NAME, HELP_DOCUMENT_LINK); + } + + /** + * 根据插件原始报错信息,获取增加了补充说明后的信息 + * @param originInfo + * @return + */ + private static String getSupplementaryMessage(String originInfo) { + return new StringBuilder() + .append(originInfo) + .append(NEW_LINE_TAG) + .append(REFERENCE) + .append(CONNECTOR) + .toString(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/handle/PluginTaskResultErrorDialog.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/handle/PluginTaskResultErrorDialog.java new file mode 100644 index 000000000..da4f43208 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/handle/PluginTaskResultErrorDialog.java @@ -0,0 +1,86 @@ +package com.fr.design.extra.exe.callback.handle; + +import com.fr.base.svg.IconUtils; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.VerticalFlowLayout; +import com.fr.design.utils.gui.GUICoreUtils; + +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * 当前仅处理Error提示,之后有需要再扩展 + * @author Yvan + */ +public class PluginTaskResultErrorDialog extends JDialog { + + private static final Dimension LABEL = new Dimension(60, 90); + + private JPanel contentPane; + + private UILabel errorLabel; + + private UIButton confirmButton; + + private MessageWithLink messageWithLink; + + public PluginTaskResultErrorDialog(Frame parent, MessageWithLink messageWithLink) { + super(parent, true); + this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning")); + this.messageWithLink = messageWithLink; + + initContentPane(); + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setResizable(false); + this.add(contentPane, BorderLayout.CENTER); + this.setSize(new Dimension( 380, 160)); + GUICoreUtils.centerWindow(this); + } + + /** + * 初始化内容面板 + */ + private void initContentPane() { + this.contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + // error图标 + errorLabel = new UILabel(IconUtils.readIcon("/com/fr/design/standard/system/error_tips.svg")); + errorLabel.setPreferredSize(LABEL); + errorLabel.setBorder(BorderFactory.createEmptyBorder(10, 20, 40, 20)); + // 提示内容 + JPanel messagePane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + messagePane.add(errorLabel, BorderLayout.WEST); + messagePane.add(messageWithLink, BorderLayout.CENTER); + messagePane.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 10)); + this.contentPane.add(messagePane, BorderLayout.CENTER); + // 确定按钮 + confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Button_OK")); + confirmButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + hideResult(); + } + }); + JPanel confirmPane = new JPanel(new VerticalFlowLayout()); + confirmPane.add(confirmButton); + confirmPane.setBorder(BorderFactory.createEmptyBorder(0, 160, 10, 0)); + this.contentPane.add(confirmPane, BorderLayout.SOUTH); + } + + public void showResult() { + this.setVisible(true); + } + + public void hideResult() { + this.setVisible(false); + } +} diff --git a/designer-base/src/main/java/com/fr/design/gui/icontainer/UIEastResizableContainer.java b/designer-base/src/main/java/com/fr/design/gui/icontainer/UIEastResizableContainer.java index 125cd3aa1..dcd33498a 100644 --- a/designer-base/src/main/java/com/fr/design/gui/icontainer/UIEastResizableContainer.java +++ b/designer-base/src/main/java/com/fr/design/gui/icontainer/UIEastResizableContainer.java @@ -296,10 +296,9 @@ public class UIEastResizableContainer extends JPanel { model = UIConstants.MODEL_NORMAL; refreshContainer(); } - @Override - public void mouseClicked(MouseEvent e) { - if (e.getX() <= ARROW_RANGE) { + public void mouseReleased(MouseEvent e) { + if (isInPane(e)) { if (containerWidth == leftPaneWidth) { showContainer(); } else { @@ -309,7 +308,9 @@ public class UIEastResizableContainer extends JPanel { } }); } - + public boolean isInPane(MouseEvent e){ + return e.getX() <= ARROW_RANGE && e.getX() >= 0 && e.getY() <= topToolPaneHeight && e.getY() >= 0; + } @Override public void paint(Graphics g) { Image button; diff --git a/designer-base/src/main/java/com/fr/design/gui/itextfield/UINumberField.java b/designer-base/src/main/java/com/fr/design/gui/itextfield/UINumberField.java index 0d1287d8c..f40237659 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itextfield/UINumberField.java +++ b/designer-base/src/main/java/com/fr/design/gui/itextfield/UINumberField.java @@ -4,6 +4,7 @@ import com.fr.base.Utils; import com.fr.general.ComparatorUtils; import com.fr.stable.CoreConstants; import com.fr.stable.StringUtils; + import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.PlainDocument; @@ -164,10 +165,16 @@ public class UINumberField extends UITextField { || ComparatorUtils.equals(s, "D") || ComparatorUtils.equals(s, "d") || (ComparatorUtils.equals(str.trim(), "0") && !ComparatorUtils.equals(s.substring(0, 1), ".") && offset != 0)// 第一位是0时,第二位只能为小数点 - || (ComparatorUtils.equals(s, ".") && maxDecimalLength == 0)); + || (ComparatorUtils.equals(s, ".") && maxDecimalLength == 0)) + || s.contains(" ")//不允许空格 + || (ComparatorUtils.equals(s.substring(0, 1),"-") && offset != 0)//负号只允许出现在第一位 + || (ComparatorUtils.equals(s.substring(0, 1),".") && offset == 0)//小数点不能在第一位 + || s.contains("-.") + || s.contains(".-")//不能包含-.或.-非法格式 + || (str.startsWith("-") && ComparatorUtils.equals(s.substring(0, 1), ".") && offset == 1 )//负号不能接小数点 + || (s.contains("-") && !ComparatorUtils.equals(s.substring(0,1), "-"));//输入的字符串如果包含负号,负号必须在第一位 } - public void insertString(int offset, String s, AttributeSet a) throws BadLocationException { String str = getText(0, getLength()); diff --git a/designer-base/src/main/java/com/fr/design/widget/component/NumberEditorValidatePane.java b/designer-base/src/main/java/com/fr/design/widget/component/NumberEditorValidatePane.java index 7fd88dd95..d10f58e18 100644 --- a/designer-base/src/main/java/com/fr/design/widget/component/NumberEditorValidatePane.java +++ b/designer-base/src/main/java/com/fr/design/widget/component/NumberEditorValidatePane.java @@ -26,6 +26,9 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + /** * Created by kerry on 2017/9/10. @@ -102,8 +105,20 @@ public class NumberEditorValidatePane extends JPanel { int[][] rowCount = {{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}; JPanel panel = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_L2, IntervalConstants.INTERVAL_L1); this.add(panel); + decimalLength.getTextField().addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + // Digit only + if (isDigit(e)) { + } else { + e.consume(); + } + } + }); + } + private boolean isDigit(KeyEvent e){ + return e.getKeyChar() >= KeyEvent.VK_0 && e.getKeyChar() <= KeyEvent.VK_9; } - private void initErrorMsgPane() { TextFieldAdapterProvider provider = ExtraDesignClassManager.getInstance().getSingle(TextFieldAdapterProvider.XML_TAG); if (provider != null) { diff --git a/designer-base/src/main/resources/com/fr/design/standard/system/error_tips.svg b/designer-base/src/main/resources/com/fr/design/standard/system/error_tips.svg new file mode 100644 index 000000000..900205215 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/system/error_tips.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/designer-base/src/test/java/com/fr/design/gui/itextfield/UINumberFieldTest.java b/designer-base/src/test/java/com/fr/design/gui/itextfield/UINumberFieldTest.java new file mode 100644 index 000000000..17b0f547c --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/gui/itextfield/UINumberFieldTest.java @@ -0,0 +1,44 @@ +package com.fr.design.gui.itextfield; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Destiny.Lin + * @version 10.0 + * created by Destiny.Lin on 2022-07-11 + */ +public class UINumberFieldTest { + + @Test + public void testUINumberFieldTest(){ + UINumberField uiNumberField = new UINumberField(); + uiNumberField.setFieldDocument(); + //异常输入测试 + uiNumberField.setText("-.1"); + Assert.assertEquals("",uiNumberField.getText()); + uiNumberField.setText(".-1"); + Assert.assertEquals("",uiNumberField.getText()); + uiNumberField.setText("1-"); + Assert.assertEquals("",uiNumberField.getText()); + uiNumberField.setText("1-1"); + Assert.assertEquals("",uiNumberField.getText()); + uiNumberField.setText("1 "); + Assert.assertEquals("",uiNumberField.getText()); + uiNumberField.setText(".1"); + Assert.assertEquals("",uiNumberField.getText()); + uiNumberField.setText("1 -"); + Assert.assertEquals("",uiNumberField.getText()); + + //正常输入测试 + uiNumberField.setText("0.1"); + Assert.assertEquals("0.1",uiNumberField.getText()); + uiNumberField.setText("1"); + Assert.assertEquals("1",uiNumberField.getText()); + uiNumberField.setText("-1.5"); + Assert.assertEquals("-1.5",uiNumberField.getText()); + uiNumberField.setText("-123.123"); + Assert.assertEquals("-123.123",uiNumberField.getText()); + + } +}